-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday_14.py
77 lines (68 loc) · 2.43 KB
/
day_14.py
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
import re
from collections import defaultdict
from models.aoc_solution import AOCSolution
class Day14(AOCSolution):
EXPECTED = {
"part_one": {"sample": 12, "data": 222208000},
"part_two": {"sample": 0, "data": 7623},
}
def __post_init__(self) -> None:
"""Sample uses a smaller grid"""
self.sample = len(self.data.splitlines()) < 20
self.width = 11 if self.sample else 101
self.height = 7 if self.sample else 103
self.robots = [
tuple(map(int, re.findall(r"-?\d+", line)))
for line in self.data.splitlines()
]
def part_one(self) -> int:
"""Find product of robots in each quadrant after 100 moves"""
ends = [
((x + dx * 100) % self.width, (y + dy * 100) % self.height)
for x, y, dx, dy in self.robots
]
lu = ru = ld = rd = 0
mw = self.width // 2
mh = self.height // 2
for x, y in ends:
lu += x < mw and y < mh
ru += x > mw and y < mh
ld += x < mw and y > mh
rd += x > mw and y > mh
return lu * ru * ld * rd
@staticmethod
def find_start_period(
vectors: list[tuple[int, int]], max_pos: int
) -> tuple[int, int]:
"""Given a list of (u, du) find the period where 30 or more line up"""
start = period = 0
for moves in range(1000):
pos = defaultdict(int)
for u, du in vectors:
pos[(u + du * moves) % max_pos] += 1
if max(pos.values()) >= 30:
if not start:
start = moves
else:
period = moves - start
if start and period:
return start, period
return 0, 0
def part_two(self) -> int:
"""Sometimes robots line up vertically or horizontally, find the first time
and the period of this happening, and where the two alignments overlap, return moves"""
if self.sample:
return 0
xs, xp = self.find_start_period(
[(x, dx) for x, _, dx, _ in self.robots], self.width
)
ys, yp = self.find_start_period(
[(y, dy) for _, y, _, dy in self.robots], self.height
)
for x in range(xp):
total = xs - ys + xp * x
if total % yp == 0:
return xs + xp * x
return 0
if __name__ == "__main__":
Day14().run()