@@ -10,6 +10,18 @@ struct RangeMap {
10
10
dst : Range < usize > ,
11
11
}
12
12
13
+ impl PartialOrd for RangeMap {
14
+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
15
+ Some ( self . cmp ( other) )
16
+ }
17
+ }
18
+
19
+ impl Ord for RangeMap {
20
+ fn cmp ( & self , other : & Self ) -> std:: cmp:: Ordering {
21
+ self . src . start . cmp ( & other. src . start )
22
+ }
23
+ }
24
+
13
25
impl FromStr for RangeMap {
14
26
type Err = Box < dyn Error > ;
15
27
@@ -105,9 +117,153 @@ fn part1(input: &Input) -> usize {
105
117
lowest_location. unwrap ( )
106
118
}
107
119
120
+ fn intersect ( a : & Range < usize > , b : & Range < usize > ) -> Option < Range < usize > > {
121
+ let start = std:: cmp:: max ( a. start , b. start ) ;
122
+ let end = std:: cmp:: min ( a. end , b. end ) ;
123
+ if end > start {
124
+ Some ( start..end)
125
+ } else {
126
+ None
127
+ }
128
+ }
129
+
130
+ fn remove ( whole : & Range < usize > , to_remove : & Range < usize > ) -> Option < ( Range < usize > , Option < Range < usize > > ) > {
131
+ let mut before = None ;
132
+ let mut after = None ;
133
+ if to_remove. start > whole. start {
134
+ before = Some ( whole. start ..to_remove. start ) ;
135
+ }
136
+ if to_remove. end < whole. end {
137
+ after = Some ( to_remove. end ..whole. end ) ;
138
+ }
139
+ if before. as_ref ( ) . is_some_and ( |r| r. is_empty ( ) ) {
140
+ before = None ;
141
+ }
142
+ if after. as_ref ( ) . is_some_and ( |r| r. is_empty ( ) ) {
143
+ after = None ;
144
+ }
145
+
146
+ match ( before, after) {
147
+ ( Some ( before) , Some ( after) ) => Some ( ( before, Some ( after) ) ) ,
148
+ ( Some ( before) , None ) => Some ( ( before, None ) ) ,
149
+ ( None , Some ( after) ) => Some ( ( after, None ) ) ,
150
+ ( None , None ) => None ,
151
+ }
152
+ }
153
+
154
+ fn find_lowest ( input : & Input , in_range : & Range < usize > , current_type : & str ) -> usize {
155
+
156
+ println ! ( "find_lowest({:?}, {:?})" , in_range, current_type) ;
157
+ let mut lowest_location = None ;
158
+ let mut remaining = Vec :: new ( ) ;
159
+ remaining. push ( RangeMap { src : in_range. clone ( ) , dst : in_range. clone ( ) } ) ;
160
+
161
+ let map = input. maps . get ( current_type) . expect ( "Couldn't find map!" ) ;
162
+ let mut range_index = 0 ;
163
+ while range_index < map. ranges . len ( ) || remaining. len ( ) > 0 {
164
+ let r = if range_index < map. ranges . len ( ) {
165
+ let r = & map. ranges [ range_index] ;
166
+ range_index += 1 ;
167
+ r. clone ( )
168
+ } else {
169
+ let r = remaining. pop ( ) . unwrap ( ) ;
170
+ if r. src . start == r. src . end {
171
+ continue ;
172
+ }
173
+ r
174
+ } ;
175
+
176
+ if let Some ( overlap) = intersect ( & r. src , & in_range) {
177
+ let offset = overlap. start - r. src . start ;
178
+ let dst = r. dst . start + offset .. r. dst . start + offset + overlap. len ( ) ;
179
+ let location = if map. to == "location" {
180
+ r. dst . start
181
+ } else {
182
+ find_lowest ( input, & dst, & map. to )
183
+ } ;
184
+
185
+ if let Some ( ref mut lowest) = & mut lowest_location {
186
+ * lowest = std:: cmp:: min ( * lowest, location) ;
187
+ } else {
188
+ assert_ne ! ( 0 , location) ;
189
+ lowest_location = Some ( location) ;
190
+ }
191
+
192
+ let remaining_intial_len = remaining. len ( ) ;
193
+ for i in 0 ..remaining_intial_len {
194
+ let remove_result = remove ( & remaining[ i] . src , & overlap) ;
195
+ println ! ( "remove({:?}, {:?}) = {:?}" , remaining[ i] . src, overlap, remove_result) ;
196
+ if let Some ( ( src_before, src_after) ) = remove_result {
197
+ let offset = remaining[ i] . dst . start as isize - remaining[ i] . src . start as isize ;
198
+ remaining[ i] . dst = ( src_before. start as isize + offset) as usize .. ( src_before. end as isize + offset) as usize ;
199
+ remaining[ i] . src = src_before;
200
+ if let Some ( src_after) = src_after {
201
+ remaining. push ( RangeMap {
202
+ dst : ( src_after. start as isize + offset) as usize .. ( src_after. end as isize + offset) as usize ,
203
+ src : src_after,
204
+ } ) ;
205
+ }
206
+ } else {
207
+ remaining[ i] . src = 0 ..0 ;
208
+ remaining[ i] . dst = 0 ..0 ;
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ if let Some ( lowest) = lowest_location {
215
+ lowest
216
+ } else if map. to == "location" {
217
+ in_range. start
218
+ } else {
219
+ find_lowest ( input, in_range, & map. to )
220
+ }
221
+ }
222
+
223
+ #[ aoc( day5, part2) ]
224
+ fn part2 ( input : & Input ) -> usize {
225
+ let mut lowest_location = None ;
226
+ for seed_range in input. seeds . chunks_exact ( 2 ) {
227
+ let seed_range = seed_range[ 0 ] ..seed_range[ 0 ] +seed_range[ 1 ] ;
228
+ let location = find_lowest ( input, & seed_range, "seed" ) ;
229
+
230
+ if let Some ( ref mut lowest) = & mut lowest_location {
231
+ * lowest = std:: cmp:: min ( * lowest, location) ;
232
+ } else {
233
+ lowest_location = Some ( location) ;
234
+ }
235
+ }
236
+ lowest_location. unwrap ( )
237
+ }
238
+
108
239
#[ cfg( test) ]
109
240
mod tests {
241
+ use core:: panic;
242
+
110
243
use super :: * ;
244
+ #[ test]
245
+ fn test_intersect ( ) {
246
+ for x1 in 0 ..5 {
247
+ for x2 in x1..5 {
248
+ for y1 in 0 ..5 {
249
+ for y2 in y1..5 {
250
+ let x = x1..x2;
251
+ let y = y1..y2;
252
+ let i = intersect ( & x, & y) ;
253
+ for z in 0 ..5 {
254
+ let in_both = x. contains ( & z) && y. contains ( & z) ;
255
+ match ( i. as_ref ( ) , in_both) {
256
+ ( Some ( i) , in_both) =>
257
+ assert_eq ! ( i. contains( & z) , in_both) ,
258
+ ( None , false ) => { } ,
259
+ _ => panic ! ( "{:?} {:?} {} {:?} {}" , x, y, z, i, in_both) ,
260
+ }
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
111
267
112
268
#[ test]
113
269
fn part1_example ( ) {
@@ -148,4 +304,54 @@ humidity-to-location map:
148
304
"# . trim ( ) ) ;
149
305
assert_eq ! ( part1( & input) , 35 ) ;
150
306
}
307
+
308
+ #[ test]
309
+ fn part2_example ( ) {
310
+ let input = parse_input ( r#"
311
+ seeds: 79 14 55 13
312
+
313
+ seed-to-soil map:
314
+ 50 98 2
315
+ 52 50 48
316
+
317
+ soil-to-fertilizer map:
318
+ 0 15 37
319
+ 37 52 2
320
+ 39 0 15
321
+
322
+ fertilizer-to-water map:
323
+ 49 53 8
324
+ 0 11 42
325
+ 42 0 7
326
+ 57 7 4
327
+
328
+ water-to-light map:
329
+ 88 18 7
330
+ 18 25 70
331
+
332
+ light-to-temperature map:
333
+ 45 77 23
334
+ 81 45 19
335
+ 68 64 13
336
+
337
+ temperature-to-humidity map:
338
+ 0 69 1
339
+ 1 0 69
340
+
341
+ humidity-to-location map:
342
+ 60 56 37
343
+ 56 93 4
344
+ "# . trim ( ) ) ;
345
+
346
+ assert_eq ! ( 46 , find_lowest( & input, & ( 46 ..47 ) , "humidity" ) ) ;
347
+ assert_eq ! ( 46 , find_lowest( & input, & ( 45 ..46 ) , "temperature" ) ) ;
348
+ assert_eq ! ( 46 , find_lowest( & input, & ( 77 ..78 ) , "light" ) ) ;
349
+ assert_eq ! ( 46 , find_lowest( & input, & ( 84 ..85 ) , "water" ) ) ;
350
+ assert_eq ! ( 46 , find_lowest( & input, & ( 84 ..85 ) , "fertilizer" ) ) ;
351
+ assert_eq ! ( 46 , find_lowest( & input, & ( 84 ..85 ) , "soil" ) ) ;
352
+ assert_eq ! ( 46 , find_lowest( & input, & ( 82 ..83 ) , "seed" ) ) ;
353
+ assert_eq ! ( 46 , find_lowest( & input, & ( 79 ..93 ) , "seed" ) ) ;
354
+
355
+ // assert_eq!(part2(&input), 46);
356
+ }
151
357
}
0 commit comments