From 22ee9226c22bb57a4378bae58ac270ea15f2c8bd Mon Sep 17 00:00:00 2001 From: Dominic Date: Tue, 5 Dec 2023 11:01:47 +0100 Subject: [PATCH] day 5 part 2 --- Cargo.lock | 16 ++++ Cargo.toml | 1 + src/bin/day5.rs | 201 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 192 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e548566..64b3d2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ dependencies = [ "bit-vec", "chumsky", "indexmap", + "itertools", "paste", ] @@ -79,6 +80,12 @@ dependencies = [ "stacker", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "equivalent" version = "1.0.1" @@ -105,6 +112,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "libc" version = "0.2.150" diff --git a/Cargo.toml b/Cargo.toml index fa9f14d..1227dce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ ariadne = "0.3" bit-vec = "0.6" chumsky = "0.9" indexmap = "2" +itertools = "0.12" paste = "1" diff --git a/src/bin/day5.rs b/src/bin/day5.rs index 0867119..3264cd9 100644 --- a/src/bin/day5.rs +++ b/src/bin/day5.rs @@ -3,29 +3,158 @@ use aoc23::read; use chumsky::{prelude::*, text::int}; +use itertools::Itertools as _; +use std::{ + fmt::{self, Debug, Display}, + ops::{Add, Deref, DerefMut} +}; -struct Range { - source_start: usize, - target_start: usize, +trait Newtype: + Copy + + Debug + + Ord + + From + + Into + + Deref + + DerefMut + + Display + + Add +{ +} + +macro_rules! newtype { + ($( struct $newtype:ident($inner:ident);)*) => { + $( + #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] + struct $newtype($inner); + + impl From<$inner> for $newtype { + fn from(inner: $inner) -> Self { + Self(inner) + } + } + + impl From<$newtype> for $inner { + fn from(newtype: $newtype) -> Self { + newtype.0 + } + } + + impl Deref for $newtype { + type Target = $inner; + + fn deref(&self) -> &$inner { + &self.0 + } + } + + impl DerefMut for $newtype { + fn deref_mut(&mut self) -> &mut $inner { + &mut self.0 + } + } + + impl Display for $newtype { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } + } + + impl Add<$inner> for $newtype { + type Output = $newtype; + + fn add(self, rhs: $inner) -> Self { + Self(self.0 + rhs) + } + } + + impl Newtype for $newtype {} + )* + }; +} + +newtype! { + struct Seed(usize); + struct Soil(usize); + struct Fertilizer(usize); + struct Water(usize); + struct Light(usize); + struct Temperature(usize); + struct Humidity(usize); + struct Location(usize); +} + +#[derive(Debug)] +struct Range { + source_start: S, + target_start: T, len: usize } -struct Map { - ranges: Vec +struct Map { + ranges: Vec> } -impl Map { - fn get(&self, source: usize) -> usize { +impl Map +where + S: Newtype, + T: Newtype +{ + fn get(&self, source: S) -> T { for r in &self.ranges { - let Some(offset) = source.checked_sub(r.source_start) else { + let Some(offset) = source.checked_sub(r.source_start.into()) else { continue; }; if offset < r.len { - return r.target_start + offset; + return (r.target_start.into() + offset).into(); } } - source + source.into().into() + } + + fn get_range_impl(&self, start: S, len: usize) -> Vec<(T, usize)> { + let mut result = Vec::new(); + for r in &self.ranges { + let possible_start = start.max(r.source_start); + let possible_end = (start + len).min(r.source_start + r.len); + if possible_start < possible_end { + let range = ( + r.target_start + (possible_start.into() - r.source_start.into()), + possible_end.into() - possible_start.into() + ); + // eprintln!("({start:?}, {len}) => {range:?} ({r:?}"); + result.push(range); + + if start < possible_start { + result.extend( + self.get_range_impl(start, possible_start.into() - start.into()) + ); + } + if (start + len) > possible_end { + result.extend(self.get_range_impl( + possible_end, + start.into() + len - possible_end.into() + )); + } + } + } + + if result.is_empty() { + result.push((start.into().into(), len)); + } + + result + } + + fn get_range(&self, (start, len): (S, usize)) -> Vec<(T, usize)> { + let result = self.get_range_impl(start, len); + + // for range in &result { + // eprintln!("({start:?}, {len}) => {range:?}"); + // } + + result } fn parser() -> impl Parser> { @@ -38,8 +167,8 @@ impl Map { ranges: ranges .into_iter() .map(|ints: Vec| Range { - source_start: ints[1].parse().unwrap(), - target_start: ints[0].parse().unwrap(), + source_start: ints[1].parse::().unwrap().into(), + target_start: ints[0].parse::().unwrap().into(), len: ints[2].parse().unwrap() }) .collect() @@ -48,25 +177,27 @@ impl Map { } struct Almanac { - seeds: Vec, - 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 + seeds: Vec, + 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> { - fn parse_another_map<'a, P, T>( + fn parse_another_map<'a, P, R, S, T>( parser: P, name: &'a str - ) -> impl Parser> + 'a + ) -> impl Parser), Error = Simple> + 'a where - P: Parser> + 'a, - T: 'a + P: Parser> + 'a, + R: 'a, + S: Newtype + 'a, + T: Newtype + 'a { parser .then_ignore(just(name)) @@ -78,8 +209,8 @@ impl Almanac { .ignore_then(int(10).separated_by(just(" "))) .map(|ints: Vec| { ints.into_iter() - .map(|int| int.parse().unwrap()) - .collect::>() + .map(|int| int.parse::().unwrap().into()) + .collect::>() }) .then_ignore(just("\n\n")); let parser = parse_another_map(parser, "seed-to-soil").then_ignore(just("\n")); @@ -152,5 +283,23 @@ fn main() -> anyhow::Result<()> { .unwrap(); println!("{min_loc}"); + let min_loc = almanac + .seeds + .iter() + .copied() + .tuples() + .map(|(start, len)| (start, len.into())) + .flat_map(|range| almanac.seed_to_soil.get_range(range)) + .flat_map(|range| almanac.soil_to_fertilizer.get_range(range)) + .flat_map(|range| almanac.fertilizer_to_water.get_range(range)) + .flat_map(|range| almanac.water_to_light.get_range(range)) + .flat_map(|range| almanac.light_to_temperature.get_range(range)) + .flat_map(|range| almanac.temperature_to_humidity.get_range(range)) + .flat_map(|range| almanac.humidity_to_location.get_range(range)) + .map(|(start, _)| start) + .min() + .unwrap(); + println!("{min_loc}"); + Ok(()) }