summaryrefslogtreecommitdiffstats
path: root/day3/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'day3/__init__.py')
-rw-r--r--day3/__init__.py94
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 -*-
2from abc import ABC
3from functools import reduce
4from typing import Optional, Iterator
5
6from aoc import BaseAssignment, I, T
7from aoc.datastructures import Coordinate
8
9
10class 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
50class 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
68class 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 ])