@@ -6,85 +6,96 @@ use utils::prelude::*;
66/// Finding when the path through a grid is blocked.
77#[ derive( Clone , Debug ) ]
88pub struct Day18 {
9- blocked_at : [ [ u16 ; WIDTH ] ; WIDTH ] ,
10- fallen : u16 ,
9+ blocked_at : Vec < u16 > ,
10+ size : usize ,
11+ start_idx : usize ,
12+ end_idx : usize ,
13+ part1_fallen : u16 ,
14+ total_fallen : u16 ,
1115}
1216
13- const MAX_COORD : usize = 70 ;
14- const WIDTH : usize = MAX_COORD + 1 ;
15-
1617impl Day18 {
17- pub fn new ( input : & str , _: InputType ) -> Result < Self , InputError > {
18- let mut blocked_at = [ [ u16:: MAX ; WIDTH ] ; WIDTH ] ;
18+ pub fn new ( input : & str , input_type : InputType ) -> Result < Self , InputError > {
19+ let ( max_coord, part1_fallen) = match input_type {
20+ InputType :: Example => ( 6 , 12 ) ,
21+ InputType :: Real => ( 70 , 1024 ) ,
22+ } ;
23+ // +1 as max_coord is inclusive, +2 for padding on each side
24+ let size = max_coord + 3 ;
25+
26+ let mut blocked_at = vec ! [ u16 :: MAX ; size * size] ;
27+ for i in 0 ..size {
28+ blocked_at[ i] = 0 ; // Top
29+ blocked_at[ ( size - 1 ) * size + i] = 0 ; // Bottom
30+ blocked_at[ i * size] = 0 ; // Left
31+ blocked_at[ i * size + ( size - 1 ) ] = 0 ; // Right
32+ }
33+
1934 let mut fallen = 0 ;
20- for item in parser:: number_range ( 0 ..=MAX_COORD )
21- . then ( parser:: number_range ( 0 ..=MAX_COORD ) . with_prefix ( b',' ) )
35+ for item in parser:: number_range ( 0 ..=max_coord )
36+ . then ( parser:: number_range ( 0 ..=max_coord ) . with_prefix ( b',' ) )
2237 . map ( |( x, y) | Point2D { x, y } )
2338 . with_suffix ( parser:: eol ( ) )
2439 . parse_iterator ( input)
2540 {
2641 let pos = item?;
27- if blocked_at[ pos. x ] [ pos. y ] == u16:: MAX {
28- blocked_at[ pos. x ] [ pos. y ] = fallen;
42+ if blocked_at[ ( pos. y + 1 ) * size + ( pos. x + 1 ) ] == u16:: MAX {
43+ blocked_at[ ( pos. y + 1 ) * size + ( pos. x + 1 ) ] = fallen;
2944 fallen += 1 ;
3045 } else {
3146 return Err ( InputError :: new ( input, 0 , "duplicate position in input" ) ) ;
3247 }
3348 }
3449
35- Ok ( Self { blocked_at, fallen } )
50+ Ok ( Self {
51+ blocked_at,
52+ size,
53+ start_idx : size + 1 ,
54+ end_idx : ( max_coord + 1 ) * size + ( max_coord + 1 ) ,
55+ part1_fallen,
56+ total_fallen : fallen,
57+ } )
3658 }
3759
3860 #[ must_use]
3961 pub fn part1 ( & self ) -> u32 {
40- self . minimum_steps ( 1024 ) . expect ( "no solution found" )
41- }
42-
43- fn minimum_steps ( & self , fallen : u16 ) -> Option < u32 > {
44- let mut blocked_at = self . blocked_at ;
62+ let mut blocked_at = self . blocked_at . clone ( ) ;
4563 let mut queue = VecDeque :: new ( ) ;
46- queue. push_back ( ( Point2D :: new ( 0 , 0 ) , 0 ) ) ;
64+ queue. push_back ( ( self . start_idx , 0 ) ) ;
4765
4866 while let Some ( ( pos, steps) ) = queue. pop_front ( ) {
49- if pos == Point2D :: new ( MAX_COORD , MAX_COORD ) {
50- return Some ( steps) ;
67+ if pos == self . end_idx {
68+ return steps;
5169 }
5270
53- if pos. x > 0 && blocked_at[ pos. x - 1 ] [ pos. y ] >= fallen {
54- queue. push_back ( ( Point2D :: new ( pos. x - 1 , pos. y ) , steps + 1 ) ) ;
55- blocked_at[ pos. x - 1 ] [ pos. y ] = 0 ;
56- }
57- if pos. x < MAX_COORD && blocked_at[ pos. x + 1 ] [ pos. y ] >= fallen {
58- queue. push_back ( ( Point2D :: new ( pos. x + 1 , pos. y ) , steps + 1 ) ) ;
59- blocked_at[ pos. x + 1 ] [ pos. y ] = 0 ;
60- }
61- if pos. y > 0 && blocked_at[ pos. x ] [ pos. y - 1 ] >= fallen {
62- queue. push_back ( ( Point2D :: new ( pos. x , pos. y - 1 ) , steps + 1 ) ) ;
63- blocked_at[ pos. x ] [ pos. y - 1 ] = 0 ;
64- }
65- if pos. y < MAX_COORD && blocked_at[ pos. x ] [ pos. y + 1 ] >= fallen {
66- queue. push_back ( ( Point2D :: new ( pos. x , pos. y + 1 ) , steps + 1 ) ) ;
67- blocked_at[ pos. x ] [ pos. y + 1 ] = 0 ;
71+ for next in [ pos - self . size , pos + 1 , pos + self . size , pos - 1 ] {
72+ if blocked_at[ next] >= self . part1_fallen {
73+ queue. push_back ( ( next, steps + 1 ) ) ;
74+ blocked_at[ next] = 0 ;
75+ }
6876 }
6977 }
7078
71- None
79+ panic ! ( "no solution found" )
7280 }
7381
7482 #[ must_use]
7583 pub fn part2 ( & self ) -> String {
76- let mut blocked_at = self . blocked_at ;
77- let mut reachable = vec ! [ None ; self . fallen as usize ] ;
78- let mut fallen = self . fallen ;
79- let mut next = Point2D :: new ( 0 , 0 ) ;
84+ let mut blocked_at = self . blocked_at . clone ( ) ;
85+ let mut reachable = vec ! [ None ; self . total_fallen as usize ] ;
86+ let mut fallen = self . total_fallen ;
87+ let mut next = self . start_idx ;
8088 loop {
8189 // Recursively flood fill the reachable grid spaces, tracking which grid spaces would
8290 // be reachable at a lower number of fallen bytes.
83- if Self :: fill ( next, fallen, & mut blocked_at, & mut reachable) . is_break ( ) {
84- if next == Point2D :: new ( 0 , 0 ) {
91+ if self
92+ . fill ( next, fallen, & mut blocked_at, & mut reachable)
93+ . is_break ( )
94+ {
95+ if fallen == self . total_fallen {
8596 panic ! ( "path is never blocked" ) ;
8697 }
87- return format ! ( "{},{}" , next. x , next. y ) ;
98+ return format ! ( "{},{}" , ( next % self . size ) - 1 , ( next / self . size ) - 1 ) ;
8899 }
89100
90101 // No path to the end. Decrease fallen to the value blocking the highest reachable grid
@@ -98,31 +109,29 @@ impl Day18 {
98109 }
99110
100111 fn fill (
101- pos : Point2D < usize > ,
112+ & self ,
113+ pos : usize ,
102114 fallen : u16 ,
103- blocked_at : & mut [ [ u16 ; 71 ] ; 71 ] ,
104- reachable : & mut [ Option < Point2D < usize > > ] ,
115+ blocked_at : & mut [ u16 ] ,
116+ reachable : & mut [ Option < usize > ] ,
105117 ) -> ControlFlow < ( ) > {
106- if pos == Point2D :: new ( MAX_COORD , MAX_COORD ) {
118+ if pos == self . end_idx {
107119 return ControlFlow :: Break ( ( ) ) ;
108120 }
109121
110- for dir in [ Point2D :: UP , Point2D :: RIGHT , Point2D :: DOWN , Point2D :: LEFT ] {
111- let next = pos. wrapping_add_signed ( dir) ;
112- if next. x > MAX_COORD || next. y > MAX_COORD {
113- continue ;
114- }
115-
116- if blocked_at[ next. x ] [ next. y ] >= fallen {
117- blocked_at[ next. x ] [ next. y ] = 0 ;
118- Self :: fill ( next, fallen, blocked_at, reachable) ?;
119- } else if blocked_at[ next. x ] [ next. y ] > 0 {
120- reachable[ blocked_at[ next. x ] [ next. y ] as usize ] = Some ( next) ;
122+ for next in [ pos - self . size , pos + 1 , pos + self . size , pos - 1 ] {
123+ if blocked_at[ next] >= fallen {
124+ blocked_at[ next] = 0 ;
125+ self . fill ( next, fallen, blocked_at, reachable) ?;
126+ } else if blocked_at[ next] > 0 {
127+ reachable[ blocked_at[ next] as usize ] = Some ( next) ;
121128 }
122129 }
123130
124131 ControlFlow :: Continue ( ( ) )
125132 }
126133}
127134
128- examples ! ( Day18 -> ( u32 , & ' static str ) [ ] ) ;
135+ examples ! ( Day18 -> ( u32 , & ' static str ) [
136+ { file: "day18_example0.txt" , part1: 22 , part2: "6,1" } ,
137+ ] ) ;
0 commit comments