Skip to content

Commit

Permalink
day 5 (slow)
Browse files Browse the repository at this point in the history
  • Loading branch information
asmello committed Dec 5, 2023
1 parent 3d56a76 commit 0f6e456
Show file tree
Hide file tree
Showing 6 changed files with 443 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/day2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub mod chumsky;
pub mod manual;

// default implementation
pub use chumsky::{part1, part2};
pub use self::chumsky::{part1, part2};

#[derive(Debug, Default, PartialEq, Eq)]
struct Game {
Expand Down
183 changes: 183 additions & 0 deletions src/day5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
use chumsky::{
error::Simple,
primitive::{end, just},
text::{self, newline, whitespace, TextParser},
Parser,
};
use eyre::{eyre, Context};
use std::{collections::BTreeMap, str::FromStr};

pub fn part1(input: &str) -> eyre::Result<usize> {
let almanac: Almanac = input.parse().wrap_err("failed to parse almanac")?;
almanac
.seeds
.iter()
.copied()
.map(|seed| almanac.seed_to_location(seed))
.min()
.ok_or_else(|| eyre!("empty almanac"))
}

pub fn part2(input: &str) -> eyre::Result<usize> {
let almanac: Almanac = input.parse().wrap_err("failed to parse almanac")?;
// TODO: optimize using range arithmetic... this is correct but, uh, slow
almanac
.seeds()
.map(|seed| almanac.seed_to_location(seed))
.min()
.ok_or_else(|| eyre!("empty almanac"))
}

#[derive(Default, Debug)]
struct Almanac {
seeds: Vec<usize>,
seed_to_soil_map: BTreeMap<usize, Span>,
soil_to_fertilizer_map: BTreeMap<usize, Span>,
fertilizer_to_water_map: BTreeMap<usize, Span>,
water_to_light_map: BTreeMap<usize, Span>,
light_to_temperature_map: BTreeMap<usize, Span>,
temperature_to_humidity_map: BTreeMap<usize, Span>,
humidity_to_location_map: BTreeMap<usize, Span>,
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Span {
start: usize,
length: usize,
}

impl Almanac {
fn seeds(&self) -> impl Iterator<Item = usize> + '_ {
self.seeds.chunks_exact(2).flat_map(|pair| {
let (initial, length) = (pair[0], pair[1]);
initial..initial + length
})
}
fn seed_to_location(&self, seed: usize) -> usize {
let next = Self::apply(&self.seed_to_soil_map, seed);
let next = Self::apply(&self.soil_to_fertilizer_map, next);
let next = Self::apply(&self.fertilizer_to_water_map, next);
let next = Self::apply(&self.water_to_light_map, next);
let next = Self::apply(&self.light_to_temperature_map, next);
let next = Self::apply(&self.temperature_to_humidity_map, next);
Self::apply(&self.humidity_to_location_map, next)
}
fn apply(map: &BTreeMap<usize, Span>, elem: usize) -> usize {
if let Some((&k, v)) = map.range(..=elem).last() {
if k <= elem && k + v.length > elem {
v.start + elem - k // elem is in span
} else {
elem // elem is in cover gap
}
} else {
elem // elem is greater than covered maximum
}
}
}

impl FromStr for Almanac {
type Err = eyre::Report;

fn from_str(s: &str) -> Result<Self, Self::Err> {
parser()
.parse(s)
.map_err(|errors| eyre!(errors.into_iter().next().unwrap()))
}
}

fn parser() -> impl Parser<char, Almanac, Error = Simple<char>> {
let integer = text::int(10).from_str::<usize>().unwrapped();

let seeds = just("seeds: ")
.ignore_then(integer.separated_by(just(' ')))
.padded();

let triple = integer.separated_by(whitespace()).exactly(3).map(|elems| {
(
elems[1],
Span {
start: elems[0],
length: elems[2],
},
)
});

let seed_to_soil_map = just("seed-to-soil map:")
.padded()
.ignore_then(triple.separated_by(newline()))
.padded()
.collect::<BTreeMap<_, _>>();

let soil_to_fertilizer_map = just("soil-to-fertilizer map:")
.padded()
.ignore_then(triple.separated_by(newline()))
.padded()
.collect::<BTreeMap<_, _>>();

let fertilizer_to_water_map = just("fertilizer-to-water map:")
.padded()
.ignore_then(triple.separated_by(newline()))
.padded()
.collect::<BTreeMap<_, _>>();

let water_to_light_map = just("water-to-light map:")
.padded()
.ignore_then(triple.separated_by(newline()))
.padded()
.collect::<BTreeMap<_, _>>();

let light_to_temperature_map = just("light-to-temperature map:")
.padded()
.ignore_then(triple.separated_by(newline()))
.padded()
.collect::<BTreeMap<_, _>>();

let temperature_to_humidity_map = just("temperature-to-humidity map:")
.padded()
.ignore_then(triple.separated_by(newline()))
.padded()
.collect::<BTreeMap<_, _>>();

let humidity_to_location_map = just("humidity-to-location map:")
.padded()
.ignore_then(triple.separated_by(newline()))
.padded()
.collect::<BTreeMap<_, _>>();

seeds
.then(seed_to_soil_map)
.then(soil_to_fertilizer_map)
.then(fertilizer_to_water_map)
.then(water_to_light_map)
.then(light_to_temperature_map)
.then(temperature_to_humidity_map)
.then(humidity_to_location_map)
.then_ignore(end())
.map(
|(
(
(
(
(
((seeds, 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,
)| Almanac {
seeds,
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,
},
)
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod day1;
pub mod day2;
pub mod day3;
pub mod day4;
pub mod day5;
5 changes: 5 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ pub fn read(path: &str) -> std::io::Result<impl Read> {
let path = Path::new("./tests/resources/").join(path);
std::fs::File::open(path)
}

pub fn read_string(path: &str) -> std::io::Result<String> {
let path = Path::new("./tests/resources/").join(path);
std::fs::read_to_string(path)
}
66 changes: 66 additions & 0 deletions tests/day5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
mod common;
use aoc2023::day5;
use indoc::indoc;

const SAMPLE: &str = indoc! { r#"
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
"# };
const INPUT_PATH: &str = "day5/input.txt";

#[test]
fn part1_sample() {
let result = day5::part1(SAMPLE).unwrap();
assert_eq!(result, 35);
}

#[test]
fn part1_input() {
let input = common::read_string(INPUT_PATH).unwrap();
let result = day5::part1(&input).unwrap();
assert_eq!(result, 535088217);
}

#[test]
fn part2_sample() {
let result = day5::part2(SAMPLE).unwrap();
assert_eq!(result, 46);
}

#[test]
fn part2_input() {
let input = common::read_string(INPUT_PATH).unwrap();
let result = day5::part2(&input).unwrap();
assert_eq!(result, 51399228);
}
Loading

0 comments on commit 0f6e456

Please sign in to comment.