22//! https://adventofcode.com/2024/day/16
33
44use std:: { fs, io} ;
5+ use std:: collections:: { BTreeMap , BTreeSet , BinaryHeap } ;
56use std:: path:: Path ;
7+ use std:: str:: FromStr ;
8+ use lib:: grid:: { Grid , Pos } ;
9+
10+ const WALL : char = '#' ;
11+ const START : char = 'S' ;
12+ const END : char = 'E' ;
613
714fn main ( ) {
815 let input = Input :: from_file ( format ! ( "{}/input.txt" , env!( "CARGO_MANIFEST_DIR" ) ) ) . expect ( "failed to read input" ) ;
9- //let input = Input::from_file(format!("{}/example1 .txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
10- println ! ( "{input:?}" ) ;
16+ //let input = Input::from_file(format!("{}/example2 .txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
17+ println ! ( "{}" , input . grid ) ;
1118
1219 // Part 1
1320 println ! ( "Part 1: {}" , part1( & input) ) ;
@@ -16,25 +23,146 @@ fn main() {
1623 println ! ( "Part 2: {}" , part2( & input) ) ;
1724}
1825
26+ fn find_start_end ( grid : & Grid ) -> ( Pos , Pos ) {
27+ let mut start = Pos :: default ( ) ;
28+ let mut end = Pos :: default ( ) ;
29+
30+ for pos in grid. positions ( ) {
31+ match grid[ pos] {
32+ START => start = pos,
33+ END => end = pos,
34+ _ => ( ) ,
35+ }
36+ }
37+
38+ ( start, end)
39+ }
40+
41+ #[ derive( Debug , Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
42+ enum Direction {
43+ East ,
44+ South ,
45+ West ,
46+ North ,
47+ }
48+
49+ impl Direction {
50+ pub fn clockwise ( self ) -> Direction {
51+ match self {
52+ Direction :: East => Direction :: South ,
53+ Direction :: South => Direction :: West ,
54+ Direction :: West => Direction :: North ,
55+ Direction :: North => Direction :: East ,
56+ }
57+ }
58+
59+ pub fn anticlockwise ( self ) -> Direction {
60+ match self {
61+ Direction :: East => Direction :: North ,
62+ Direction :: North => Direction :: West ,
63+ Direction :: West => Direction :: South ,
64+ Direction :: South => Direction :: East ,
65+ }
66+ }
67+ }
68+
69+ impl From < Direction > for Pos {
70+ fn from ( direction : Direction ) -> Self {
71+ match direction {
72+ Direction :: East => Pos :: new ( [ 1 , 0 ] ) ,
73+ Direction :: South => Pos :: new ( [ 0 , 1 ] ) ,
74+ Direction :: West => Pos :: new ( [ -1 , 0 ] ) ,
75+ Direction :: North => Pos :: new ( [ 0 , -1 ] ) ,
76+ }
77+ }
78+ }
79+
1980fn part1 ( input : & Input ) -> usize {
81+ let ( start, end) = find_start_end ( & input. grid ) ;
82+
83+ let mut best: BTreeMap < ( Pos , Direction ) , usize > = [ ( ( start, Direction :: East ) , 0 ) ] . into_iter ( ) . collect ( ) ;
84+ let mut edge: BinaryHeap < ( usize , ( Pos , Direction ) , BTreeSet < Pos > ) > = [ ( 0 , ( start, Direction :: East ) , BTreeSet :: new ( ) ) ] . into_iter ( ) . collect ( ) ;
85+
86+ while let Some ( ( _, cur, path) ) = edge. pop ( ) {
87+ if cur. 0 == end {
88+ return best[ & cur] ;
89+ }
90+
91+ for next in [ ( cur. 0 + cur. 1 . into ( ) , cur. 1 ) , ( cur. 0 , cur. 1 . clockwise ( ) ) , ( cur. 0 , cur. 1 . anticlockwise ( ) ) ] {
92+ if input. grid [ next. 0 ] == WALL {
93+ continue ;
94+ }
95+
96+ let new_score = best[ & cur] + ( next. 1 != cur. 1 ) . then_some ( 1000 ) . unwrap_or ( 1 ) ;
97+ if best. contains_key ( & next) && best[ & next] < new_score {
98+ continue ;
99+ }
100+
101+ let mut new_path: BTreeSet < Pos > = path. clone ( ) ;
102+ new_path. insert ( next. 0 ) ;
103+
104+ best. insert ( next, new_score) ;
105+ edge. push ( ( usize:: MAX - new_score, next, new_path) ) ;
106+ }
107+ }
108+
20109 0
21110}
22111
23112fn part2 ( input : & Input ) -> usize {
24- 0
113+ let ( start, end) = find_start_end ( & input. grid ) ;
114+
115+ let mut best: BTreeMap < ( Pos , Direction ) , usize > = [ ( ( start, Direction :: East ) , 0 ) ] . into_iter ( ) . collect ( ) ;
116+ let start_path: BTreeSet < Pos > = [ start] . into_iter ( ) . collect ( ) ;
117+ let mut edge: BinaryHeap < ( usize , ( Pos , Direction ) , BTreeSet < Pos > ) > = [ ( 0 , ( start, Direction :: East ) , start_path) ] . into_iter ( ) . collect ( ) ;
118+
119+ let mut best_score = None ;
120+ let mut all_path = BTreeSet :: new ( ) ;
121+
122+ while let Some ( ( _, cur, path) ) = edge. pop ( ) {
123+ if cur. 0 == end {
124+ all_path. extend ( path. iter ( ) . copied ( ) ) ;
125+
126+ if let Some ( best_score) = best_score {
127+ if best[ & cur] > best_score {
128+ break ;
129+ }
130+ }
131+ best_score = Some ( best[ & cur] ) ;
132+ }
133+
134+ for next in [ ( cur. 0 + cur. 1 . into ( ) , cur. 1 ) , ( cur. 0 , cur. 1 . clockwise ( ) ) , ( cur. 0 , cur. 1 . anticlockwise ( ) ) ] {
135+ if input. grid [ next. 0 ] == WALL {
136+ continue ;
137+ }
138+
139+ let new_score = best[ & cur] + ( next. 1 != cur. 1 ) . then_some ( 1000 ) . unwrap_or ( 1 ) ;
140+ if best. contains_key ( & next) && best[ & next] < new_score {
141+ continue ;
142+ }
143+
144+ let mut new_path: BTreeSet < Pos > = path. clone ( ) ;
145+ new_path. insert ( next. 0 ) ;
146+
147+ best. insert ( next, new_score) ;
148+ edge. push ( ( usize:: MAX - new_score, next, new_path) ) ;
149+ }
150+ }
151+
152+ all_path. len ( )
25153}
26154
27155#[ derive( Debug , Clone ) ]
28156struct Input {
29- values : Vec < String > ,
157+ pub grid : Grid ,
30158}
31159
32160impl Input {
33161 fn from_file ( path : impl AsRef < Path > ) -> io:: Result < Self > {
34162 let input = fs:: read_to_string ( path) ?;
35- let values = input . lines ( ) . map ( str :: to_string ) . collect ( ) ;
163+ let grid = Grid :: from_str ( & input ) ? ;
36164
37- Ok ( Self { values } )
165+ Ok ( Self { grid } )
38166 }
39167}
40168
@@ -43,30 +171,44 @@ mod test {
43171 use super :: * ;
44172
45173 #[ test]
46- fn test_part1 ( ) {
174+ fn test_part1_example1 ( ) {
47175 let input = Input :: from_file ( "example1.txt" ) . unwrap ( ) ;
48176
49- assert_eq ! ( part1( & input) , 0 ) ;
177+ assert_eq ! ( part1( & input) , 7036 ) ;
178+ }
179+
180+ #[ test]
181+ fn test_part1_example2 ( ) {
182+ let input = Input :: from_file ( "example2.txt" ) . unwrap ( ) ;
183+
184+ assert_eq ! ( part1( & input) , 11048 ) ;
50185 }
51186
52187 #[ test]
53188 fn test_part1_solution ( ) {
54189 let input = Input :: from_file ( "input.txt" ) . unwrap ( ) ;
55190
56- assert_eq ! ( part1( & input) , 0 ) ;
191+ assert_eq ! ( part1( & input) , 102460 ) ;
57192 }
58193
59194 #[ test]
60- fn test_part2 ( ) {
195+ fn test_part2_example1 ( ) {
61196 let input = Input :: from_file ( "example1.txt" ) . unwrap ( ) ;
62197
63- assert_eq ! ( part2( & input) , 0 ) ;
198+ assert_eq ! ( part2( & input) , 45 ) ;
199+ }
200+
201+ #[ test]
202+ fn test_part2_example2 ( ) {
203+ let input = Input :: from_file ( "example2.txt" ) . unwrap ( ) ;
204+
205+ assert_eq ! ( part2( & input) , 64 ) ;
64206 }
65207
66208 #[ test]
67209 fn test_part2_solution ( ) {
68210 let input = Input :: from_file ( "input.txt" ) . unwrap ( ) ;
69211
70- assert_eq ! ( part2( & input) , 0 ) ;
212+ assert_eq ! ( part2( & input) , 527 ) ;
71213 }
72214}
0 commit comments