solve year 2023 day 05

This commit is contained in:
2023-12-05 22:55:01 +01:00
parent 180faf92bb
commit 46165b1acb
4 changed files with 347 additions and 0 deletions

View File

@@ -24,4 +24,5 @@ pub mod year2023 {
pub mod day02;
pub mod day03;
pub mod day04;
pub mod day05;
}

View File

@@ -99,5 +99,6 @@ fn year2023() -> Vec<Solution> {
solution!(year2023, day02),
solution!(year2023, day03),
solution!(year2023, day04),
solution!(year2023, day05),
]
}

155
src/year2023/day05.rs Normal file
View File

@@ -0,0 +1,155 @@
use crate::util::parse::ParseExt;
#[derive(Debug)]
struct Map {
tuples: Vec<(usize, usize, usize)>,
}
impl Map {
// We divide the seed range up into smaller seed ranges:
// 1) seed range before map range
// 2) seed range after map range
// 3) seed range in map range
// for the seed range inside the map range we map it and it to the result array
// the before and after seed ranges will be saved for the next map where. apply again
// rinse and repeat until the ranges are empty
fn calc_range(&self, mut ranges: Vec<(usize, usize)>) -> Vec<(usize, usize)> {
let mut mapped = vec![];
for (dest, source, l) in &self.tuples {
let mut todo = vec![];
while let Some((range_start, range_end)) = ranges.pop() {
let before_range = (range_start, range_end.min(*source));
if before_range.1 > before_range.0 {
todo.push(before_range);
}
let after_range = ((*source + l).max(range_start), range_end);
if after_range.1 > after_range.0 {
todo.push(after_range);
}
let between_range = (range_start.max(*source), (*source + *l).min(range_end));
if between_range.1 > between_range.0 {
mapped.push((
between_range.0 - source + dest,
between_range.1 - source + dest,
));
}
}
ranges = todo;
}
mapped.extend(ranges);
mapped
}
}
fn parse(input: &str) -> (Vec<usize>, Vec<Map>) {
let mut chunks = input.split("\n\n");
let seeds = chunks
.next()
.unwrap()
.u32s()
.map(|v| v as usize)
.collect::<Vec<_>>();
let maps = chunks
.map(|chunk| Map {
tuples: chunk
.lines()
.enumerate()
.filter_map(|(idx, line)| {
if idx == 0 {
return None;
}
let mut nums = line.u32s();
Some((
nums.next()? as usize,
nums.next()? as usize,
nums.next()? as usize,
))
})
.collect(),
})
.collect::<Vec<_>>();
(seeds, maps)
}
pub fn part1(input: &str) -> impl std::fmt::Display {
let (mut seeds, maps) = parse(input);
for seed in seeds.iter_mut() {
for map in &maps {
for (dest, source, l) in &map.tuples {
// if seed is within range map it in-place else leave it
if *seed >= *source && *seed < *source + *l {
*seed = *seed + *dest - *source;
break;
}
}
}
}
seeds.sort();
seeds[0]
}
pub fn part2(input: &str) -> impl std::fmt::Display {
let (seeds, maps) = parse(input);
let seeds = seeds.chunks(2).map(|v| (v[0], v[1]));
let mut res = seeds
.map(|(start, end)| {
let mut ranges = maps
.iter()
.fold(vec![(start, start + end)], |acc, map| map.calc_range(acc));
ranges.sort();
// We want the lowest, so take lower end of range
ranges[0].0
})
.collect::<Vec<_>>();
res.sort();
res[0]
}
static TEST_INPUT: &str = "seeds: 79 14 55 13
seed-to-soil map:
50 98 2
52 50 48
soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15
fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4
water-to-light map:
88 18 7
18 25 70
light-to-temperature map:
45 77 23
81 45 19
68 64 13
temperature-to-humidity map:
0 69 1
1 0 69
humidity-to-location map:
60 56 37
56 93 4";
#[test]
fn test_part1() {
assert_eq!("35", part1(TEST_INPUT).to_string())
}
#[test]
fn test_part2() {
assert_eq!("46", part2(TEST_INPUT).to_string())
}