@@ -5,11 +5,13 @@ use utils::prelude::*;
55/// Finding shortcuts phasing through walls in a maze.
66#[ derive( Clone , Debug ) ]
77pub struct Day20 {
8- grid : Vec < u8 > ,
9- distances : Vec < u32 > ,
8+ distances : Vec < Distance > ,
109 cols : usize ,
1110}
1211
12+ // Depending on AVX support, u16 or u32 can be faster
13+ type Distance = u16 ;
14+
1315impl Day20 {
1416 pub fn new ( input : & str , _: InputType ) -> Result < Self , InputError > {
1517 let ( _, cols, mut grid) = grid:: from_str_padded ( input, 20 , b'#' , |b| match b {
@@ -35,58 +37,46 @@ impl Day20 {
3537 }
3638 grid[ end] = b'.' ;
3739
38- let mut distances = vec ! [ u32 :: MAX ; grid. len( ) ] ;
40+ let mut distances = vec ! [ Distance :: MAX ; grid. len( ) ] ;
3941 distances[ start] = 0 ;
4042 let mut queue = VecDeque :: new ( ) ;
41- queue. push_back ( ( start, 0 ) ) ;
43+ queue. push_back ( ( start, 0 as Distance ) ) ;
4244 while let Some ( ( index, distance) ) = queue. pop_front ( ) {
4345 if index == end {
4446 break ;
4547 }
4648
49+ let Some ( next_distance) = distance. checked_add ( 1 ) else {
50+ return Err ( InputError :: new ( input, 0 , "path too long" ) ) ;
51+ } ;
52+
4753 for offset in [ 1 , cols as isize , -1 , -( cols as isize ) ] {
4854 let next = index. wrapping_add_signed ( offset) ;
49- if grid[ next] == b'.' && distances[ next] == u32 :: MAX {
50- distances[ next] = distance + 1 ;
51- queue. push_back ( ( next, distance + 1 ) ) ;
55+ if grid[ next] == b'.' && distances[ next] == Distance :: MAX {
56+ distances[ next] = next_distance ;
57+ queue. push_back ( ( next, next_distance ) ) ;
5258 }
5359 }
5460 }
5561
56- Ok ( Self {
57- grid,
58- distances,
59- cols,
60- } )
62+ Ok ( Self { distances, cols } )
6163 }
6264
6365 #[ must_use]
6466 pub fn part1 ( & self ) -> u32 {
6567 let mut cheats = 0 ;
66- for index in ( self . cols * 20 ) ..self . grid . len ( ) - ( self . cols * 20 ) {
67- let this_distance = self . distances [ index] ;
68- if this_distance == u32:: MAX {
69- continue ;
70- }
71-
72- for offset in [ 1 , self . cols as isize , -1 , -( self . cols as isize ) ] {
73- if self . grid [ index. wrapping_add_signed ( offset) ] != b'#' {
74- continue ;
75- }
76-
77- let Some ( next) = index. checked_add_signed ( offset * 2 ) else {
78- continue ;
79- } ;
80- let Some ( & target_distance) = self . distances . get ( next) else {
81- continue ;
82- } ;
83- if target_distance == u32:: MAX || target_distance < this_distance + 2 {
84- continue ;
85- }
86- let diff = target_distance - this_distance - 2 ;
87- if diff >= 100 {
88- cheats += 1 ;
89- }
68+ for offset in [ 2 , self . cols as isize * 2 , -2 , self . cols as isize * -2 ] {
69+ for ( index, target) in ( self . cols * 20 ..self . distances . len ( ) - ( self . cols * 20 ) )
70+ . zip ( ( self . cols * 20 ) . wrapping_add_signed ( offset) ..)
71+ {
72+ let this_distance = self . distances [ index] ;
73+ let target_distance = self . distances [ target] ;
74+ cheats += u32:: from (
75+ ( target_distance != Distance :: MAX )
76+ & ( this_distance != Distance :: MAX )
77+ & ( target_distance > this_distance. wrapping_add ( 2 ) )
78+ & ( target_distance. wrapping_sub ( this_distance) . wrapping_sub ( 2 ) >= 100 ) ,
79+ ) ;
9080 }
9181 }
9282 cheats
@@ -95,36 +85,32 @@ impl Day20 {
9585 #[ must_use]
9686 pub fn part2 ( & self ) -> u32 {
9787 let mut cheats = 0 ;
98- for index in ( self . cols * 20 ) ..self . grid . len ( ) - ( self . cols * 20 ) {
99- let this_distance = self . distances [ index] ;
100- if this_distance == u32:: MAX {
101- continue ;
102- }
103-
104- for x_offset in -20isize ..=20 {
105- let y_limit = 20 - x_offset. abs ( ) ;
106- for y_offset in -y_limit..=y_limit {
107- let cheat_length = ( x_offset. unsigned_abs ( ) + y_offset. unsigned_abs ( ) ) as u32 ;
108- if cheat_length == 0 {
109- continue ;
110- }
111-
112- let offset = y_offset * ( self . cols as isize ) + x_offset;
113- let next = index. wrapping_add_signed ( offset) ;
114- let target_distance = self . distances [ next] ;
115- if target_distance == u32:: MAX || target_distance < this_distance + cheat_length
116- {
117- continue ;
118- }
88+ for x_offset in -20isize ..=20 {
89+ let y_limit = 20 - x_offset. abs ( ) ;
90+ for y_offset in -y_limit..=y_limit {
91+ let cheat_length = ( x_offset. unsigned_abs ( ) + y_offset. unsigned_abs ( ) ) as Distance ;
92+ if cheat_length == 0 {
93+ continue ;
94+ }
11995
120- let diff = target_distance - this_distance - cheat_length;
121- if diff >= 100 {
122- cheats += 1 ;
123- }
96+ let offset = y_offset * ( self . cols as isize ) + x_offset;
97+ for ( index, target) in ( self . cols * 20 ..self . distances . len ( ) - ( self . cols * 20 ) )
98+ . zip ( ( self . cols * 20 ) . wrapping_add_signed ( offset) ..)
99+ {
100+ let this_distance = self . distances [ index] ;
101+ let target_distance = self . distances [ target] ;
102+ cheats += u32:: from (
103+ ( target_distance != Distance :: MAX )
104+ & ( this_distance != Distance :: MAX )
105+ & ( target_distance > this_distance. wrapping_add ( cheat_length) )
106+ & ( target_distance
107+ . wrapping_sub ( this_distance)
108+ . wrapping_sub ( cheat_length)
109+ >= 100 ) ,
110+ ) ;
124111 }
125112 }
126113 }
127-
128114 cheats
129115 }
130116}
0 commit comments