day 5 part 2

This commit is contained in:
Dominic 2023-12-05 11:01:47 +01:00
parent 4437121bb0
commit 22ee9226c2
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
3 changed files with 192 additions and 26 deletions

16
Cargo.lock generated
View file

@ -35,6 +35,7 @@ dependencies = [
"bit-vec", "bit-vec",
"chumsky", "chumsky",
"indexmap", "indexmap",
"itertools",
"paste", "paste",
] ]
@ -79,6 +80,12 @@ dependencies = [
"stacker", "stacker",
] ]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
@ -105,6 +112,15 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.150" version = "0.2.150"

View file

@ -10,4 +10,5 @@ ariadne = "0.3"
bit-vec = "0.6" bit-vec = "0.6"
chumsky = "0.9" chumsky = "0.9"
indexmap = "2" indexmap = "2"
itertools = "0.12"
paste = "1" paste = "1"

View file

@ -3,29 +3,158 @@
use aoc23::read; use aoc23::read;
use chumsky::{prelude::*, text::int}; use chumsky::{prelude::*, text::int};
use itertools::Itertools as _;
use std::{
fmt::{self, Debug, Display},
ops::{Add, Deref, DerefMut}
};
struct Range { trait Newtype:
source_start: usize, Copy
target_start: usize, + Debug
+ Ord
+ From<usize>
+ Into<usize>
+ Deref<Target = usize>
+ DerefMut
+ Display
+ Add<usize, Output = Self>
{
}
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<S, T> {
source_start: S,
target_start: T,
len: usize len: usize
} }
struct Map { struct Map<S, T> {
ranges: Vec<Range> ranges: Vec<Range<S, T>>
} }
impl Map { impl<S, T> Map<S, T>
fn get(&self, source: usize) -> usize { where
S: Newtype,
T: Newtype
{
fn get(&self, source: S) -> T {
for r in &self.ranges { 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; continue;
}; };
if offset < r.len { 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<char, Self, Error = Simple<char>> { fn parser() -> impl Parser<char, Self, Error = Simple<char>> {
@ -38,8 +167,8 @@ impl Map {
ranges: ranges ranges: ranges
.into_iter() .into_iter()
.map(|ints: Vec<String>| Range { .map(|ints: Vec<String>| Range {
source_start: ints[1].parse().unwrap(), source_start: ints[1].parse::<usize>().unwrap().into(),
target_start: ints[0].parse().unwrap(), target_start: ints[0].parse::<usize>().unwrap().into(),
len: ints[2].parse().unwrap() len: ints[2].parse().unwrap()
}) })
.collect() .collect()
@ -48,25 +177,27 @@ impl Map {
} }
struct Almanac { struct Almanac {
seeds: Vec<usize>, seeds: Vec<Seed>,
seed_to_soil: Map, seed_to_soil: Map<Seed, Soil>,
soil_to_fertilizer: Map, soil_to_fertilizer: Map<Soil, Fertilizer>,
fertilizer_to_water: Map, fertilizer_to_water: Map<Fertilizer, Water>,
water_to_light: Map, water_to_light: Map<Water, Light>,
light_to_temperature: Map, light_to_temperature: Map<Light, Temperature>,
temperature_to_humidity: Map, temperature_to_humidity: Map<Temperature, Humidity>,
humidity_to_location: Map humidity_to_location: Map<Humidity, Location>
} }
impl Almanac { impl Almanac {
fn parser() -> impl Parser<char, Self, Error = Simple<char>> { fn parser() -> impl Parser<char, Self, Error = Simple<char>> {
fn parse_another_map<'a, P, T>( fn parse_another_map<'a, P, R, S, T>(
parser: P, parser: P,
name: &'a str name: &'a str
) -> impl Parser<char, (T, Map), Error = Simple<char>> + 'a ) -> impl Parser<char, (R, Map<S, T>), Error = Simple<char>> + 'a
where where
P: Parser<char, T, Error = Simple<char>> + 'a, P: Parser<char, R, Error = Simple<char>> + 'a,
T: 'a R: 'a,
S: Newtype + 'a,
T: Newtype + 'a
{ {
parser parser
.then_ignore(just(name)) .then_ignore(just(name))
@ -78,8 +209,8 @@ impl Almanac {
.ignore_then(int(10).separated_by(just(" "))) .ignore_then(int(10).separated_by(just(" ")))
.map(|ints: Vec<String>| { .map(|ints: Vec<String>| {
ints.into_iter() ints.into_iter()
.map(|int| int.parse().unwrap()) .map(|int| int.parse::<usize>().unwrap().into())
.collect::<Vec<usize>>() .collect::<Vec<Seed>>()
}) })
.then_ignore(just("\n\n")); .then_ignore(just("\n\n"));
let parser = parse_another_map(parser, "seed-to-soil").then_ignore(just("\n")); let parser = parse_another_map(parser, "seed-to-soil").then_ignore(just("\n"));
@ -152,5 +283,23 @@ fn main() -> anyhow::Result<()> {
.unwrap(); .unwrap();
println!("{min_loc}"); 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(()) Ok(())
} }