From 41c97f0d7a60f94ec7d392889176a0cfa257b635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20K=C3=B6rber?= Date: Wed, 6 Dec 2023 00:01:32 +0100 Subject: [PATCH] Add types for readability --- 2023/day5/src/main.rs | 66 ++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/2023/day5/src/main.rs b/2023/day5/src/main.rs index 6db7d52..1d9f689 100644 --- a/2023/day5/src/main.rs +++ b/2023/day5/src/main.rs @@ -221,38 +221,57 @@ fn part2(input: &str, approach: Approach) -> Result { Approach::Ranges => { let current_ranges = almanac.seeds.0; - // Why the bool? So we mark ranges that we have already mapped, as we must not map them again - // in the same MapList - // - // I guess this could also be done using recusion, only recursing deeper for non-mapped ranges. - let mut current_ranges: Vec<(Range, bool)> = current_ranges + // As each range can be partially mapped, the mapped ranges need to be marked properly. They + // are skipped on further mappings, as mappings are only done *once* per block (they are not + // iterative). Only unmapped ranges are mapped. + #[derive(PartialEq, Eq)] + enum State { + Unmapped, + Mapped, + } + + #[derive(PartialEq, Eq)] + struct RangeToMap { + range: Range, + state: State, + } + + let mut current_ranges: Vec = current_ranges .into_iter() - .map(|range| (range, false)) + .map(|range| RangeToMap { + range, + state: State::Unmapped, + }) .collect(); + // There is one potential optimization that could be done after each mapping: Merge overlapping or adjacent ranges. + // This would cut down on the number of ranges. For the puzzle input, it does not make sense, as the number of ranges + // stays manageable (exactly 100 for my input). As number may grow by as much as three times for each mapping line, + // this optimization would become prudent for longer input with many half-matching ranges. for map_list in almanac.map_lists.into_iter() { // Mark all ranges as unmapped again - for i in 0..current_ranges.len() { - current_ranges[i].1 = false; + for range in &mut current_ranges { + range.state = State::Unmapped; } for map in map_list.maps { for i in 0..current_ranges.len() { - if current_ranges[i].1 { + if current_ranges[i].state == State::Mapped { continue; } - if current_ranges[i].0.end <= map.source_range.start { + if current_ranges[i].range.end <= map.source_range.start { // does not overlap, nothing to do continue; } - if current_ranges[i].0.start >= map.source_range.end { + if current_ranges[i].range.start >= map.source_range.end { // does not overlap, nothing to do continue; } - let overlap_start = max(current_ranges[i].0.start, map.source_range.start); + let overlap_start = + max(current_ranges[i].range.start, map.source_range.start); - let overlap_end = min(current_ranges[i].0.end, map.source_range.end); + let overlap_end = min(current_ranges[i].range.end, map.source_range.end); let overlapping_range = (overlap_start + map.destination_range.start - map.source_range.start) @@ -260,22 +279,31 @@ fn part2(input: &str, approach: Approach) -> Result { assert!(!overlapping_range.is_empty()); - let range_before = current_ranges[i].0.start..overlap_start; - let range_after = overlap_end..current_ranges[i].0.end; + let range_before = current_ranges[i].range.start..overlap_start; + let range_after = overlap_end..current_ranges[i].range.end; if !range_before.is_empty() { - current_ranges.push((range_before, false)); + current_ranges.push(RangeToMap { + range: range_before, + state: State::Unmapped, + }); } if !range_after.is_empty() { - current_ranges.push((range_after, false)); + current_ranges.push(RangeToMap { + range: range_after, + state: State::Unmapped, + }); } - current_ranges[i] = (overlapping_range, true); + current_ranges[i] = RangeToMap { + range: overlapping_range, + state: State::Mapped, + }; } } } current_ranges .into_iter() - .map(|range| range.0.start) + .map(|range| range.range.start) .min() .unwrap() }