From ee317153e619967a01412d634b2059ef862d22f5 Mon Sep 17 00:00:00 2001 From: Andy Teijelo Date: Thu, 7 Dec 2023 01:17:13 -0500 Subject: [PATCH] day 5 part 2 --- src/main.rs | 225 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 186 insertions(+), 39 deletions(-) diff --git a/src/main.rs b/src/main.rs index 179b9f1..fed0cd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,55 +1,126 @@ +use std::cmp::{max, min}; use std::{collections::HashMap, io::stdin}; use anyhow::Result; use regex::Regex; -// -// #[derive(Debug)] -// struct Seed(i64); -// -// #[derive(Debug)] -// struct Soil(i64); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Range { + start: i64, + len: i64, +} + +impl Range { + fn intersection(&self, other: &Range) -> Option { + let i1 = max(self.start, other.start); + let i2 = min(self.start + self.len, other.start + other.len); + + if i1 >= i2 { + return None; + } + + Some(Range { + start: i1, + len: i2 - i1, + }) + } +} + +#[derive(Debug, Clone, Copy)] +struct RangeMap { + from: i64, + to: i64, + len: i64, +} #[derive(Debug)] -struct Map { - ranges: Vec<(i64, i64, i64)>, +struct SeedsMap { + range_maps: Vec, } -impl Map { - fn new() -> Map { - Map { ranges: vec![] } +impl SeedsMap { + fn new() -> SeedsMap { + SeedsMap { range_maps: vec![] } } + fn add_range(&mut self, range: (i64, i64, i64)) { - self.ranges.push(range); + self.range_maps.push(RangeMap { + from: range.1, + to: range.0, + len: range.2, + }); + self.range_maps.sort_by_key(|r| r.from); } - fn map(&self, value: i64) -> i64 { - for (to, from, len) in &self.ranges { - if value >= *from && value < (from + len) { - return to + (value - from); + + fn map(&self, ranges: &[Range]) -> Vec { + let mut result = vec![]; + for r in ranges { + result.extend(self.map_single_range(r)); + } + result + } + + fn map_single_range(&self, range: &Range) -> Vec { + if self.range_maps.is_empty() { + return vec![*range]; + } + + let mut result = vec![]; + let first = self.range_maps.first().unwrap(); + let last = self.range_maps.last().unwrap(); + let left = min(range.start, first.from); + let right = max(range.start + range.len, last.from + last.len); + + let mut last = left; + for rm in &self.range_maps { + // first, a range from last to rm.start, with no mapping + let r = Range { + start: last, + len: rm.from - last, + }; + if let Some(i) = r.intersection(range) { + result.push(i); + } + // second, a range from rm.start, with mapping + let r = Range { + start: rm.from, + len: rm.len, + }; + if let Some(i) = r.intersection(range) { + result.push(Range { + start: rm.to + i.start - rm.from, + len: i.len, + }); + last = i.start + i.len; } } - value + + let r = Range { + start: last, + len: right - last, + }; + if let Some(i) = r.intersection(range) { + result.push(i); + } + + result } } -// impl Seed { -// fn to_soil(self) -> Soil { -// Soil(seed_to_soil -// } -// } - -// type RangeMap = Vec<(i64, i64, i64)>; - -fn main() -> Result<()> { - let mut maps: HashMap = HashMap::new(); +fn parse_input(seed_ranges: &mut Vec, maps: &mut HashMap) -> Result<()> { let numbers = Regex::new(r"(\d+)")?; + let pairs = Regex::new(r"(\d+) (\d+)")?; let mut current_map = String::new(); - let mut seeds: Vec = vec![]; - for line in stdin().lines().map_while(Result::ok) { if line.starts_with("seeds:") { - seeds = numbers + *seed_ranges = pairs .captures_iter(&line) - .map(|c| c.get(1).unwrap().as_str().parse()) + .map(|c| -> Result { + Ok(Range { + start: c.get(1).unwrap().as_str().parse()?, + len: c.get(2).unwrap().as_str().parse()?, + }) + }) .map_while(Result::ok) .collect(); } else if line.ends_with(" map:") { @@ -65,11 +136,22 @@ fn main() -> Result<()> { .map_while(Result::ok) .collect(); if nums.len() == 3 { - let map = maps.entry(current_map.clone()).or_insert_with(Map::new); + let map = maps + .entry(current_map.clone()) + .or_insert_with(SeedsMap::new); map.add_range((nums[0], nums[1], nums[2])); } } } + Ok(()) +} + +fn main() -> Result<()> { + let mut maps: HashMap = HashMap::new(); + let mut seed_ranges: Vec = vec![]; + + parse_input(&mut seed_ranges, &mut maps)?; + let map_names = [ "seed-to-soil", "soil-to-fertilizer", @@ -79,17 +161,82 @@ fn main() -> Result<()> { "temperature-to-humidity", "humidity-to-location", ]; + let mut locations = vec![]; - for seed in &seeds { - let mut step = *seed; + for range in seed_ranges { + let mut mapped_ranges = vec![range]; for map_name in map_names { - let mapped = maps.get(map_name).unwrap().map(step); - println!("mapped {step} using {map_name} to {mapped}"); - step = mapped; + let mapped = maps.get(map_name).unwrap().map(&mapped_ranges); + mapped_ranges = mapped; } - locations.push(step); - println!(); + locations.extend(mapped_ranges.iter().map(|r| r.start)); } println!("{}", locations.iter().min().unwrap()); Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_intersections() { + let l = |r1: &Range, r2: &Range| r1.intersection(r2); + assert_eq!( + l(&Range { start: 1, len: 3 }, &Range { start: 1, len: 3 }), + Some(Range { start: 1, len: 3 }) + ); + assert_eq!( + l(&Range { start: 1, len: 2 }, &Range { start: 1, len: 3 }), + Some(Range { start: 1, len: 2 }) + ); + assert_eq!( + l(&Range { start: 2, len: 4 }, &Range { start: 0, len: 3 }), + Some(Range { start: 2, len: 1 }) + ); + assert_eq!( + l(&Range { start: 0, len: 3 }, &Range { start: 2, len: 4 }), + Some(Range { start: 2, len: 1 }) + ); + assert_eq!( + l(&Range { start: 3, len: 4 }, &Range { start: 0, len: 3 }), + None + ); + assert_eq!( + l(&Range { start: 0, len: 10 }, &Range { start: 4, len: 4 }), + Some(Range { start: 4, len: 4 }) + ); + } + + #[test] + fn test_range_map() { + let mut m = SeedsMap::new(); + m.add_range((52, 50, 48)); + m.add_range((50, 98, 2)); + let r = m.map_single_range(&Range { start: 79, len: 14 }); + assert_eq!(r, [Range { start: 81, len: 14 }]); + + let mut m = SeedsMap::new(); + m.add_range((39, 0, 15)); + m.add_range((0, 15, 37)); + m.add_range((37, 52, 2)); + let r = m.map_single_range(&Range { start: 81, len: 14 }); + assert_eq!(r, [Range { start: 81, len: 14 }]); + + let mut m = SeedsMap::new(); + m.add_range((88, 18, 7)); + m.add_range((18, 25, 70)); + let r = m.map_single_range(&Range { start: 81, len: 14 }); + assert_eq!(r, [Range { start: 74, len: 14 }]); + + let mut m = SeedsMap::new(); + m.add_range((81, 45, 19)); + m.add_range((68, 64, 13)); + m.add_range((45, 77, 23)); + let r = m.map_single_range(&Range { start: 74, len: 14 }); + assert_eq!( + r, + [Range { start: 78, len: 3 }, Range { start: 45, len: 11 }] + ); + } +}