From 1ba0b5b1182b1212237e099b05ed2eb76a1658d9 Mon Sep 17 00:00:00 2001 From: Christopher Bohn <bohn@unl.edu> Date: Sat, 16 Dec 2023 09:24:33 -0600 Subject: [PATCH] Completed 2023 Day 09, plus part 1 of 07 & part 1 (maybe part 2) of 08 --- 2023/python/Day07.py | 167 +++++++++++++++++++++++++++++++++++++++++++ 2023/python/Day08.py | 91 +++++++++++++++++++++++ 2023/python/Day09.py | 51 +++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 2023/python/Day07.py create mode 100644 2023/python/Day08.py create mode 100644 2023/python/Day09.py diff --git a/2023/python/Day07.py b/2023/python/Day07.py new file mode 100644 index 0000000..649cf54 --- /dev/null +++ b/2023/python/Day07.py @@ -0,0 +1,167 @@ +from typing import List, Optional, Tuple + +from ImportData import import_data + +day: int = 7 + +sample_data: List[str] = ''' +32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483 +'''.split('\n')[1:-1] + + +class Hand: + part: int = 1 + + def __init__(self, cards: str, bid: str): + self.bid: int = int(bid) + self.cards_string = cards + self._strength_of_hand = None + self.original_cards: List[int] = [] + self.sorted_cards = [] + self.reset_hand() + + @staticmethod + def face_to_int(card: str) -> int: + number: int + try: + number = int(card) + except ValueError: # it's pythonic but it stinks + if card == 'T': + number = 10 + elif card == 'J': + number = 11 if Hand.part == 1 else 1 + elif card == 'Q': + number = 12 + elif card == 'K': + number = 13 + elif card == 'A': + number = 14 + else: + number = 0 + return number + + @property + def strength_of_hand(self) -> int: + """ + 6 = five of a kind + 5 = four of a kind + 4 = full house + 3 = three of a kind + 2 = two pair + 1 = one pair + 0 = high card + :return: strength of the hand + """ + if self._strength_of_hand is not None: + return self._strength_of_hand + number_of_pairs: int = 0 + number_of_triples: int = 0 + number_of_quartets: int = 0 + number_of_quintets: int = 0 + number_of_wildcards: int = 0 + i: int = 0 + while i < len(self.original_cards) - 1: + card: int = self.sorted_cards[i] + card_count: int = self.sorted_cards[i:].count(card) + if card == 1: + number_of_wildcards = card_count + elif card_count == 2: + number_of_pairs += 1 + elif card_count == 3: + number_of_triples += 1 + elif card_count == 4: + number_of_quartets += 1 + elif card_count == 5: + number_of_quintets += 1 + i += card_count + # yahtzee + if (number_of_quintets == 1 or + (number_of_quartets == 1 and number_of_wildcards == 1) or + (number_of_triples == 1 and number_of_wildcards == 2) or + (number_of_pairs == 1 and number_of_wildcards == 3) or + number_of_wildcards == 4): + self._strength_of_hand = 6 + # four of a kind + elif (number_of_quartets == 1 or + (number_of_triples == 1 and number_of_wildcards == 1) or + (number_of_pairs == 1 and number_of_wildcards == 2) or + number_of_wildcards == 3): + self._strength_of_hand = 5 + # full house, or three of a kind + elif number_of_triples == 1: + self._strength_of_hand = 4 if number_of_pairs == 1 else 3 + # full house, or two pair + elif number_of_pairs == 2: + self._strength_of_hand = 4 if number_of_wildcards == 1 else 2 + # three of a kind + elif ((number_of_pairs == 1 and number_of_wildcards == 1) or + number_of_wildcards == 2): + self._strength_of_hand = 3 + # one pair + elif (number_of_pairs == 1 + or number_of_wildcards == 1): + self._strength_of_hand = 1 + else: + self._strength_of_hand = 0 + return self._strength_of_hand + + def is_stronger_than(self, other: "Hand") -> bool: + if self.strength_of_hand > other._strength_of_hand: + return True + if other._strength_of_hand > self.strength_of_hand: + return False + is_stronger: bool = False + i: int = 0 + while i < len(self.original_cards) and not is_stronger: + if self.original_cards[i] > other.original_cards[i]: + is_stronger = True + return is_stronger + + def get_sortable_key(self) -> Tuple[int, ...]: + keys: List[int] = self.original_cards.copy() + keys.insert(0, self.strength_of_hand) + return_value = tuple(keys) + return return_value + + def reset_hand(self): + self._strength_of_hand = None + self.original_cards: List[int] = [Hand.face_to_int(card) for card in self.cards_string] + self.sorted_cards = sorted(self.original_cards) + + +data_structure: type = List[Hand] + + +def parse_data(data: List[str]) -> data_structure: + return [Hand(datum.split()[0], datum.split()[1]) for datum in data] + + +def part_x(data: data_structure, part: int) -> int: + Hand.part = part + for hand in data: + hand.reset_hand() + sorted_hands: List[Hand] = sorted(data, key=lambda h: h.get_sortable_key()) + winnings: int = 0 + for rank, hand in enumerate(sorted_hands): + winning = (rank + 1) * hand.bid + winnings += winning + return winnings + + +def part1(data: data_structure) -> int: + return part_x(data, 1) + + +def part2(data: data_structure) -> int: + return part_x(data, 2) + + +if __name__ == '__main__': + production_ready = False + raw_data = import_data(day) if production_ready else sample_data + print(part1(parse_data(raw_data))) + print(part2(parse_data(raw_data))) diff --git a/2023/python/Day08.py b/2023/python/Day08.py new file mode 100644 index 0000000..0ec3536 --- /dev/null +++ b/2023/python/Day08.py @@ -0,0 +1,91 @@ +import functools +from typing import List, Dict, Tuple + +from ImportData import import_data + +day: int = 8 + +sample_data: List[List[str]] = [ + ''' +RL + +AAA = (BBB, CCC) +BBB = (DDD, EEE) +CCC = (ZZZ, GGG) +DDD = (DDD, DDD) +EEE = (EEE, EEE) +GGG = (GGG, GGG) +ZZZ = (ZZZ, ZZZ) + '''.split('\n')[1:-1], + ''' +LLR + +AAA = (BBB, BBB) +BBB = (AAA, ZZZ) +ZZZ = (ZZZ, ZZZ) + '''.split('\n')[1:-1], + ''' +LR + +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) + '''.split('\n')[1:-1] +] + +data_structure: type = Tuple[str, Dict[str, Tuple[str, str]]] + + +def parse_data(data: List[str]) -> data_structure: + directions: str = data[0] + nodes: Dict[str, Tuple[str, str]] = {} + for line in data[2:]: + key = line[:3] + left = line[7:10] + right = line[12:15] + nodes[key] = (left, right) + return directions, nodes + + +def part1(data: data_structure) -> int: + directions, nodes = data + steps = 0 + modulus = len(directions) + node = 'AAA' + while node != 'ZZZ': + direction = 0 if directions[steps % modulus] == 'L' else 1 + node = nodes[node][direction] + steps += 1 + return steps + + +def part2(data: data_structure) -> int: + directions, nodes = data + steps = 0 + modulus = len(directions) + locations = [location for location in nodes.keys() if location[-1] == 'A'] + while functools.reduce(lambda x, y: x + y, map(lambda z: (1 if z[-1] == 'Z' else 0), locations)) != len(locations): + direction = 0 if directions[steps % modulus] == 'L' else 1 + locations = [nodes[location][direction] for location in locations] + steps += 1 + return steps + + +if __name__ == '__main__': + production_ready = True + # raw_data = import_data(day) if production_ready else sample_data + # print(part1(parse_data(raw_data))) + # print(part2(parse_data(raw_data))) + if not production_ready: + print(part1(parse_data(sample_data[0]))) + print(part1(parse_data(sample_data[1]))) + print(part2(parse_data(sample_data[2]))) + else: + raw_data = import_data(day) + print(part1(parse_data(raw_data))) + print(part2(parse_data(raw_data))) diff --git a/2023/python/Day09.py b/2023/python/Day09.py new file mode 100644 index 0000000..f4e6822 --- /dev/null +++ b/2023/python/Day09.py @@ -0,0 +1,51 @@ +import functools +from typing import List, Tuple + +from ImportData import import_data + +day: int = 9 + +sample_data: List[str] = ''' +0 3 6 9 12 15 +1 3 6 10 15 21 +10 13 16 21 30 45 +'''.split('\n')[1:-1] + +data_structure: type = List[List[int]] + + +def parse_data(data: List[str]) -> data_structure: + reports: List[List[int]] = [] + for line in data: + reports.append([int(datum) for datum in line.split()]) + return reports + + +def extrapolator(report: List[int]) -> Tuple[int, int]: + differences: List[int] = [] + no_differences: bool = True + for i in range(1, len(report)): + difference = report[i] - report[i - 1] + differences.append(difference) + if difference != 0: + no_differences = False + if no_differences: + return report[0], report[0] + else: + left, right = extrapolator(differences) + return report[0] - left, report[-1] + right + + +def part1(data: data_structure) -> int: + return functools.reduce(lambda x, y: x + y, map(lambda report: extrapolator(report)[1], data)) + + +def part2(data: data_structure) -> int: + return functools.reduce(lambda x, y: x + y, map(lambda report: extrapolator(report)[0], data)) + + +if __name__ == '__main__': + production_ready = True + raw_data = import_data(day) if production_ready else sample_data + print(part1(parse_data(raw_data))) + print(part2(parse_data(raw_data))) -- GitLab