day 5 part 2
This commit is contained in:
parent
89734cdd86
commit
ee317153e6
1 changed files with 186 additions and 39 deletions
221
src/main.rs
221
src/main.rs
|
@ -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 }]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue