aoc23/src/bin/day5.rs
2023-12-05 10:03:20 +01:00

156 lines
3.6 KiB
Rust

#![allow(clippy::let_and_return)]
#![forbid(elided_lifetimes_in_paths, unsafe_code)]
use aoc23::read;
use chumsky::{prelude::*, text::int};
struct Range {
source_start: usize,
target_start: usize,
len: usize
}
struct Map {
ranges: Vec<Range>
}
impl Map {
fn get(&self, source: usize) -> usize {
for r in &self.ranges {
let Some(offset) = source.checked_sub(r.source_start) else {
continue;
};
if offset < r.len {
return r.target_start + offset;
}
}
source
}
fn parser() -> impl Parser<char, Self, Error = Simple<char>> {
int(10)
.separated_by(just(" "))
.exactly(3)
.then_ignore(just("\n"))
.repeated()
.map(|ranges| Self {
ranges: ranges
.into_iter()
.map(|ints: Vec<String>| Range {
source_start: ints[1].parse().unwrap(),
target_start: ints[0].parse().unwrap(),
len: ints[2].parse().unwrap()
})
.collect()
})
}
}
struct Almanac {
seeds: Vec<usize>,
seed_to_soil: Map,
soil_to_fertilizer: Map,
fertilizer_to_water: Map,
water_to_light: Map,
light_to_temperature: Map,
temperature_to_humidity: Map,
humidity_to_location: Map
}
impl Almanac {
fn parser() -> impl Parser<char, Self, Error = Simple<char>> {
fn parse_another_map<'a, P, T>(
parser: P,
name: &'a str
) -> impl Parser<char, (T, Map), Error = Simple<char>> + 'a
where
P: Parser<char, T, Error = Simple<char>> + 'a,
T: 'a
{
parser
.then_ignore(just(name))
.then_ignore(just(" map:\n"))
.then(Map::parser())
}
let parser = just("seeds: ")
.ignore_then(int(10).separated_by(just(" ")))
.map(|ints: Vec<String>| {
ints.into_iter()
.map(|int| int.parse().unwrap())
.collect::<Vec<usize>>()
})
.then_ignore(just("\n\n"));
let parser = parse_another_map(parser, "seed-to-soil").then_ignore(just("\n"));
let parser =
parse_another_map(parser, "soil-to-fertilizer").then_ignore(just("\n"));
let parser =
parse_another_map(parser, "fertilizer-to-water").then_ignore(just("\n"));
let parser = parse_another_map(parser, "water-to-light").then_ignore(just("\n"));
let parser =
parse_another_map(parser, "light-to-temperature").then_ignore(just("\n"));
let parser =
parse_another_map(parser, "temperature-to-humidity").then_ignore(just("\n"));
let parser = parse_another_map(parser, "humidity-to-location");
parser.map(
|(
(
(
(
(
((seeds, seed_to_soil), soil_to_fertilizer),
fertilizer_to_water
),
water_to_light
),
light_to_temperature
),
temperature_to_humidity
),
humidity_to_location
)| {
Almanac {
seeds,
seed_to_soil,
soil_to_fertilizer,
fertilizer_to_water,
water_to_light,
light_to_temperature,
temperature_to_humidity,
humidity_to_location
}
}
)
}
}
fn parser() -> impl Parser<char, Almanac, Error = Simple<char>> {
Almanac::parser().then_ignore(end())
}
fn main() -> anyhow::Result<()> {
let almanac = read("inputs/day5.txt", parser())?;
let min_loc = almanac
.seeds
.iter()
.copied()
.map(|seed| {
let soil = almanac.seed_to_soil.get(seed);
let fertilizer = almanac.soil_to_fertilizer.get(soil);
let water = almanac.fertilizer_to_water.get(fertilizer);
let light = almanac.water_to_light.get(water);
let temperature = almanac.light_to_temperature.get(light);
let humidity = almanac.temperature_to_humidity.get(temperature);
let location = almanac.humidity_to_location.get(humidity);
// eprintln!("{seed}, {soil}, {fertilizer}, {water}, {light}, {temperature}, {humidity}, {location}");
location
})
.min()
.unwrap();
println!("{min_loc}");
Ok(())
}