1
1
from GhostyUtils import aoc
2
2
from GhostyUtils .grid import Grid
3
3
from GhostyUtils .vec2 import Vec2 , Dir
4
+ from typing import Union
4
5
5
6
6
7
class Robot :
7
8
def __init__ (self , pos : Vec2 ) -> 'Robot' :
8
9
self .pos = Vec2 (pos )
9
10
11
+ def process (self , instructions : str , grid : Grid ):
12
+ for instr in instructions :
13
+ if instr == '\n ' :
14
+ continue
15
+ self .move (Dir .map_nswe ('^v<>' )[instr ], grid )
16
+
17
+ if aoc .args .verbose :
18
+ print (instr )
19
+ print (grid )
20
+
10
21
def move (self , dir : Dir , grid : Grid ) -> bool :
11
22
if grid [self .pos + dir ].move (dir , grid ):
12
23
grid [self .pos ] = Air (self .pos )
@@ -22,17 +33,60 @@ def __str__(self) -> str:
22
33
class Box :
23
34
def __init__ (self , pos : Vec2 ) -> 'Box' :
24
35
self .pos = Vec2 (pos )
36
+ self .width = 1
37
+ self .last_draw = - 1
38
+
39
+ def touching (self , dir : Dir , grid : Grid ) -> set [Union ['Box' , 'Wall' , 'Air' ]]:
40
+ if dir in {Dir .NORTH , Dir .SOUTH }:
41
+ cells = filter (lambda c : type (c ) is not Air ,
42
+ (grid [self .pos + Vec2 (Dir .EAST ) * i + dir ]
43
+ for i in range (self .width )))
44
+ else :
45
+ cells = filter (lambda c : type (c ) is not Air ,
46
+ [grid [self .pos + (dir if dir == Dir .WEST else Vec2 (dir ) * self .width )]])
47
+ cells = set (cells )
48
+
49
+ return cells
50
+
51
+ def can_move (self , dir : Dir , grid : Grid ) -> bool :
52
+ return all (cell .can_move (dir , grid ) for cell in self .touching (dir , grid ))
25
53
26
54
def move (self , dir : Dir , grid : Grid ) -> bool :
27
- if grid [self .pos + dir ].move (dir , grid ):
28
- grid [self .pos ] = Air (self .pos )
29
- self .pos += dir
30
- grid [self .pos ] = self
31
- return True
32
- return False
55
+ if not self .can_move (dir , grid ):
56
+ return False
57
+
58
+ for cell in self .touching (dir , grid ):
59
+ grid [cell .pos ].move (dir , grid )
60
+
61
+ self .pos += dir
62
+ if dir == Dir .WEST :
63
+ grid [self .pos + Vec2 (Dir .EAST ) * self .width ] = Air (None )
64
+ for i in range (self .width ):
65
+ grid [self .pos + Vec2 (Dir .EAST ) * i ] = self
66
+ elif dir == Dir .EAST :
67
+ grid [self .pos + Vec2 (Dir .WEST )] = Air (None )
68
+ for i in range (self .width ):
69
+ grid [self .pos + Vec2 (Dir .EAST ) * i ] = self
70
+ else :
71
+ for i in range (self .width ):
72
+ grid [self .pos + Vec2 (Dir .EAST ) * i - dir ] = Air (None )
73
+ grid [self .pos + Vec2 (Dir .EAST ) * i ] = self
74
+ return True
75
+
76
+ def gps (self ) -> int :
77
+ return 100 * self .pos .y + self .pos .x
33
78
34
79
def __str__ (self ) -> str :
35
- return 'O'
80
+ if self .width == 1 :
81
+ return 'O'
82
+ elif self .width == 2 :
83
+ self .last_draw += 1
84
+ if self .last_draw > 1 :
85
+ self .last_draw = 0
86
+ return '[]' [self .last_draw ]
87
+
88
+ def __repr__ (self ) -> str :
89
+ return f"Box at { self .pos } "
36
90
37
91
38
92
class Wall :
@@ -42,9 +96,15 @@ def __init__(self, pos: Vec2) -> 'Wall':
42
96
def move (self , dir : Dir , grid : Grid ) -> bool :
43
97
return False
44
98
99
+ def can_move (self , dir : Dir , grid : Grid ) -> bool :
100
+ return False
101
+
45
102
def __str__ (self ) -> str :
46
103
return '#'
47
104
105
+ def __repr__ (self ) -> str :
106
+ return f"Wall at { self .pos } "
107
+
48
108
49
109
class Air :
50
110
def __init__ (self , pos : Vec2 ) -> 'Air' :
@@ -53,38 +113,68 @@ def __init__(self, pos: Vec2) -> 'Air':
53
113
def move (self , dir : Dir , grid : Grid ) -> bool :
54
114
return True
55
115
116
+ def can_move (self , dir : Dir , grid : Grid ) -> bool :
117
+ return True
118
+
56
119
def __str__ (self ) -> str :
57
120
return '.'
58
121
59
122
60
- def convert (cell : str , pos : Vec2 ) -> Robot | Box | Wall | Air :
61
- return {'@' : Robot , '#' : Wall , 'O' : Box , '.' : Air }[cell ](pos )
62
-
123
+ def build_warehouse (floorplan : str , wide : bool = False ) -> tuple [Grid , Robot , list [Box ]]:
124
+ floorplan = floorplan .splitlines ()
125
+ if wide :
126
+ new_floorplan = []
127
+ for row in floorplan :
128
+ new_row = []
129
+ for c in row :
130
+ new_row .append ({'#' : '##' , '@' : '@.' , 'O' : '[]' , '.' : '..' }[c ])
131
+ new_floorplan .append ('' .join (new_row ))
132
+ floorplan = new_floorplan
63
133
64
- def main ():
65
- warehouse , instructions = aoc .read_sections ()
66
- warehouse = Grid (warehouse .splitlines ())
134
+ warehouse = Grid (floorplan )
67
135
robot = None
68
136
boxes = []
69
137
for cell , pos in warehouse .by_cell ():
138
+ if cell == ']' :
139
+ warehouse [pos ] = warehouse [Vec2 (pos ) + Dir .WEST ]
140
+ warehouse [pos ].width = 2
141
+ continue
70
142
warehouse [pos ] = convert (cell , pos )
71
143
if type (warehouse [pos ]) is Box :
72
144
boxes .append (warehouse [pos ])
73
145
elif type (warehouse [pos ]) is Robot :
74
146
robot = warehouse [pos ]
147
+ return warehouse , robot , boxes
75
148
76
- if aoc .args .verbose or aoc .args .progress :
77
- print (warehouse )
78
149
79
- for instr in instructions :
80
- if instr == '\n ' :
81
- continue
82
- robot .move (Dir .map_nswe ('^v<>' )[instr ], warehouse )
150
+ def convert (cell : str , pos : Vec2 ) -> Robot | Box | Wall | Air :
151
+ return {
152
+ '@' : Robot ,
153
+ 'O' : Box ,
154
+ '[' : Box ,
155
+ '#' : Wall ,
156
+ '.' : Air
157
+ }[cell ](pos )
83
158
84
- if aoc .args .verbose or aoc .args .progress :
85
- print (warehouse )
86
159
87
- print (f"p1: { sum (box .pos .y * 100 + box .pos .x for box in boxes )} " )
160
+ def main ():
161
+ floorplan , instructions = aoc .read_sections ()
162
+
163
+ warehouse , robot , boxes = build_warehouse (floorplan )
164
+ if aoc .args .verbose or aoc .args .progress :
165
+ print (warehouse )
166
+ robot .process (instructions , warehouse )
167
+ if aoc .args .progress :
168
+ print (warehouse )
169
+ print (f"p1: { sum (box .gps () for box in boxes )} " )
170
+
171
+ warehouse , robot , boxes = build_warehouse (floorplan , wide = True )
172
+ if aoc .args .verbose or aoc .args .progress :
173
+ print (warehouse )
174
+ robot .process (instructions , warehouse )
175
+ if aoc .args .progress :
176
+ print (warehouse )
177
+ print (f"p2: { sum (box .gps () for box in boxes )} " )
88
178
89
179
90
180
if __name__ == "__main__" :
0 commit comments