From 907593e2e192c857a039e023a3ce8ea668d364fe Mon Sep 17 00:00:00 2001 From: missing Date: Wed, 14 Dec 2022 19:49:51 -0600 Subject: [PATCH] day 12 --- src/days/day12.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 133 insertions(+) create mode 100644 src/days/day12.rs diff --git a/src/days/day12.rs b/src/days/day12.rs new file mode 100644 index 0000000..a95ae67 --- /dev/null +++ b/src/days/day12.rs @@ -0,0 +1,132 @@ +use std::{ + cmp::{Ordering, Reverse}, + collections::{BinaryHeap, HashMap}, +}; + +use aoc_runner_derive::{aoc, aoc_generator}; + +struct PuzzleInput { + heights: Vec>, + start: (usize, usize), + end: (usize, usize), +} + +#[aoc_generator(day12)] +fn generator(input: &str) -> PuzzleInput { + let mut start = None; + let mut end = None; + + let nodes = input + .lines() + .enumerate() + .map(|(i, l)| { + l.chars() + .enumerate() + .map(|(j, c)| match c { + 'S' => { + assert!(start.replace((i, j)).is_none()); + 1 + } + 'E' => { + assert!(end.replace((i, j)).is_none()); + 26 + } + 'a'..='z' => c as u8 - b'a' + 1, + _ => panic!(), + }) + .collect() + }) + .collect(); + + PuzzleInput { + heights: nodes, + start: start.unwrap(), + end: end.unwrap(), + } +} + +fn neighbours(coord: (usize, usize), heights: &[Vec]) -> impl Iterator { + let current_height = heights[coord.0][coord.1]; + + let a = (coord.0 + 1, coord.1); + let a = heights + .get(a.0) + .and_then(|v| (v[a.1] >= current_height - 1).then_some(a)); + + let b = coord.0.checked_sub(1).map(|v| (v, coord.1)); + let b = b.filter(|b| heights[b.0][b.1] >= current_height - 1); + + let c = (coord.0, coord.1 + 1); + let c = heights[c.0] + .get(c.1) + .and_then(|v| (*v >= current_height - 1).then_some(c)); + + let d = coord.1.checked_sub(1).map(|v| (coord.0, v)); + let d = d.filter(|d| heights[d.0][d.1] >= current_height - 1); + + a.into_iter().chain(b).chain(c).chain(d) +} + +struct CmpByFirst(T, U); + +impl PartialEq for CmpByFirst { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl Eq for CmpByFirst {} + +impl PartialOrd for CmpByFirst { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for CmpByFirst { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +fn fill_dist_map(input: &PuzzleInput) -> HashMap<(usize, usize), u32> { + let mut to_visit = BinaryHeap::new(); + to_visit.push(CmpByFirst(Reverse(0), input.end)); + + let mut map = HashMap::new(); + map.insert(input.end, 0); + + while let Some(CmpByFirst(Reverse(dist), coord)) = to_visit.pop() { + for neighbour in neighbours(coord, &input.heights) { + map.entry(neighbour) + .and_modify(|v| *v = (*v).min(dist + 1)) + .or_insert_with(|| { + to_visit.push(CmpByFirst(Reverse(dist + 1), neighbour)); + + dist + 1 + }); + } + } + + map +} + +#[aoc(day12, part1)] +fn part1(input: &PuzzleInput) -> u32 { + fill_dist_map(input)[&input.start] +} + +#[aoc(day12, part2)] +fn part2(input: &PuzzleInput) -> u32 { + let map = fill_dist_map(input); + + input + .heights + .iter() + .enumerate() + .flat_map(|(i, v)| v.iter().enumerate().map(move |(j, v)| ((i, j), v))) + .filter_map(|(coord, h)| (*h == 1).then_some(coord)) + .map(|coord| *map.get(&coord).unwrap_or(&u32::MAX)) + .min() + .unwrap() +} diff --git a/src/lib.rs b/src/lib.rs index 59c9d65..25e5eb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ mod days { // curse you, rustfmt mod day10; mod day11; + mod day12; } aoc_lib! { year = 2022 }