22//! https://adventofcode.com/2024/day/12
33
44use std:: { fs, io} ;
5+ use std:: collections:: { BTreeMap , BTreeSet } ;
6+ use std:: ops:: Range ;
57use std:: path:: Path ;
8+ use lib:: vector:: Vector ;
69
710fn main ( ) {
811 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:?}" ) ;
12+ //let input = Input::from_file(format!("{}/example2 .txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
13+ // println!("{input:?}");
1114
1215 // Part 1
1316 println ! ( "Part 1: {}" , part1( & input) ) ;
@@ -16,25 +19,162 @@ fn main() {
1619 println ! ( "Part 2: {}" , part2( & input) ) ;
1720}
1821
22+ const UP : Vec2 = Vec2 :: new ( [ 0 , -1 ] ) ;
23+ const DOWN : Vec2 = Vec2 :: new ( [ 0 , 1 ] ) ;
24+ const LEFT : Vec2 = Vec2 :: new ( [ -1 , 0 ] ) ;
25+ const RIGHT : Vec2 = Vec2 :: new ( [ 1 , 0 ] ) ;
26+
1927fn part1 ( input : & Input ) -> usize {
20- 0
28+ let mut regions = Vec :: new ( ) ;
29+
30+ let mut visitied = BTreeSet :: new ( ) ;
31+ for y in input. bounds . 1 . clone ( ) {
32+ for x in input. bounds . 0 . clone ( ) {
33+ let pos = Vec2 :: new ( [ x, y] ) ;
34+ if visitied. contains ( & pos) {
35+ continue ;
36+ }
37+
38+ let plant = input. map [ & pos] ;
39+ let mut perimenter = 0 ;
40+ let mut area = 0 ;
41+
42+ let mut edge = vec ! [ pos] ;
43+ while let Some ( pos) = edge. pop ( ) {
44+ if visitied. contains ( & pos) {
45+ continue ;
46+ }
47+
48+ visitied. insert ( pos) ;
49+
50+ let plant = input. map [ & pos] ;
51+ area += 1 ;
52+
53+ for adj in [ UP , DOWN , LEFT , RIGHT ] . into_iter ( ) . map ( |d| pos + d) {
54+ if input. map . get ( & adj) != Some ( & plant) {
55+ perimenter += 1 ;
56+ }
57+
58+ if !input. map . contains_key ( & adj) {
59+ continue ;
60+ }
61+
62+ if visitied. contains ( & adj) {
63+ continue ;
64+ }
65+
66+ if input. map [ & adj] == plant {
67+ edge. push ( adj) ;
68+ }
69+ }
70+ }
71+
72+ regions. push ( ( plant, area, perimenter) ) ;
73+ }
74+ }
75+
76+ regions. into_iter ( )
77+ . map ( |( _, a, p) | a * p)
78+ . sum ( )
2179}
2280
2381fn part2 ( input : & Input ) -> usize {
24- 0
82+ let mut regions = Vec :: new ( ) ;
83+
84+ let mut visitied = BTreeSet :: new ( ) ;
85+ for y in input. bounds . 1 . clone ( ) {
86+ for x in input. bounds . 0 . clone ( ) {
87+ let pos = Vec2 :: new ( [ x, y] ) ;
88+ if visitied. contains ( & pos) {
89+ continue ;
90+ }
91+
92+ let plant = input. map [ & pos] ;
93+ let mut area = 0 ;
94+ let mut edges = 0 ;
95+
96+ let mut edge = vec ! [ pos] ;
97+ while let Some ( pos) = edge. pop ( ) {
98+ if visitied. contains ( & pos) {
99+ continue ;
100+ }
101+
102+ visitied. insert ( pos) ;
103+
104+ let plant = input. map [ & pos] ;
105+ area += 1 ;
106+
107+ for adj in [ UP , DOWN , LEFT , RIGHT ] . into_iter ( ) . map ( |d| pos + d) {
108+ if !input. map . contains_key ( & adj) {
109+ continue ;
110+ }
111+
112+ if visitied. contains ( & adj) {
113+ continue ;
114+ }
115+
116+ if input. map [ & adj] == plant {
117+ edge. push ( adj) ;
118+ }
119+ }
120+
121+ // Look for corners
122+ for d1 in [ UP , DOWN ] {
123+ for d2 in [ LEFT , RIGHT ] {
124+ let c1 = input. map . get ( & ( pos + d1) ) == Some ( & plant) ;
125+ let c2 = input. map . get ( & ( pos + d2) ) == Some ( & plant) ;
126+ let c3 = input. map . get ( & ( pos + d1 + d2) ) == Some ( & plant) ;
127+
128+ match ( c1, c2, c3) {
129+ // A A A X
130+ // A X or X ?
131+ ( true , true , false ) | ( false , false , _) => {
132+ edges += 1 ;
133+ } ,
134+ _ => ( ) ,
135+ }
136+ }
137+ }
138+ }
139+
140+ regions. push ( ( plant, area, edges) ) ;
141+ }
142+ }
143+
144+ regions. into_iter ( )
145+ . map ( |( _, a, p) | a * p)
146+ . sum ( )
25147}
26148
149+ type Vec2 = Vector < i32 , 2 > ;
150+
27151#[ derive( Debug , Clone ) ]
28152struct Input {
29- values : Vec < String > ,
153+ bounds : ( Range < i32 > , Range < i32 > ) ,
154+ map : BTreeMap < Vec2 , char > ,
30155}
31156
32157impl Input {
33158 fn from_file ( path : impl AsRef < Path > ) -> io:: Result < Self > {
34159 let input = fs:: read_to_string ( path) ?;
35- let values = input. lines ( ) . map ( str:: to_string) . collect ( ) ;
36160
37- Ok ( Self { values } )
161+ let mut map = BTreeMap :: new ( ) ;
162+ let mut width = 0 ;
163+ let mut height = 0 ;
164+
165+ for ( y, line) in input. lines ( ) . enumerate ( ) {
166+ for ( x, c) in line. trim ( ) . chars ( ) . enumerate ( ) {
167+ let pos = Vec2 :: new ( [ x as i32 , y as i32 ] ) ;
168+
169+ map. insert ( pos, c) ;
170+ width = width. max ( x as i32 + 1 ) ;
171+ }
172+ height = height. max ( y as i32 + 1 ) ;
173+ }
174+
175+ let bounds = ( 0 ..width, 0 ..height) ;
176+
177+ Ok ( Self { bounds, map } )
38178 }
39179}
40180
@@ -43,30 +183,66 @@ mod test {
43183 use super :: * ;
44184
45185 #[ test]
46- fn test_part1 ( ) {
186+ fn test_part1_example1 ( ) {
47187 let input = Input :: from_file ( "example1.txt" ) . unwrap ( ) ;
48188
49- assert_eq ! ( part1( & input) , 0 ) ;
189+ assert_eq ! ( part1( & input) , 140 ) ;
190+ }
191+
192+ #[ test]
193+ fn test_part1_example2 ( ) {
194+ let input = Input :: from_file ( "example2.txt" ) . unwrap ( ) ;
195+
196+ assert_eq ! ( part1( & input) , 1930 ) ;
50197 }
51198
52199 #[ test]
53200 fn test_part1_solution ( ) {
54201 let input = Input :: from_file ( "input.txt" ) . unwrap ( ) ;
55202
56- assert_eq ! ( part1( & input) , 0 ) ;
203+ assert_eq ! ( part1( & input) , 1371306 ) ;
57204 }
58205
59206 #[ test]
60- fn test_part2 ( ) {
207+ fn test_part2_example1 ( ) {
61208 let input = Input :: from_file ( "example1.txt" ) . unwrap ( ) ;
62209
63- assert_eq ! ( part2( & input) , 0 ) ;
210+ assert_eq ! ( part2( & input) , 80 ) ;
211+ }
212+
213+ #[ test]
214+ fn test_part2_example2 ( ) {
215+ let input = Input :: from_file ( "example2.txt" ) . unwrap ( ) ;
216+
217+ assert_eq ! ( part2( & input) , 1206 ) ;
218+ }
219+
220+ #[ test]
221+ fn test_part2_example3 ( ) {
222+ let input = Input :: from_file ( "example3.txt" ) . unwrap ( ) ;
223+
224+ assert_eq ! ( part2( & input) , 436 ) ;
225+ }
226+
227+ #[ test]
228+ fn test_part2_example4 ( ) {
229+ let input = Input :: from_file ( "example4.txt" ) . unwrap ( ) ;
230+
231+ assert_eq ! ( part2( & input) , 236 ) ;
232+ }
233+
234+ #[ test]
235+ fn test_part2_example5 ( ) {
236+ let input = Input :: from_file ( "example5.txt" ) . unwrap ( ) ;
237+
238+ assert_eq ! ( part2( & input) , 368 ) ;
64239 }
65240
66241 #[ test]
67242 fn test_part2_solution ( ) {
68243 let input = Input :: from_file ( "input.txt" ) . unwrap ( ) ;
69244
70- assert_eq ! ( part2( & input) , 0 ) ;
245+ // Not 798452
246+ assert_eq ! ( part2( & input) , 805880 ) ;
71247 }
72248}
0 commit comments