solution: day 5

This commit is contained in:
Matej Janezic 2023-12-05 07:41:42 +01:00
parent 0000010044
commit 00000110da
Signed by: janezicmatej
GPG Key ID: 4298E230ED37B2C0
2 changed files with 191 additions and 0 deletions

33
data/examples/05.txt Normal file
View File

@ -0,0 +1,33 @@
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

158
src/bin/05.rs Normal file
View File

@ -0,0 +1,158 @@
use std::{cmp::min, str::FromStr};
use aoc::parsers::to_vec;
#[derive(Debug)]
struct Mapping {
destination: u64,
source: u64,
range: u64,
}
struct ParseMappingError;
impl FromStr for Mapping {
type Err = ParseMappingError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let nums: Vec<u64> = to_vec(s, ' ');
Ok(Self {
destination: nums[0],
source: nums[1],
range: nums[2],
})
}
}
impl Mapping {
fn contains(&self, n: u64) -> bool {
n >= self.source && n < self.source + self.range
}
fn contains_any(&self, s: u64, r: u64) -> bool {
s < self.source + self.range && s + r > self.source
}
fn map(&self, n: u64) -> u64 {
debug_assert!(self.contains(n));
let shift = n - self.source;
self.destination + shift
}
fn map_range(&self, s: u64, r: u64) -> ((u64, u64), Vec<(u64, u64)>) {
debug_assert!(self.contains_any(s, r));
let shift = {
if self.contains(s) {
s - self.source
} else {
0
}
};
let neg_shift = {
if self.contains(s) {
0
} else {
self.source - s
}
};
let dest_start = self.destination + shift;
let dest_range = min(r - neg_shift, self.range - shift);
let mut rest = Vec::new();
if !self.contains(s) {
rest.push((s, self.source - s));
}
if r - neg_shift > dest_range {
rest.push((self.source + self.range, r - dest_range))
}
((dest_start, dest_range), rest)
}
}
fn parse_map(maps: &str) -> Vec<Vec<Mapping>> {
let mut res = Vec::new();
for mapper in maps.split("\n\n") {
res.push(
mapper
.lines()
.skip(1)
.filter_map(|m| m.parse().ok())
.collect(),
)
}
res
}
pub fn part_one(input: &str) -> Option<u64> {
let (first, rest) = input.split_once("\n\n").unwrap();
let maps = parse_map(rest);
let seeds = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' ');
let mut m = u64::MAX;
for seed in seeds.iter() {
let mut s = *seed;
'maps: for map in maps.iter() {
for inner in map.iter() {
if inner.contains(s) {
s = inner.map(s);
continue 'maps;
}
}
}
m = min(m, s)
}
Some(m)
}
pub fn part_two(input: &str) -> Option<u64> {
let (first, rest) = input.split_once("\n\n").unwrap();
let maps = parse_map(rest);
let seeds = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' ');
let mut seeds_ranges: Vec<_> = Vec::new();
for s in seeds.chunks(2) {
seeds_ranges.push((s[0], s[1]))
}
for mapping in maps.iter() {
let mut new_ranges = Vec::new();
'queue: while let Some((s, r)) = seeds_ranges.pop() {
for map in mapping {
if map.contains_any(s, r) {
let (rng, rest) = map.map_range(s, r);
new_ranges.push(rng);
seeds_ranges.extend_from_slice(&rest);
continue 'queue;
}
}
new_ranges.push((s, r));
}
seeds_ranges = new_ranges;
}
seeds_ranges.iter().map(|(x, _)| *x).min()
}
aoc::solution!(5);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(part_one(&aoc::template::read_file("examples", 5)), Some(35));
}
#[test]
fn test_part_two() {
assert_eq!(part_two(&aoc::template::read_file("examples", 5)), Some(46));
}
}