-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.py
73 lines (57 loc) · 2.44 KB
/
script.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
from GhostyUtils import aoc
from GhostyUtils.grid import Grid
from GhostyUtils.vec2 import Vec2
from itertools import combinations
# calculates and returns a list of antinode positions from a pair of antenna
def calc_antinodes(a: tuple, b: tuple, grid: Grid, resonates: bool = False) -> list[tuple]:
a, b = Vec2(a), Vec2(b)
v = b - a # vector between a & b
# print(f"a: {a} b: {b} v: {v} | {a - v} {b + v}")
nodes = []
# p1
if not resonates:
aa = (a - v).as_tuple() # project v back from a
ab = (b + v).as_tuple() # project v forward from b
nodes += [aa] if grid.in_bounds(aa) else []
nodes += [ab] if grid.in_bounds(ab) else []
# p2
else:
# add the antenna positions themselves
nodes += [a.as_tuple(), b.as_tuple()]
# repeatedly project v back from a until we go out of bounds
pos = a - v
while grid.in_bounds(pos):
nodes += [pos.as_tuple()]
pos -= v
# repeatedly project v forward from b until we go out of bounds
pos = b + v
while grid.in_bounds(pos):
nodes += [pos.as_tuple()]
pos += v
return nodes
def main():
grid = Grid(aoc.read_lines())
antennas = {}
# find all the antennas, and store them as {frequency: [positions]}
for freq, pos in grid.by_cell():
if freq != '.':
antennas.setdefault(freq, []).append(pos)
antinode_positions = set()
# calculate antinodes for each antenna frequency
for antenna, positions in antennas.items():
for combo in combinations(positions, 2):
antinode_positions.update(calc_antinodes(*combo, grid))
if aoc.args().verbose:
print(grid.render_with_overlays([{pos: '#' for pos in antinode_positions},
{p: freq for freq, pos in antennas.items() for p in pos}]))
print(f"p1: {len(antinode_positions)}")
# calculate antinodes for each antenna frequency, with resonance
for antenna, positions in antennas.items():
for combo in combinations(positions, 2):
antinode_positions.update(calc_antinodes(*combo, grid, resonates=True))
if aoc.args().verbose:
print(grid.render_with_overlays([{pos: '#' for pos in antinode_positions},
{p: freq for freq, pos in antennas.items() for p in pos}]))
print(f"p2: {len(antinode_positions)}")
if __name__ == "__main__":
main()