diff --git a/2023/src/bin/day05.rs b/2023/src/bin/day05.rs index ca5e9b9..161f01e 100644 --- a/2023/src/bin/day05.rs +++ b/2023/src/bin/day05.rs @@ -1,4 +1,4 @@ -#![feature(test)] +#![feature(test, iter_array_chunks)] extern crate test; use std::ops::Range; @@ -7,7 +7,7 @@ use itertools::Itertools; const DAY: usize = 5; type I = i64; -type Mapping = Vec<(Range, I)>; +type Mapping = Vec<(Range, Range, I)>; type Parsed = (Vec, Vec); fn parse_input(raw: &str) -> Parsed { @@ -18,7 +18,8 @@ fn parse_input(raw: &str) -> Parsed { g.lines() .skip(1) .map(|l| l.split(' ').map(parse_num).collect_tuple().unwrap()) - .map(|(dst, src, len)| (src..src + len, dst - src)) + // We swap the order of dst and src here because it just makes much more sense to me this way. + .map(|(dst, src, len)| (src..src + len, dst..dst + len, dst - src)) .collect() }) .collect(); @@ -26,15 +27,40 @@ fn parse_input(raw: &str) -> Parsed { } fn resolve(start: I, mappings: &Vec) -> I { - mappings.iter().fold(start, |i, map| map.iter().find_map(|(range, offset)| range.contains(&i).then_some(i + offset)).unwrap_or(i)) + mappings.iter().fold(start, |i, map| map.iter().find_map(|(range, _, offset)| range.contains(&i).then_some(i + offset)).unwrap_or(i)) } fn part1((seeds, mappings): &Parsed) -> I { seeds.iter().map(|&s| resolve(s, mappings)).min().unwrap() } -fn part2(parsed: &Parsed) -> I { - unimplemented!() +fn resolve_backwards(start: I, mappings: &Vec) -> I { + mappings.iter().fold(start, |i, map| map.iter().find_map(|(_, range, offset)| range.contains(&i).then_some(i - offset)).unwrap_or(i)) +} + +fn part2((seeds, mappings): &Parsed) -> I { + let seed_ranges = seeds.iter().array_chunks().map(|[&a, &b]| a..a + b).collect_vec(); + let mut mappings = mappings.clone(); + // let smallest_range = mappings.iter().flatten().map(|(r, _, _)| r.try_len().unwrap()).min().unwrap(); + + let mut destinations = mappings.remove(mappings.len() - 1); + destinations.sort_by_key(|(_, range, _)| range.start); + mappings.reverse(); + + let mut ranges = Vec::<(Range, I)>::new(); + for (_, range, offset) in destinations { + ranges.push((ranges.last().map(|(r, _)| r.end).unwrap_or(1)..range.start, 0)); + ranges.push((range, offset)); + } + ranges.into_iter().find_map(|(range, offset)| starting_seed(range, offset, &seed_ranges, &mappings, 1)).unwrap() +} + +fn starting_seed(range: Range, offset: i64, seed_ranges: &Vec>, mappings: &Vec, step: usize) -> Option { + range.step_by(step).find(|&s| { + let seed = resolve_backwards(s - offset, &mappings); + // If seed == s, the entire resolution didn’t hit a single mapping, so we don’t need to check seeds. + seed != s && seed_ranges.iter().any(|r| r.contains(&seed)) + }) } boilerplate! { @@ -73,9 +99,9 @@ humidity-to-location map: 56 93 4", tests: { part1: { TEST_INPUT => 35 }, - part2: { TEST_INPUT => 0 }, + part2: { TEST_INPUT => 46 }, }, bench1 == 462648396, - bench2 == 0, + bench2 == 2520479, bench_parse: |(v1, v2): &Parsed| (v1.len(), v2.len()) => (20, 7), }