22//! https://adventofcode.com/2024/day/10
33
44use std:: { fs, io} ;
5+ use std:: collections:: { BTreeMap , BTreeSet } ;
56use std:: path:: Path ;
7+ use lib:: vector:: Vector ;
8+
9+ type Vec2 = Vector < i32 , 2 > ;
10+
11+ const UP : Vec2 = Vec2 :: new ( [ 0 , -1 ] ) ;
12+ const DOWN : Vec2 = Vec2 :: new ( [ 0 , 1 ] ) ;
13+ const LEFT : Vec2 = Vec2 :: new ( [ -1 , 0 ] ) ;
14+ const RIGHT : Vec2 = Vec2 :: new ( [ 1 , 0 ] ) ;
615
716fn main ( ) {
8- 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");
17+ // let input = Input::from_file(format!("{}/input.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
18+ let input = Input :: from_file ( format ! ( "{}/example5 .txt" , env!( "CARGO_MANIFEST_DIR" ) ) ) . expect ( "failed to read input" ) ;
1019 println ! ( "{input:?}" ) ;
1120
1221 // Part 1
@@ -17,24 +26,107 @@ fn main() {
1726}
1827
1928fn part1 ( input : & Input ) -> usize {
20- 0
29+ let trailheads: BTreeSet < Vec2 > = input. heights . iter ( )
30+ . filter_map ( |( & p, & h) | ( h == 0 ) . then_some ( p) ) . collect ( ) ;
31+
32+ trailheads. into_iter ( ) . map ( |head| score ( head, & input. heights ) ) . sum ( )
33+ }
34+
35+ fn score ( trailhead : Vec2 , heights : & BTreeMap < Vec2 , u8 > ) -> usize {
36+ let mut score = 0 ;
37+
38+ let mut visited: BTreeSet < Vec2 > = [ trailhead] . into_iter ( ) . collect ( ) ;
39+ let mut edge = vec ! [ trailhead] ;
40+
41+ while let Some ( pos) = edge. pop ( ) {
42+ let height = heights[ & pos] ;
43+
44+ if height == 9 {
45+ score += 1 ;
46+ continue ;
47+ }
48+
49+ for dir in [ UP , DOWN , LEFT , RIGHT ] {
50+ let next_pos = pos + dir;
51+ if visited. contains ( & next_pos) {
52+ continue ;
53+ }
54+
55+ if heights. get ( & next_pos) . copied ( ) == Some ( height + 1 ) {
56+ visited. insert ( next_pos) ;
57+ edge. push ( next_pos) ;
58+ }
59+ }
60+ }
61+
62+ score
2163}
2264
65+
2366fn part2 ( input : & Input ) -> usize {
24- 0
67+ let trailheads: BTreeSet < Vec2 > = input. heights . iter ( )
68+ . filter_map ( |( & p, & h) | ( h == 0 ) . then_some ( p) ) . collect ( ) ;
69+
70+ trailheads. into_iter ( ) . map ( |head| rating ( head, & input. heights ) ) . sum ( )
71+ }
72+
73+ fn rating ( trailhead : Vec2 , heights : & BTreeMap < Vec2 , u8 > ) -> usize {
74+ let mut rating = 0 ;
75+
76+ let mut visited: BTreeSet < Vec < Vec2 > > = [ vec ! [ trailhead] ] . into_iter ( ) . collect ( ) ;
77+ let mut edge = vec ! [ vec![ trailhead] ] ;
78+
79+ while let Some ( trail) = edge. pop ( ) {
80+ let pos = * trail. last ( ) . unwrap ( ) ;
81+ let height = heights[ & pos] ;
82+ if height == 9 {
83+ rating += 1 ;
84+ continue ;
85+ }
86+
87+ for dir in [ UP , DOWN , LEFT , RIGHT ] {
88+ let next_pos = pos + dir;
89+ let new_trail: Vec < _ > = trail. iter ( ) . copied ( ) . chain ( [ next_pos] ) . collect ( ) ;
90+
91+ if visited. contains ( & new_trail) {
92+ continue ;
93+ }
94+
95+ if heights. get ( & next_pos) . copied ( ) == Some ( height + 1 ) {
96+ visited. insert ( new_trail. clone ( ) ) ;
97+ edge. push ( new_trail) ;
98+ }
99+ }
100+ }
101+
102+ rating
25103}
26104
27105#[ derive( Debug , Clone ) ]
28106struct Input {
29- values : Vec < String > ,
107+ heights : BTreeMap < Vec2 , u8 > ,
30108}
31109
32110impl Input {
33111 fn from_file ( path : impl AsRef < Path > ) -> io:: Result < Self > {
34112 let input = fs:: read_to_string ( path) ?;
35- let values = input. lines ( ) . map ( str:: to_string) . collect ( ) ;
36113
37- Ok ( Self { values } )
114+ let mut heights = BTreeMap :: new ( ) ;
115+
116+ for ( x, line) in input. lines ( ) . enumerate ( ) {
117+ for ( y, c) in line. trim ( ) . chars ( ) . enumerate ( ) {
118+ if c == '.' {
119+ continue ;
120+ }
121+
122+ let pos = Vec2 :: new ( [ x as i32 , y as i32 ] ) ;
123+ let height = c. to_digit ( 10 ) . unwrap ( ) as u8 ;
124+
125+ heights. insert ( pos, height) ;
126+ }
127+ }
128+
129+ Ok ( Self { heights } )
38130 }
39131}
40132
@@ -44,29 +136,29 @@ mod test {
44136
45137 #[ test]
46138 fn test_part1 ( ) {
47- let input = Input :: from_file ( "example1 .txt" ) . unwrap ( ) ;
139+ let input = Input :: from_file ( "example5 .txt" ) . unwrap ( ) ;
48140
49- assert_eq ! ( part1( & input) , 0 ) ;
141+ assert_eq ! ( part1( & input) , 36 ) ;
50142 }
51143
52144 #[ test]
53145 fn test_part1_solution ( ) {
54146 let input = Input :: from_file ( "input.txt" ) . unwrap ( ) ;
55147
56- assert_eq ! ( part1( & input) , 0 ) ;
148+ assert_eq ! ( part1( & input) , 776 ) ;
57149 }
58150
59151 #[ test]
60152 fn test_part2 ( ) {
61- let input = Input :: from_file ( "example1 .txt" ) . unwrap ( ) ;
153+ let input = Input :: from_file ( "example5 .txt" ) . unwrap ( ) ;
62154
63- assert_eq ! ( part2( & input) , 0 ) ;
155+ assert_eq ! ( part2( & input) , 81 ) ;
64156 }
65157
66158 #[ test]
67159 fn test_part2_solution ( ) {
68160 let input = Input :: from_file ( "input.txt" ) . unwrap ( ) ;
69161
70- assert_eq ! ( part2( & input) , 0 ) ;
162+ assert_eq ! ( part2( & input) , 1657 ) ;
71163 }
72164}
0 commit comments