diff --git a/src/bin/05.rs b/src/bin/05.rs index 111bbf5..a554734 100644 --- a/src/bin/05.rs +++ b/src/bin/05.rs @@ -25,40 +25,18 @@ pub fn part_two(input: &str) -> Option { #[derive(Clone, Debug, PartialEq, Eq)] struct Almanac { seeds: Vec, - seed_to_soil: AlmanacMap, - soil_to_fertilizer: AlmanacMap, - fertilizer_to_water: AlmanacMap, - water_to_light: AlmanacMap, - light_to_temperature: AlmanacMap, - temperature_to_humidity: AlmanacMap, - humidity_to_location: AlmanacMap, + maps: Vec, } impl Almanac { pub fn lookup_location(&self, seed: u64) -> u64 { - let soil = self.seed_to_soil.map(seed); - let fertilizer = self.soil_to_fertilizer.map(soil); - let water = self.fertilizer_to_water.map(fertilizer); - let light = self.water_to_light.map(water); - let temperature = self.light_to_temperature.map(light); - let humidity = self.temperature_to_humidity.map(temperature); - - self.humidity_to_location.map(humidity) + self.maps.iter().fold(seed, |value, map| map.map(value)) } pub fn min_location(&self, input: Range) -> Option { - let mappings = [ - &self.seed_to_soil, - &self.soil_to_fertilizer, - &self.fertilizer_to_water, - &self.water_to_light, - &self.light_to_temperature, - &self.temperature_to_humidity, - &self.humidity_to_location, - ]; let mut ranges = vec![input]; let mut next_ranges = vec![]; - for mapping in mappings { + for mapping in &self.maps { for range in ranges.drain(..) { next_ranges.extend(mapping.map_range(range)); } @@ -81,17 +59,23 @@ impl AlmanacMap { .unwrap_or(input) } - pub fn map_range(&self, input: Range) -> Vec> { + pub fn map_range(&self, input: Range) -> impl IntoIterator> + '_ { let mut input = input; let mut output = vec![]; + + // NOTE: we assume the ranges are sorted in ascending order. for range_mapping in &self.ranges { if range_mapping.source_range.start >= input.end { + // The input range ends before this range starts, so we can early terminate here. output.push(input); return output; } if input.start < range_mapping.source_range.start { + // Part of the input precedes this range, so pass that part through as is. output.push(input.start..range_mapping.source_range.start); } + + // Compute the intersection of the two ranges and add the offset of this mapping. let start = std::cmp::max(input.start, range_mapping.source_range.start); let end = std::cmp::min(input.end, range_mapping.source_range.end); if end > start { @@ -99,6 +83,8 @@ impl AlmanacMap { (start as i64 + range_mapping.offset) as u64 ..(end as i64 + range_mapping.offset) as u64, ); + + // Adjust the input to be only the part that comes after the range in this mapping. if end < input.end { input = end..input.end; } else { @@ -106,7 +92,10 @@ impl AlmanacMap { } } } - output.push(input); + if input.end > input.start { + // If there is anything leftover after the last mapping, pass that through as-is. + output.push(input); + } output } } @@ -145,7 +134,7 @@ impl FromStr for Almanac { .map(|s| s.parse::()) .collect::, _>>() .map_err(|_| ParseAlmanacErr)?; - let mut maps = parts + let maps = parts .into_iter() .skip(1) .map(|s| { @@ -153,18 +142,8 @@ impl FromStr for Almanac { .ok_or(ParseAlmanacErr) .and_then(|(_, s)| s.parse::()) }) - .collect::, _>>()? - .into_iter(); - Ok(Self { - seeds, - seed_to_soil: maps.next().ok_or(ParseAlmanacErr)?, - soil_to_fertilizer: maps.next().ok_or(ParseAlmanacErr)?, - fertilizer_to_water: maps.next().ok_or(ParseAlmanacErr)?, - water_to_light: maps.next().ok_or(ParseAlmanacErr)?, - light_to_temperature: maps.next().ok_or(ParseAlmanacErr)?, - temperature_to_humidity: maps.next().ok_or(ParseAlmanacErr)?, - humidity_to_location: maps.next().ok_or(ParseAlmanacErr)?, - }) + .collect::, _>>()?; + Ok(Self { seeds, maps }) } } @@ -230,10 +209,13 @@ mod tests { }, ], }; - assert_eq!(almananc_map.map_range(79..93), vec![81..95]); - assert_eq!(almananc_map.map_range(90..100), vec![92..100, 50..52]); + assert_eq!(Vec::from_iter(almananc_map.map_range(79..93)), vec![81..95]); + assert_eq!( + Vec::from_iter(almananc_map.map_range(90..100)), + vec![92..100, 50..52] + ); assert_eq!( - almananc_map.map_range(90..102), + Vec::from_iter(almananc_map.map_range(90..102)), vec![92..100, 50..52, 100..102] ); @@ -253,6 +235,6 @@ mod tests { }, ], }; - assert_eq!(almanac_map.map_range(81..95), vec![81..95]); + assert_eq!(Vec::from_iter(almanac_map.map_range(81..95)), vec![81..95]); } }