3
3
from collections import namedtuple
4
4
import math
5
5
from functools import lru_cache
6
+ import itertools
6
7
from operator import itemgetter
7
- from pprint import pprint
8
8
9
9
class Vector (namedtuple ('Vector' , ['x' ,'y' ])):
10
10
def __add__ (self , other ):
@@ -27,10 +27,11 @@ def main():
27
27
aoc .header ("Monitoring Station" )
28
28
aoc .run_tests ()
29
29
30
- aoc .output (1 , part1 )
31
- # aoc.output(2, part2)
30
+ ( asteroids , edge , station , _ ) = aoc .output (1 , part1 , post = itemgetter ( 3 ) )
31
+ aoc .output (2 , part2 , args = [ asteroids , edge , station ] )
32
32
33
33
def test ():
34
+ # part 1
34
35
def assert_best (inp : List [str ], station : Vector , count : int ):
35
36
(ast , edge ) = to_points (inp )
36
37
best = find_best (ast , edge )
@@ -84,7 +85,7 @@ def assert_best(inp : List[str], station : Vector, count : int):
84
85
".....#.#.."
85
86
], Vector (6 ,3 ), 41 )
86
87
87
- assert_best ( [
88
+ large_example = [
88
89
".#..##.###...#######" ,
89
90
"##.############..##." ,
90
91
".#.######.########.#" ,
@@ -105,15 +106,42 @@ def assert_best(inp : List[str], station : Vector, count : int):
105
106
".#.#.###########.###" ,
106
107
"#.#.#.#####.####.###" ,
107
108
"###.##.####.##.#..##"
108
- ], Vector (11 ,13 ), 210 )
109
+ ]
110
+ assert_best (large_example , Vector (11 ,13 ), 210 )
111
+
112
+ # part 2
113
+ (ast , edge ) = to_points (large_example )
114
+ (station , _ ) = find_best (ast ,edge )
115
+ ast .remove (station )
116
+
117
+ vaporized_ordered = list (vaporize (station , rays_sorted (edge ), ast , edge ))
118
+ assert vaporized_ordered [:3 ] == [Vector (11 ,12 ),Vector (12 ,1 ),Vector (12 ,2 )]
119
+
120
+ assert vaporized_ordered [9 ] == Vector (12 ,8 )
121
+ assert vaporized_ordered [19 ] == Vector (16 ,0 )
122
+ assert vaporized_ordered [49 ] == Vector (16 ,9 )
123
+ assert vaporized_ordered [99 ] == Vector (10 ,16 )
124
+
125
+ assert vaporized_ordered [198 ] == Vector (9 ,6 )
126
+ assert vaporized_ordered [199 ] == Vector (8 ,2 )
127
+ assert vaporized_ordered [200 ] == Vector (10 ,9 )
128
+
129
+ assert vaporized_ordered [298 ] == Vector (11 ,1 )
130
+ assert len (vaporized_ordered ) == 299
131
+
132
+ assert next (itertools .islice (vaporized_ordered , 199 , None )) == Vector (8 ,2 )
133
+
109
134
110
135
def part1 ():
111
136
(asteroids , edge ) = to_points (aoc .get_input ().readlines ())
112
137
best = find_best (asteroids , edge )
113
- return best [1 ]
138
+ return ( asteroids , edge , best [0 ], best [ 1 ])
114
139
115
- def part2 ():
116
- pass
140
+ def part2 (asteroids , edge , station ):
141
+ asteroids .remove (station )
142
+ it = vaporize (station , rays_sorted (edge ), asteroids , edge )
143
+ v = next (itertools .islice (it , 199 , None ))
144
+ return (v .x * 100 ) + v .y
117
145
118
146
def to_points (lines : List [str ]) -> Tuple [Set [Vector ], Vector ]:
119
147
edge = Vector (len (lines [- 1 ]) - 1 , len (lines ) - 1 )
@@ -135,20 +163,6 @@ def edges(edge : Vector):
135
163
def in_field (test : Vector , edge : Vector ):
136
164
return 0 <= test .x <= edge .x and 0 <= test .y <= edge .y
137
165
138
- def rays (origin : Vector , edge : Vector , edge_vectors ):
139
- # This is problematic
140
- # Consider a ray of normalised length
141
- # where 1 step from the origin is before the edge,
142
- # and two steps from the origin is after the edge
143
- # It will never be generated
144
- rays = set ()
145
- for e in map (lambda e : e - origin , edge_vectors ):
146
- if (g := math .gcd (e .x ,e .y )) > 1 :
147
- rays .add (Vector (e .x // g , e .y // g ))
148
- elif g > 0 :
149
- rays .add (e )
150
- return rays
151
-
152
166
@lru_cache
153
167
def rays_fixed (edge : Vector ):
154
168
def it (origin : Vector ):
@@ -164,25 +178,36 @@ def it(origin : Vector):
164
178
rays .update (it (Vector ( edge .x , edge .y )))
165
179
return rays
166
180
181
+ @lru_cache
182
+ def rays_sorted (edge : Vector ):
183
+ rays = rays_fixed (edge )
184
+ return list (sorted (rays , key = lambda V :math .atan2 (V .x , V .y ), reverse = True ))
185
+
167
186
def trace (origin : Vector , rays : Set [Vector ], asteroids : Set [Vector ], edge : Vector ) -> Set [Vector ]:
168
187
for ray in rays :
169
- # print(f" Tracing along {ray}", end="", flush=True)
170
188
p = origin + ray
171
189
while in_field (p , edge ):
172
- # print(".", end="", flush=True)
173
190
if p in asteroids :
174
191
yield p
175
- # print(f"asteroid at {p}")
176
192
break
177
193
p = p + ray
178
- # print()
194
+
195
+ def vaporize (origin : Vector , rays_clockwise : List [Vector ], asteroids : Set [Vector ], edge : Vector ):
196
+ for ray in itertools .cycle (rays_clockwise ):
197
+ p = origin + ray
198
+ while in_field (p , edge ):
199
+ if p in asteroids :
200
+ yield p
201
+ asteroids .remove (p )
202
+ break # while in_field, go to next iteration of for
203
+ p = p + ray
204
+ if len (asteroids ) == 0 : break
205
+
179
206
180
207
def naive_raytrace (asteroids : Set [Vector ], edge : Vector ):
181
208
r = rays_fixed (edge )
182
209
183
210
for asteroid in asteroids :
184
- # print(f"Tracing from {asteroid}")
185
- # print(f" Got rays ({len(r)})")
186
211
yield asteroid , trace (asteroid , r , asteroids , edge )
187
212
188
213
def find_best (asteroids : Set [Vector ], edge : Vector ):
@@ -208,7 +233,6 @@ def print_field(mappings : Dict[str,Set[Vector]], edge : Vector):
208
233
for line in field :
209
234
print ("" .join (line ))
210
235
211
-
212
236
213
237
if __name__ == "__main__" :
214
238
main ()
0 commit comments