day 5 part 2

This commit is contained in:
Andy Teijelo 2023-12-07 01:17:13 -05:00
parent 89734cdd86
commit ee317153e6

View file

@ -1,55 +1,126 @@
use std::cmp::{max, min};
use std::{collections::HashMap, io::stdin}; use std::{collections::HashMap, io::stdin};
use anyhow::Result; use anyhow::Result;
use regex::Regex; use regex::Regex;
//
// #[derive(Debug)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
// struct Seed(i64); struct Range {
// start: i64,
// #[derive(Debug)] len: i64,
// struct Soil(i64); }
impl Range {
fn intersection(&self, other: &Range) -> Option<Range> {
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)] #[derive(Debug)]
struct Map { struct SeedsMap {
ranges: Vec<(i64, i64, i64)>, range_maps: Vec<RangeMap>,
} }
impl Map { impl SeedsMap {
fn new() -> Map { fn new() -> SeedsMap {
Map { ranges: vec![] } SeedsMap { range_maps: vec![] }
} }
fn add_range(&mut self, range: (i64, i64, i64)) { 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 { fn map(&self, ranges: &[Range]) -> Vec<Range> {
if value >= *from && value < (from + len) { let mut result = vec![];
return to + (value - from); for r in ranges {
result.extend(self.map_single_range(r));
} }
result
} }
value
fn map_single_range(&self, range: &Range) -> Vec<Range> {
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;
} }
} }
// impl Seed { let r = Range {
// fn to_soil(self) -> Soil { start: last,
// Soil(seed_to_soil len: right - last,
// } };
// } if let Some(i) = r.intersection(range) {
result.push(i);
}
// type RangeMap = Vec<(i64, i64, i64)>; result
}
}
fn main() -> Result<()> { fn parse_input(seed_ranges: &mut Vec<Range>, maps: &mut HashMap<String, SeedsMap>) -> Result<()> {
let mut maps: HashMap<String, Map> = HashMap::new();
let numbers = Regex::new(r"(\d+)")?; let numbers = Regex::new(r"(\d+)")?;
let pairs = Regex::new(r"(\d+) (\d+)")?;
let mut current_map = String::new(); let mut current_map = String::new();
let mut seeds: Vec<i64> = vec![];
for line in stdin().lines().map_while(Result::ok) { for line in stdin().lines().map_while(Result::ok) {
if line.starts_with("seeds:") { if line.starts_with("seeds:") {
seeds = numbers *seed_ranges = pairs
.captures_iter(&line) .captures_iter(&line)
.map(|c| c.get(1).unwrap().as_str().parse()) .map(|c| -> Result<Range> {
Ok(Range {
start: c.get(1).unwrap().as_str().parse()?,
len: c.get(2).unwrap().as_str().parse()?,
})
})
.map_while(Result::ok) .map_while(Result::ok)
.collect(); .collect();
} else if line.ends_with(" map:") { } else if line.ends_with(" map:") {
@ -65,11 +136,22 @@ fn main() -> Result<()> {
.map_while(Result::ok) .map_while(Result::ok)
.collect(); .collect();
if nums.len() == 3 { 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])); map.add_range((nums[0], nums[1], nums[2]));
} }
} }
} }
Ok(())
}
fn main() -> Result<()> {
let mut maps: HashMap<String, SeedsMap> = HashMap::new();
let mut seed_ranges: Vec<Range> = vec![];
parse_input(&mut seed_ranges, &mut maps)?;
let map_names = [ let map_names = [
"seed-to-soil", "seed-to-soil",
"soil-to-fertilizer", "soil-to-fertilizer",
@ -79,17 +161,82 @@ fn main() -> Result<()> {
"temperature-to-humidity", "temperature-to-humidity",
"humidity-to-location", "humidity-to-location",
]; ];
let mut locations = vec![]; let mut locations = vec![];
for seed in &seeds { for range in seed_ranges {
let mut step = *seed; let mut mapped_ranges = vec![range];
for map_name in map_names { for map_name in map_names {
let mapped = maps.get(map_name).unwrap().map(step); let mapped = maps.get(map_name).unwrap().map(&mapped_ranges);
println!("mapped {step} using {map_name} to {mapped}"); mapped_ranges = mapped;
step = mapped;
} }
locations.push(step); locations.extend(mapped_ranges.iter().map(|r| r.start));
println!();
} }
println!("{}", locations.iter().min().unwrap()); println!("{}", locations.iter().min().unwrap());
Ok(()) 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 }]
);
}
}