11#![ feature( test) ]
22
33use advent_lib:: day_main;
4- use advent_lib:: direction:: Direction ;
4+ use advent_lib:: direction:: { Direction , ALL_DIRECTIONS } ;
55use advent_lib:: geometry:: { point2, vector2, Point , Vector } ;
66use advent_lib:: grid:: { uneven_grid_parser, Grid } ;
77use advent_lib:: parsing:: double_line_ending;
88use advent_macros:: FromRepr ;
9- use fxhash:: { FxBuildHasher , FxHashMap } ;
9+ use fxhash:: FxHashMap ;
1010use nom_parse_macros:: parse_from;
1111use std:: cmp:: min;
1212use std:: ops:: Neg ;
@@ -25,23 +25,17 @@ enum Command {
2525 Right ,
2626}
2727
28+ #[ derive( Debug , Copy , Clone ) ]
2829struct Person {
29- position : Point < 2 , i32 > ,
30- direction : Direction ,
30+ pos : Point < 2 , i32 > ,
31+ dir : Direction ,
3132}
3233
3334impl Person {
34- fn step ( & self ) -> Point < 2 , i32 > { self . position + self . direction }
35+ fn step ( & self ) -> Person { Person { dir : self . dir , pos : self . pos + self . dir } }
3536
3637 fn score ( & self ) -> i32 {
37- ( self . position . x ( ) + 1 ) * 4
38- + ( self . position . y ( ) + 1 ) * 1000
39- + match self . direction {
40- East => 0 ,
41- South => 1 ,
42- West => 2 ,
43- North => 3 ,
44- }
38+ ( self . pos . x ( ) + 1 ) * 4 + ( self . pos . y ( ) + 1 ) * 1000 + i32:: from ( self . dir )
4539 }
4640}
4741
@@ -60,100 +54,231 @@ struct GridAndCommands {
6054 commands : Vec < Command > ,
6155}
6256
57+ #[ derive( Debug ) ]
58+ struct Block {
59+ grid : Grid < FieldType > ,
60+ offset : Vector < 2 , i32 > ,
61+ }
62+
6363#[ derive( Debug ) ]
6464struct Input {
6565 commands : Vec < Command > ,
66- block_size : usize ,
67- blocks : FxHashMap < Vector < 2 , i32 > , Grid < FieldType > > ,
68- start_offset : Vector < 2 , i32 > ,
66+ block_size : i32 ,
67+ blocks : [ Block ; 6 ] ,
68+ block_jump_2d : BlockJumps ,
69+ block_jump_3d : BlockJumps ,
70+ }
71+
72+ #[ derive( Debug , Copy , Clone ) ]
73+ enum Turn {
74+ None ,
75+ Left ,
76+ Right ,
77+ Around ,
78+ }
79+
80+ impl Turn {
81+ fn apply ( & self , person : & Person , block_size : i32 ) -> Person {
82+ let position = person. pos + ( person. dir . neg ( ) . as_vec ( ) * block_size) ;
83+ match self {
84+ Self :: None => Person { dir : person. dir , pos : position } ,
85+ Self :: Left => Person {
86+ dir : person. dir . turn_left ( ) ,
87+ pos : point2 ( position. y ( ) , block_size - position. x ( ) - 1 ) ,
88+ } ,
89+ Self :: Right => Person {
90+ dir : person. dir . turn_right ( ) ,
91+ pos : point2 ( block_size - position. y ( ) - 1 , position. x ( ) ) ,
92+ } ,
93+ Self :: Around => Person {
94+ dir : -person. dir ,
95+ pos : point2 (
96+ block_size - position. x ( ) - 1 ,
97+ ( block_size) - position. y ( ) - 1 ,
98+ ) ,
99+ } ,
100+ }
101+ }
69102}
70103
71104fn preprocess ( input : GridAndCommands ) -> Input {
72- let mut blocks = FxHashMap :: with_capacity_and_hasher ( 10 , FxBuildHasher :: default ( ) ) ;
73- let block_size = ( min ( input. grid . x_range ( ) . end , input. grid . y_range ( ) . end ) / 3 ) as usize ;
105+ let mut blocks = vec ! [ ] ;
106+ let block_size = min ( input. grid . x_range ( ) . end , input. grid . y_range ( ) . end ) / 3 ;
74107
75- for y in input. grid . y_range ( ) . step_by ( block_size) {
76- for x in input. grid . x_range ( ) . step_by ( block_size) {
108+ for y in input. grid . y_range ( ) . step_by ( block_size as usize ) {
109+ for x in input. grid . x_range ( ) . step_by ( block_size as usize ) {
77110 if input. grid . get ( point2 ( x, y) ) . cloned ( ) . unwrap_or_default ( ) != Outside {
78- blocks. insert (
79- vector2 ( x , y) ,
80- input . grid . sub_grid ( x.. ( x + block_size as i32 ) , y.. ( y + block_size as i32 ) ) ,
81- ) ;
111+ blocks. push ( Block {
112+ grid : input . grid . sub_grid ( x.. ( x + block_size ) , y.. ( y + block_size ) ) ,
113+ offset : vector2 ( x , y) ,
114+ } ) ;
82115 }
83116 }
84117 }
85118
86- let start_block = * blocks. keys ( ) . find ( |offset| offset. y ( ) == 0 ) . unwrap ( ) ;
119+ let blocks = blocks. try_into ( ) . expect ( "Expected 6 blocks as input" ) ;
120+ let block_jump_2d = calc_block_jumps_2d ( & blocks) ;
121+ let block_jump_3d = calc_block_jumps_3d ( & blocks) ;
87122
88- Input { commands : input. commands , block_size, blocks, start_offset : start_block }
123+ Input {
124+ commands : input. commands ,
125+ block_size,
126+ blocks : blocks,
127+ block_jump_2d,
128+ block_jump_3d,
129+ }
89130}
90131
91- impl Input {
92- fn next_block_2d (
93- & self ,
94- current_offset : & Vector < 2 , i32 > ,
95- direction : & Direction ,
96- ) -> ( Vector < 2 , i32 > , & Grid < FieldType > ) {
97- let step = direction. as_vec ( ) * self . block_size as i32 ;
98- let mut next_offset = * current_offset;
99- let modulus = 4 * self . block_size as i32 ;
100-
101- for _ in 0 ..5 {
102- next_offset = next_offset + step;
103- next_offset = vector2 (
104- ( next_offset. x ( ) + modulus) % modulus,
105- ( next_offset. y ( ) + modulus) % modulus,
106- ) ;
107- if let Some ( block) = self . blocks . get ( & next_offset) {
108- return ( next_offset, block) ;
132+ type BlockJumps = FxHashMap < ( usize , Direction ) , ( usize , Turn ) > ;
133+
134+ fn calc_block_jumps_2d ( blocks : & [ Block ; 6 ] ) -> BlockJumps {
135+ let mut jumps = FxHashMap :: default ( ) ;
136+ let block_size = blocks[ 0 ] . grid . width ( ) ;
137+
138+ for start_ix in 0 ..6 {
139+ let start_offset = blocks[ start_ix] . offset ;
140+ for direction in ALL_DIRECTIONS {
141+ let step = direction. as_vec ( ) * block_size;
142+ let modulus = 4 * block_size;
143+ let mut next_offset = start_offset;
144+
145+ for _ in 1 ..=4 {
146+ next_offset = ( next_offset + step + vector2 ( modulus, modulus) ) % modulus;
147+ if let Some ( ( next_ix, _) ) =
148+ blocks. iter ( ) . enumerate ( ) . find ( |( _, block) | block. offset == next_offset)
149+ {
150+ jumps. insert ( ( start_ix, direction) , ( next_ix, Turn :: None ) ) ;
151+ break ;
152+ }
109153 }
110154 }
155+ }
156+
157+ return jumps;
158+ }
159+
160+ fn calc_block_jumps_3d ( blocks : & [ Block ; 6 ] ) -> BlockJumps {
161+ let mut jumps = FxHashMap :: default ( ) ;
162+ let block_size = blocks[ 0 ] . grid . width ( ) ;
111163
112- panic ! ( "No block found" )
164+ if block_size == 4 {
165+ jumps. insert ( ( 0 , North ) , ( 1 , Turn :: Around ) ) ;
166+ jumps. insert ( ( 0 , East ) , ( 5 , Turn :: Around ) ) ;
167+ jumps. insert ( ( 0 , South ) , ( 3 , Turn :: None ) ) ;
168+ jumps. insert ( ( 0 , West ) , ( 2 , Turn :: Left ) ) ;
169+
170+ jumps. insert ( ( 1 , North ) , ( 0 , Turn :: Around ) ) ;
171+ jumps. insert ( ( 1 , East ) , ( 2 , Turn :: None ) ) ;
172+ jumps. insert ( ( 1 , South ) , ( 4 , Turn :: Around ) ) ;
173+ jumps. insert ( ( 1 , West ) , ( 5 , Turn :: Left ) ) ;
174+
175+ jumps. insert ( ( 2 , North ) , ( 0 , Turn :: Right ) ) ;
176+ jumps. insert ( ( 2 , East ) , ( 3 , Turn :: None ) ) ;
177+ jumps. insert ( ( 2 , South ) , ( 4 , Turn :: Left ) ) ;
178+ jumps. insert ( ( 2 , West ) , ( 1 , Turn :: None ) ) ;
179+
180+ jumps. insert ( ( 3 , North ) , ( 0 , Turn :: None ) ) ;
181+ jumps. insert ( ( 3 , East ) , ( 5 , Turn :: Right ) ) ;
182+ jumps. insert ( ( 3 , South ) , ( 4 , Turn :: None ) ) ;
183+ jumps. insert ( ( 3 , West ) , ( 2 , Turn :: None ) ) ;
184+
185+ jumps. insert ( ( 4 , North ) , ( 3 , Turn :: None ) ) ;
186+ jumps. insert ( ( 4 , East ) , ( 5 , Turn :: None ) ) ;
187+ jumps. insert ( ( 4 , South ) , ( 1 , Turn :: Around ) ) ;
188+ jumps. insert ( ( 4 , West ) , ( 2 , Turn :: Right ) ) ;
189+
190+ jumps. insert ( ( 5 , North ) , ( 3 , Turn :: Left ) ) ;
191+ jumps. insert ( ( 5 , East ) , ( 0 , Turn :: Around ) ) ;
192+ jumps. insert ( ( 5 , South ) , ( 1 , Turn :: Right ) ) ;
193+ jumps. insert ( ( 5 , West ) , ( 4 , Turn :: None ) ) ;
194+ } else {
195+ jumps. insert ( ( 0 , North ) , ( 5 , Turn :: Right ) ) ;
196+ jumps. insert ( ( 0 , East ) , ( 1 , Turn :: None ) ) ;
197+ jumps. insert ( ( 0 , South ) , ( 2 , Turn :: None ) ) ;
198+ jumps. insert ( ( 0 , West ) , ( 3 , Turn :: Around ) ) ;
199+
200+ jumps. insert ( ( 1 , North ) , ( 5 , Turn :: None ) ) ;
201+ jumps. insert ( ( 1 , East ) , ( 4 , Turn :: Around ) ) ;
202+ jumps. insert ( ( 1 , South ) , ( 2 , Turn :: Right ) ) ;
203+ jumps. insert ( ( 1 , West ) , ( 0 , Turn :: None ) ) ;
204+
205+ jumps. insert ( ( 2 , North ) , ( 0 , Turn :: None ) ) ;
206+ jumps. insert ( ( 2 , East ) , ( 1 , Turn :: Left ) ) ;
207+ jumps. insert ( ( 2 , South ) , ( 4 , Turn :: None ) ) ;
208+ jumps. insert ( ( 2 , West ) , ( 3 , Turn :: Left ) ) ;
209+
210+ jumps. insert ( ( 3 , North ) , ( 2 , Turn :: Right ) ) ;
211+ jumps. insert ( ( 3 , East ) , ( 4 , Turn :: None ) ) ;
212+ jumps. insert ( ( 3 , South ) , ( 5 , Turn :: None ) ) ;
213+ jumps. insert ( ( 3 , West ) , ( 0 , Turn :: Around ) ) ;
214+
215+ jumps. insert ( ( 4 , North ) , ( 2 , Turn :: None ) ) ;
216+ jumps. insert ( ( 4 , East ) , ( 1 , Turn :: Around ) ) ;
217+ jumps. insert ( ( 4 , South ) , ( 5 , Turn :: Right ) ) ;
218+ jumps. insert ( ( 4 , West ) , ( 3 , Turn :: None ) ) ;
219+
220+ jumps. insert ( ( 5 , North ) , ( 3 , Turn :: None ) ) ;
221+ jumps. insert ( ( 5 , East ) , ( 4 , Turn :: Left ) ) ;
222+ jumps. insert ( ( 5 , South ) , ( 1 , Turn :: None ) ) ;
223+ jumps. insert ( ( 5 , West ) , ( 0 , Turn :: Left ) ) ;
113224 }
225+
226+ return jumps;
114227}
115228
116- fn calculate_part1 ( input : & Input ) -> i32 {
117- let mut current_offset = input. start_offset ;
118- let mut current_block = input. blocks . get ( & current_offset) . unwrap ( ) ;
119- let mut person = Person { position : point2 ( 0 , 0 ) , direction : East } ;
229+ fn handle_command (
230+ input : & Input ,
231+ block_jump : & BlockJumps ,
232+ command : & Command ,
233+ index : & usize ,
234+ person : & Person ,
235+ ) -> ( usize , Person ) {
236+ let mut person = person. clone ( ) ;
237+ let mut index = * index;
120238
121- for command in & input. commands {
122- match command {
123- Command :: Forward ( steps) => {
124- for _ in 0 ..* steps {
125- let mut next_position = person. step ( ) ;
126- match current_block. get ( next_position) {
127- Some ( Empty ) => person. position = next_position,
128- Some ( Wall ) => break ,
129- Some ( Outside ) => panic ! ( "Outside a block should not be possible" ) ,
130- None => {
131- let ( next_offset, next_block) =
132- input. next_block_2d ( & current_offset, & person. direction ) ;
133- next_position = next_position
134- + ( person. direction . neg ( ) . as_vec ( ) * ( input. block_size as i32 ) ) ;
135-
136- if next_block. get ( next_position) == Some ( & Wall ) {
137- break ;
138- }
139-
140- current_offset = next_offset;
141- current_block = next_block;
142- person. position = next_position;
239+ match command {
240+ Command :: Forward ( steps) => {
241+ for _ in 0 ..* steps {
242+ let mut next_person = person. step ( ) ;
243+ match input. blocks [ index] . grid . get ( next_person. pos ) {
244+ Some ( Empty ) => person = next_person,
245+ Some ( Wall ) => break ,
246+ Some ( Outside ) => panic ! ( "Outside a block should not be possible" ) ,
247+ None => {
248+ let ( next_ix, turn) = block_jump[ & ( index, person. dir ) ] ;
249+ next_person = turn. apply ( & next_person, input. block_size ) ;
250+
251+ if input. blocks [ next_ix] . grid . get ( next_person. pos ) == Some ( & Wall ) {
252+ break ;
143253 }
254+
255+ ( index, person) = ( next_ix, next_person) ;
144256 }
145257 }
146258 }
147- Command :: Left => person. direction = person. direction . turn_left ( ) ,
148- Command :: Right => person. direction = person. direction . turn_right ( ) ,
149259 }
260+ Command :: Left => person. dir = person. dir . turn_left ( ) ,
261+ Command :: Right => person. dir = person. dir . turn_right ( ) ,
262+ }
263+
264+ ( index, person)
265+ }
266+
267+ fn calculate ( input : & Input , block_jump : & BlockJumps ) -> i32 {
268+ let mut index = 0 ;
269+ let mut person = Person { pos : point2 ( 0 , 0 ) , dir : East } ;
270+
271+ for command in & input. commands {
272+ ( index, person) = handle_command ( input, block_jump, command, & mut index, & mut person) ;
150273 }
151274
152- person. position = person. position + current_offset ;
275+ person. pos = person. pos + input . blocks [ index ] . offset ;
153276 person. score ( )
154277}
155278
156- fn calculate_part2 ( _input : & Input ) -> i32 { todo ! ( ) }
279+ fn calculate_part1 ( input : & Input ) -> i32 { calculate ( input, & input. block_jump_2d ) }
280+
281+ fn calculate_part2 ( input : & Input ) -> i32 { calculate ( input, & input. block_jump_3d ) }
157282
158283day_main ! ( preprocess => calculate_part1, calculate_part2 ) ;
159284
@@ -166,8 +291,8 @@ mod tests {
166291 use nom:: Parser ;
167292 use nom_parse_trait:: ParseFrom ;
168293
169- day_test ! ( 22 , example => 6032 /* , 5031 */ ; preprocess ) ;
170- day_test ! ( 22 => 197160 ; preprocess ) ;
294+ day_test ! ( 22 , example => 6032 , 5031 ; preprocess ) ;
295+ day_test ! ( 22 => 197160 , 145065 ; preprocess ) ;
171296
172297 #[ test]
173298 fn test_command_parsing ( ) {
0 commit comments