diff options
Diffstat (limited to 'day3/__init__.py')
| -rw-r--r-- | day3/__init__.py | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/day3/__init__.py b/day3/__init__.py new file mode 100644 index 0000000..ebe243e --- /dev/null +++ b/day3/__init__.py | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from abc import ABC | ||
| 3 | from functools import reduce | ||
| 4 | from typing import Optional, Iterator | ||
| 5 | |||
| 6 | from aoc import BaseAssignment, I, T | ||
| 7 | from aoc.datastructures import Coordinate | ||
| 8 | |||
| 9 | |||
| 10 | class Assignment(BaseAssignment, ABC): | ||
| 11 | def symbol_overlap(self, number_coordinates: list[Coordinate], symbols: set[Coordinate]) -> set[Coordinate]: | ||
| 12 | neighbours = { | ||
| 13 | neighbour | ||
| 14 | for coordinate in number_coordinates | ||
| 15 | for neighbour in coordinate.neighbours() | ||
| 16 | } | ||
| 17 | |||
| 18 | return neighbours & symbols | ||
| 19 | |||
| 20 | def read_input(self, example=False) -> tuple[list[str], list[tuple[list[Coordinate], int]], set[Coordinate]]: | ||
| 21 | schematic = list(super().read_input(example)) | ||
| 22 | numbers: list[tuple[list[Coordinate], int]] = list() | ||
| 23 | symbols: set[Coordinate] = set() | ||
| 24 | |||
| 25 | for y, row in enumerate(schematic): | ||
| 26 | number: Optional[list[list[Coordinate], str]] = None | ||
| 27 | |||
| 28 | for x, item in enumerate(row): | ||
| 29 | if item.isdigit(): | ||
| 30 | if number is None: | ||
| 31 | number = [[], ''] | ||
| 32 | |||
| 33 | number[0].append(Coordinate(x, y)) | ||
| 34 | number[1] += item | ||
| 35 | continue | ||
| 36 | |||
| 37 | if number is not None: | ||
| 38 | numbers.append((number[0], int(number[1]))) | ||
| 39 | number = None | ||
| 40 | |||
| 41 | if item != '.': | ||
| 42 | symbols.add(Coordinate(x, y)) | ||
| 43 | |||
| 44 | if number is not None: | ||
| 45 | numbers.append((number[0], int(number[1]))) | ||
| 46 | |||
| 47 | return schematic, numbers, symbols | ||
| 48 | |||
| 49 | |||
| 50 | class AssignmentOne(Assignment): | ||
| 51 | example_result = 4361 | ||
| 52 | |||
| 53 | def is_part_number(self, number_coordinates: list[Coordinate], symbols: set[Coordinate]) -> bool: | ||
| 54 | return len(self.symbol_overlap(number_coordinates, symbols)) > 0 | ||
| 55 | |||
| 56 | def run(self, input: tuple[list[tuple[list[Coordinate], int]], set[Coordinate]]) -> int: | ||
| 57 | _, numbers, symbols = input | ||
| 58 | |||
| 59 | part_numbers = [] | ||
| 60 | |||
| 61 | for coordinates, number in numbers: | ||
| 62 | if self.is_part_number(coordinates, symbols): | ||
| 63 | part_numbers.append(number) | ||
| 64 | |||
| 65 | return sum(part_numbers) | ||
| 66 | |||
| 67 | |||
| 68 | class AssignmentTwo(Assignment): | ||
| 69 | example_result = 467835 | ||
| 70 | |||
| 71 | def run(self, input: tuple[list[tuple[list[Coordinate], int]], set[Coordinate]]) -> int: | ||
| 72 | schematic, numbers, symbols = input | ||
| 73 | |||
| 74 | possible_gears = {} | ||
| 75 | |||
| 76 | for coordinates, number in numbers: | ||
| 77 | for symbol in self.symbol_overlap(coordinates, symbols): | ||
| 78 | if schematic[symbol.y][symbol.x] != '*': | ||
| 79 | continue | ||
| 80 | |||
| 81 | if symbol not in possible_gears: | ||
| 82 | possible_gears[symbol] = [] | ||
| 83 | |||
| 84 | possible_gears[symbol].append(number) | ||
| 85 | |||
| 86 | return sum([ | ||
| 87 | reduce( | ||
| 88 | lambda total, number: total * number, | ||
| 89 | numbers, | ||
| 90 | 1 | ||
| 91 | ) | ||
| 92 | for symbol, numbers in possible_gears.items() | ||
| 93 | if len(numbers) > 1 | ||
| 94 | ]) | ||
