2
2
from typing import List , Tuple , Set , Dict
3
3
from collections import namedtuple
4
4
import math
5
+ from functools import lru_cache
6
+ from operator import itemgetter
5
7
from pprint import pprint
6
8
7
9
class Vector (namedtuple ('Vector' , ['x' ,'y' ])):
@@ -13,56 +15,113 @@ def __sub__(self, other):
13
15
assert type (other ) == Vector
14
16
return Vector (self .x - other .x , self .y - other .y )
15
17
18
+ def __mul__ (self , other ):
19
+ assert type (other ) == int
20
+ return Vector (self .x * other , self .y * other )
21
+
22
+ def __floordiv__ (self , other ):
23
+ assert type (other ) == int
24
+ return Vector (self .x // other , self .y // other )
25
+
16
26
def main ():
17
27
aoc .header ("Monitoring Station" )
18
28
aoc .run_tests ()
19
29
20
- # aoc.output(1, part1)
30
+ aoc .output (1 , part1 )
21
31
# aoc.output(2, part2)
22
32
23
33
def test ():
24
-
25
- inp1 = [
34
+ def assert_best (inp : List [str ], station : Vector , count : int ):
35
+ (ast , edge ) = to_points (inp )
36
+ best = find_best (ast , edge )
37
+ assert best [0 ] == station
38
+ assert count == best [1 ]
39
+
40
+ assert_best ([
26
41
".#..#" ,
27
42
"....." ,
28
43
"#####" ,
29
44
"....#" ,
30
45
"...##"
31
- ]
32
- t = to_points (inp1 )
33
- print_field ({
34
- '#' : t [0 ],
35
- "." : t [1 ]
36
- }, t [2 ])
37
-
38
- print (t [2 ])
39
- edge_vectors = set (edges (t [2 ]))
40
- pprint (edge_vectors )
41
- print_field ({'+' : edge_vectors }, t [2 ])
42
-
43
- for origin , asteroids in naive_raytrace (t [0 ],t [1 ],t [2 ]):
44
- print_field ({
45
- "*" : t [0 ],
46
- "#" : asteroids ,
47
- "." : {origin }
48
- },t [2 ])
49
- input ()
46
+ ], Vector (3 ,4 ), 8 )
47
+
48
+ assert_best ([
49
+ "......#.#." ,
50
+ "#..#.#...." ,
51
+ "..#######." ,
52
+ ".#.#.###.." ,
53
+ ".#..#....." ,
54
+ "..#....#.#" ,
55
+ "#..#....#." ,
56
+ ".##.#..###" ,
57
+ "##...#..#." ,
58
+ ".#....####"
59
+ ], Vector (5 ,8 ), 33 )
60
+
61
+ assert_best ([
62
+ "#.#...#.#." ,
63
+ ".###....#." ,
64
+ ".#....#..." ,
65
+ "##.#.#.#.#" ,
66
+ "....#.#.#." ,
67
+ ".##..###.#" ,
68
+ "..#...##.." ,
69
+ "..##....##" ,
70
+ "......#..." ,
71
+ ".####.###."
72
+ ], Vector (1 ,2 ), 35 )
73
+
74
+ assert_best ([
75
+ ".#..#..###" ,
76
+ "####.###.#" ,
77
+ "....###.#." ,
78
+ "..###.##.#" ,
79
+ "##.##.#.#." ,
80
+ "....###..#" ,
81
+ "..#.#..#.#" ,
82
+ "#..#.#.###" ,
83
+ ".##...##.#" ,
84
+ ".....#.#.."
85
+ ], Vector (6 ,3 ), 41 )
86
+
87
+ assert_best ([
88
+ ".#..##.###...#######" ,
89
+ "##.############..##." ,
90
+ ".#.######.########.#" ,
91
+ ".###.#######.####.#." ,
92
+ "#####.##.#.##.###.##" ,
93
+ "..#####..#.#########" ,
94
+ "####################" ,
95
+ "#.####....###.#.#.##" ,
96
+ "##.#################" ,
97
+ "#####.##.###..####.." ,
98
+ "..######..##.#######" ,
99
+ "####.##.####...##..#" ,
100
+ ".#####..#.######.###" ,
101
+ "##...#.##########..." ,
102
+ "#.##########.#######" ,
103
+ ".####.#.###.###.#.##" ,
104
+ "....##.##.###..#####" ,
105
+ ".#.#.###########.###" ,
106
+ "#.#.#.#####.####.###" ,
107
+ "###.##.####.##.#..##"
108
+ ], Vector (11 ,13 ), 210 )
50
109
51
110
def part1 ():
52
- pass
111
+ (asteroids , edge ) = to_points (aoc .get_input ().readlines ())
112
+ best = find_best (asteroids , edge )
113
+ return best [1 ]
53
114
54
115
def part2 ():
55
116
pass
56
117
57
- def to_points (lines : List [str ]) -> Tuple [Set [Vector ], Set [ Vector ], Vector ]:
118
+ def to_points (lines : List [str ]) -> Tuple [Set [Vector ], Vector ]:
58
119
edge = Vector (len (lines [- 1 ]) - 1 , len (lines ) - 1 )
59
120
asteroids = set ()
60
- gaps = set ()
61
121
for x in range (edge .x + 1 ):
62
122
for y in range (edge .y + 1 ):
63
123
if lines [y ][x ] == "#" : asteroids .add (Vector (x ,y ))
64
- elif lines [y ][x ] == "." : gaps .add (Vector (x ,y ))
65
- return (asteroids , gaps , edge )
124
+ return (asteroids , edge )
66
125
67
126
68
127
def edges (edge : Vector ):
@@ -90,28 +149,50 @@ def rays(origin : Vector, edge : Vector, edge_vectors):
90
149
rays .add (e )
91
150
return rays
92
151
152
+ @lru_cache
153
+ def rays_fixed (edge : Vector ):
154
+ def it (origin : Vector ):
155
+ for x in range (edge .x + 1 ):
156
+ for y in range (edge .y + 1 ):
157
+ ray = origin - Vector (x ,y )
158
+ if (g := math .gcd (origin .x - x , origin .y - y )) > 0 :
159
+ yield (ray // g )
160
+ rays = set ()
161
+ rays .update (it (Vector ( 0 , 0 )))
162
+ rays .update (it (Vector ( edge .x , 0 )))
163
+ rays .update (it (Vector ( 0 , edge .y )))
164
+ rays .update (it (Vector ( edge .x , edge .y )))
165
+ return rays
166
+
93
167
def trace (origin : Vector , rays : Set [Vector ], asteroids : Set [Vector ], edge : Vector ) -> Set [Vector ]:
94
168
for ray in rays :
95
- print (f" Tracing along { ray } " , end = "" , flush = True )
169
+ # print(f" Tracing along {ray}", end="", flush=True)
96
170
p = origin + ray
97
171
while in_field (p , edge ):
98
- print ("." , end = "" , flush = True )
172
+ # print(".", end="", flush=True)
99
173
if p in asteroids :
100
174
yield p
101
- print (f"asteroid at { p } " )
175
+ # print(f"asteroid at {p}")
102
176
break
103
177
p = p + ray
104
- print ()
105
-
106
- def naive_raytrace (asteroids : Set [Vector ], gaps : Set [Vector ], edge : Vector ):
107
- edge_vectors = set (edges (edge ))
108
-
109
- for gap in gaps :
110
- print (f"Tracing from { gap } " )
111
- r = rays (gap , edge , edge_vectors )
112
- print (f" Got rays ({ len (r )} )" )
113
- yield gap , trace (gap , r , asteroids , edge )
114
-
178
+ # print()
179
+
180
+ def naive_raytrace (asteroids : Set [Vector ], edge : Vector ):
181
+ r = rays_fixed (edge )
182
+
183
+ for asteroid in asteroids :
184
+ # print(f"Tracing from {asteroid}")
185
+ # print(f" Got rays ({len(r)})")
186
+ yield asteroid , trace (asteroid , r , asteroids , edge )
187
+
188
+ def find_best (asteroids : Set [Vector ], edge : Vector ):
189
+ return max (
190
+ map (
191
+ lambda t : (t [0 ], len (set (t [1 ]))),
192
+ naive_raytrace (asteroids , edge )
193
+ ),
194
+ key = itemgetter (1 )
195
+ )
115
196
116
197
def print_field (mappings : Dict [str ,Set [Vector ]], edge : Vector ):
117
198
field = [[" " ] * (edge .x + 1 ) for _ in range ((edge .y + 1 ))]
0 commit comments