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