diff options
Diffstat (limited to 'day4/__init__.py')
| -rw-r--r-- | day4/__init__.py | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/day4/__init__.py b/day4/__init__.py new file mode 100644 index 0000000..74c01f1 --- /dev/null +++ b/day4/__init__.py | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | from collections import Counter | ||
| 2 | from copy import copy | ||
| 3 | from dataclasses import dataclass | ||
| 4 | from typing import Iterator, List, Optional, Tuple, Callable | ||
| 5 | |||
| 6 | from aoc import BaseAssignment | ||
| 7 | |||
| 8 | def bold(item: str, condition: Callable[[], bool]): | ||
| 9 | return f'\033[36m{item}\033[0m' if condition() else item | ||
| 10 | |||
| 11 | @dataclass | ||
| 12 | class Number: | ||
| 13 | nr: int | ||
| 14 | marked: bool = False | ||
| 15 | |||
| 16 | def __int__(self): | ||
| 17 | return self.nr | ||
| 18 | |||
| 19 | def __bool__(self): | ||
| 20 | return self.marked | ||
| 21 | |||
| 22 | def __radd__(self, other): | ||
| 23 | return other + int(self) | ||
| 24 | |||
| 25 | def mark(self): | ||
| 26 | self.marked = True | ||
| 27 | |||
| 28 | @dataclass | ||
| 29 | class BingoCard: | ||
| 30 | card: List[List[Number]] | ||
| 31 | |||
| 32 | @property | ||
| 33 | def bingo(self) -> bool: | ||
| 34 | return any([ | ||
| 35 | *[all(bool(item) for item in row) for row in self.card], | ||
| 36 | *[all(bool(row[col_index]) for row in self.card) for col_index in range(5)] | ||
| 37 | ]) | ||
| 38 | |||
| 39 | def mark(self, nr: int): | ||
| 40 | for row in self.card: | ||
| 41 | for item in row: | ||
| 42 | if int(item) == nr: | ||
| 43 | item.mark() | ||
| 44 | |||
| 45 | @property | ||
| 46 | def unmarked(self): | ||
| 47 | return [ | ||
| 48 | item | ||
| 49 | for row in self.card | ||
| 50 | for item in row | ||
| 51 | if not bool(item) | ||
| 52 | ] | ||
| 53 | |||
| 54 | def __str__(self): | ||
| 55 | return '\n'.join([ | ||
| 56 | ''.join([ | ||
| 57 | bold(str(int(item)).rjust(2, ' ').ljust(3, ' '), lambda: item.marked) | ||
| 58 | for item in row | ||
| 59 | ]) | ||
| 60 | for row in self.card | ||
| 61 | ]) | ||
| 62 | |||
| 63 | |||
| 64 | class Assignment(BaseAssignment): | ||
| 65 | def read_input(self, example = False) -> Tuple[List[int], List[BingoCard]]: | ||
| 66 | cards = [] | ||
| 67 | card = None | ||
| 68 | |||
| 69 | input = super().read_input(example) | ||
| 70 | nrs = [ int(n) for n in next(input).split(',') ] | ||
| 71 | |||
| 72 | try: | ||
| 73 | row = next(input) | ||
| 74 | |||
| 75 | while True: | ||
| 76 | if row == '': | ||
| 77 | if card is not None: | ||
| 78 | cards.append(BingoCard(card=card)) | ||
| 79 | card = [] | ||
| 80 | else: | ||
| 81 | card.append([ Number(nr=int(item)) for item in row.split(' ') if item != '']) | ||
| 82 | |||
| 83 | row = next(input) | ||
| 84 | |||
| 85 | except StopIteration: | ||
| 86 | if card is not None: | ||
| 87 | cards.append(BingoCard(card=card)) | ||
| 88 | |||
| 89 | return nrs, cards | ||
| 90 | |||
| 91 | |||
| 92 | class AssignmentOne(Assignment): | ||
| 93 | example_result = 4512 | ||
| 94 | |||
| 95 | def run(self, input: Tuple[List[int], List[BingoCard]]) -> int: | ||
| 96 | nrs, cards = input | ||
| 97 | |||
| 98 | for nr in nrs: | ||
| 99 | for card in cards: | ||
| 100 | card.mark(nr) | ||
| 101 | |||
| 102 | if card.bingo: | ||
| 103 | return nr * sum(card.unmarked) | ||
| 104 | |||
| 105 | |||
| 106 | class AssignmentTwo(Assignment): | ||
| 107 | example_result = 1924 | ||
| 108 | |||
| 109 | def run(self, input: Tuple[List[int], List[BingoCard]]) -> int: | ||
| 110 | nrs, cards = input | ||
| 111 | |||
| 112 | in_game = copy(cards) | ||
| 113 | for nr in nrs: | ||
| 114 | for card in copy(in_game): | ||
| 115 | card.mark(nr) | ||
| 116 | |||
| 117 | if card.bingo: | ||
| 118 | in_game.remove(card) | ||
| 119 | |||
| 120 | if len(in_game) == 0: | ||
| 121 | return nr * sum(card.unmarked) \ No newline at end of file | ||
