-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathday10.py
86 lines (65 loc) · 2.58 KB
/
day10.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
78
79
80
81
82
83
84
85
86
import pytest
from typing import List, Tuple
from math import atan2, degrees
from collections import defaultdict, OrderedDict
@pytest.mark.parametrize('filename, expected',
[
('input/day10_test1.txt', 8),
('input/day10_test2.txt', 33),
])
def test_part1(filename: str, expected: int):
data = parse_input(filename)
assert part1(data) == expected
def parse_input(filename: str) -> List[Tuple]:
asteroids = []
lines = [line.strip() for line in open(filename).readlines()]
for y, line in enumerate(lines):
for x, ch in enumerate(line):
if ch == '#':
asteroids.append((x, y))
return asteroids
def angle_between(p1, p2):
x_diff = p2[0] - p1[0]
y_diff = p2[1] - p1[1]
return degrees(atan2(y_diff, x_diff))
def part1(asteroids: List[Tuple]) -> (int, Tuple[int, int]):
max_seen = 0
best_asteroid = (0, 0)
for a1 in asteroids:
seen = set(angle_between(a1, a2) for a2 in asteroids if a1 != a2)
if len(seen) > max_seen:
max_seen = len(seen)
best_asteroid = a1
return max_seen, best_asteroid
def part2(asteroids: List[Tuple], outpost: Tuple[int, int]) -> int:
angles = defaultdict(lambda: [])
ox, oy = outpost
# get all asteroids - grouped by angle we see them at
for asteroid in asteroids:
if asteroid != outpost:
angles[(angle_between(asteroid, outpost) - 90 + 360) % 360].append(asteroid)
# sort the asteroids for each angle based on how far they are from the outpost
for angle in angles:
angles[angle] = list(sorted(angles[angle], key=lambda a: abs(a[0] - ox) + abs(a[1] - oy)))
# get the possible angles we rotate between
possible_angles = list(sorted(angles.keys()))
num_possible = len(possible_angles)
angle_index = 0
for i in range(1, 201):
# find the next angle with asteroids left
while not angles[possible_angles[angle_index]]:
angle_index = (angle_index + 1) % num_possible
# vaporize the closest asteroid
vaporized = angles[possible_angles[angle_index]].pop(0)
if i == 200:
return vaporized[0] * 100 + vaporized[1]
# move to the next angle
angle_index = (angle_index + 1) % num_possible
return 0
def main():
asteroids = parse_input('input/day10.txt')
max_seen, best_asteroid = part1(asteroids)
print(f'Part 1: {max_seen}')
print(f'Part 2: {part2(asteroids, best_asteroid)}')
if __name__ == "__main__":
main()