diff --git a/2023/day05/src/lib.rs b/2023/day05/src/lib.rs index 6b020e2..2698b38 100644 --- a/2023/day05/src/lib.rs +++ b/2023/day05/src/lib.rs @@ -1,3 +1,5 @@ +mod p2basic; + use helper::{Day, IteratorExt, Variants}; pub fn main() { @@ -12,7 +14,7 @@ helper::define_variants! { basic => crate::part1; } part2 { - basic => crate::part2; + basic => crate::p2basic::part2; } } @@ -33,6 +35,12 @@ struct MappedRange { len: u64, } +impl MappedRange { + fn source_end(&self) -> u64 { + self.source_start + self.len + } +} + fn parse_maps<'a>(mut lines: impl Iterator) -> Vec> { let mut maps = vec![]; while let Some(line) = lines.next() { @@ -95,8 +103,42 @@ fn part1(input: &str) -> u64 { min_loc } -fn part2(_input: &str) -> u64 { - 0 +#[allow(dead_code)] +fn part2_brute_force(input: &str) -> u64 { + let mut lines = input.lines(); + let seeds = lines + .next() + .unwrap() + .strip_prefix("seeds: ") + .unwrap() + .split_ascii_whitespace() + .map(|n| n.parse::().unwrap()) + .collect::>(); + let seeds = seeds.chunks(2); + + let maps = parse_maps(lines); + + let mut min_loc = u64::MAX; + + for seeds in seeds { + for mut seed in seeds[0]..(seeds[0] + seeds[1]) { + for ranges in &maps { + match ranges.iter().find(|range| { + (range.source_start..(range.source_start + range.len)).contains(&seed) + }) { + Some(range) => { + let offset = seed - range.source_start; + let new = range.dest_start + offset; + seed = new; + } + None => {} + } + } + min_loc = std::cmp::min(min_loc, seed); + } + } + + min_loc } helper::tests! { @@ -106,8 +148,8 @@ helper::tests! { default => 457535844; } part2 { - small => 0; - default => 0; + small => 46; + default => 41222968; } } helper::benchmarks! {} diff --git a/2023/day05/src/p2basic.rs b/2023/day05/src/p2basic.rs new file mode 100644 index 0000000..7f7f2f8 --- /dev/null +++ b/2023/day05/src/p2basic.rs @@ -0,0 +1,61 @@ +use std::{cmp, ops::Range}; + +pub fn part2(input: &str) -> u64 { + let mut lines = input.lines(); + let seeds = lines + .next() + .unwrap() + .strip_prefix("seeds: ") + .unwrap() + .split_ascii_whitespace() + .map(|n| n.parse::().unwrap()) + .collect::>(); + let seeds = seeds.chunks(2); + + let maps = super::parse_maps(lines); + + let mut min_loc = u64::MAX; + + let mut current_seeds: Vec<(usize, usize, Range)> = seeds + .map(|seed| (0, 0, seed[0]..(seed[0] + seed[1]))) + .collect(); + + 'queue: while let Some((place, range_idx, numbers)) = current_seeds.pop() { + if place == maps.len() { + // Range is done. + min_loc = cmp::min(min_loc, numbers.start); + continue; + } + let ranges = &maps[place]; + for range in ranges.iter().skip(range_idx) { + if range.source_end() <= numbers.start || range.source_start >= numbers.end { + // Completely out of range. + continue; + } + + // Part that falls before this redirection, needs to be queued again. + let pre = numbers.start..range.source_start; + let in_ = cmp::max(numbers.start, range.source_start) + ..cmp::min(numbers.end, range.source_end()); + let post = range.source_end()..(numbers.end); + + if !pre.is_empty() { + current_seeds.push((place, range_idx + 1, pre)); + } + if !post.is_empty() { + current_seeds.push((place, range_idx + 1, post)); + } + let offset = in_.start - range.source_start; + let new = (range.dest_start + offset)..(range.dest_start + offset + (in_.end - in_.start)); + + if !new.is_empty() { + current_seeds.push((place + 1, 0, new)); + } + continue 'queue; + } + // No change, pass unaffected + current_seeds.push((place + 1, 0, numbers)); + } + + min_loc +}