diff options
Diffstat (limited to 'day9/__init__.py')
| -rw-r--r-- | day9/__init__.py | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/day9/__init__.py b/day9/__init__.py new file mode 100644 index 0000000..696a6dc --- /dev/null +++ b/day9/__init__.py | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | from abc import ABC | ||
| 2 | from collections import Counter | ||
| 3 | from copy import copy | ||
| 4 | from functools import reduce | ||
| 5 | from operator import mul | ||
| 6 | from typing import List, Tuple, Dict, FrozenSet, Iterator, Set, Callable | ||
| 7 | |||
| 8 | from aoc import BaseAssignment | ||
| 9 | |||
| 10 | Coordinate = Tuple[int, int] | ||
| 11 | Field = List[List[int]] | ||
| 12 | |||
| 13 | |||
| 14 | class Assignment(BaseAssignment, ABC): | ||
| 15 | def parse_item(self, item: str) -> List[int]: | ||
| 16 | return [int(i) for i in item] | ||
| 17 | |||
| 18 | def read_input(self, example = False) -> Field: | ||
| 19 | return list(super().read_input(example)) | ||
| 20 | |||
| 21 | @classmethod | ||
| 22 | def neighbours(cls, field: Field, x: int, y: int) -> Iterator[Coordinate]: | ||
| 23 | for ny in list(range(max(0, y - 1), min(len(field) - 1, y + 1) + 1)): | ||
| 24 | if ny == y: | ||
| 25 | continue | ||
| 26 | yield (x, ny) | ||
| 27 | |||
| 28 | for nx in list(range(max(0, x - 1), min(len(field[0]) - 1, x + 1) + 1)): | ||
| 29 | if nx == x: | ||
| 30 | continue | ||
| 31 | yield (nx, y) | ||
| 32 | |||
| 33 | @classmethod | ||
| 34 | def lowest_points(cls, field: Field) -> Iterator[Coordinate]: | ||
| 35 | for y, row in enumerate(field): | ||
| 36 | for x, item in enumerate(row): | ||
| 37 | if all(field[ny][nx] > item for nx, ny in cls.neighbours(field, x, y)): | ||
| 38 | yield x, y | ||
| 39 | |||
| 40 | |||
| 41 | class AssignmentOne(Assignment): | ||
| 42 | example_result = 15 | ||
| 43 | |||
| 44 | def run(self, input: Field) -> int: | ||
| 45 | lows = list(input[y][x] for x, y in self.lowest_points(input)) | ||
| 46 | return sum(lows) + len(lows) | ||
| 47 | |||
| 48 | |||
| 49 | def flatten(l: List[Set[Coordinate]]) -> List: | ||
| 50 | flat_list = [] | ||
| 51 | for _ in l: | ||
| 52 | flat_list += _ | ||
| 53 | |||
| 54 | return flat_list | ||
| 55 | |||
| 56 | |||
| 57 | class AssignmentTwo(Assignment): | ||
| 58 | example_result = 1134 | ||
| 59 | |||
| 60 | @classmethod | ||
| 61 | def find_basin_members(cls, field: Field, x: int, y: int) -> Set[Coordinate]: | ||
| 62 | return { | ||
| 63 | (x, y), | ||
| 64 | *flatten([ | ||
| 65 | cls.find_basin_members(field, nx, ny) | ||
| 66 | for nx, ny in cls.neighbours(field, x, y) | ||
| 67 | if field[y][x] < field[ny][nx] < 9 | ||
| 68 | ]) | ||
| 69 | } | ||
| 70 | |||
| 71 | def run(self, input: Field) -> int: | ||
| 72 | return reduce( | ||
| 73 | mul, | ||
| 74 | sorted([ | ||
| 75 | len(self.find_basin_members(input, x, y)) | ||
| 76 | for x, y in self.lowest_points(input) | ||
| 77 | ], reverse=True)[:3], | ||
| 78 | 1 | ||
| 79 | ) | ||
