diff --git a/src/day17.rs b/src/day17.rs new file mode 100644 index 0000000..3f428c0 --- /dev/null +++ b/src/day17.rs @@ -0,0 +1,115 @@ +use std::{collections::BinaryHeap, cmp::Reverse}; + +use aoc_runner_derive::{aoc, aoc_generator}; + +#[derive(Debug)] +struct Grid { + data: Box<[u8]>, + offset: usize +} + +impl Grid { + fn from_str(s: &str) -> Self { + let mut lines = s.lines().peekable(); + let line_len = lines.peek().map_or(0, |line| line.len()); + Self { + data: lines.flat_map(str::as_bytes).map(|&char| char - b'0').collect::>(), + offset: line_len + } + } + + fn next_pos(&self, p: usize, dir: u8) -> Option { + Some(match dir { + 0 if p > self.offset => p - self.offset, + 1 if (p + 1) % self.offset != 0 => p + 1, + 2 if p < self.data.len() - self.offset => p + self.offset, + 3 if p % self.offset != 0 => p - 1, + _ => { return None } + }) + } + + fn run(&self, dmin: usize, dmax: usize) -> Option { + let lp = self.data.len() - 1; + let mut visit = vec![0u8; self.data.len()]; + let mut ccache = vec![usize::MAX; 2 * self.data.len()]; + let mut q = BinaryHeap::new(); + q.push((Reverse(0), 0, 0)); + q.push((Reverse(0), 0, 1)); + + while let Some((Reverse(cost), p, dir)) = q.pop() { + if p == lp { + return Some(cost) + } + if visit[p] & (1u8 << dir) != 0 { + continue; + } + visit[p] |= 1u8 << dir; + let odir = dir ^ 1; + for nd in [odir, odir ^ 2] { + let mut costsum = 0; + let mut np = p; + for dist in 1..=dmax { + if let Some(op) = self.next_pos(np, nd) { + costsum += self.data[op] as usize; + if dist >= dmin { + let ncost = cost + costsum; + let cache_idx = (op << 1) | odir as usize; + if ccache[cache_idx] > ncost { + ccache[cache_idx] = ncost; + q.push((Reverse(ncost), op, odir)); + } + } + np = op; + } + } + } + } + None + } +} + + +#[aoc_generator(day17)] +fn parse(input: &str) -> Grid { + Grid::from_str(input) +} + +#[aoc(day17, part1)] +fn part1(input: &Grid) -> usize { + input.run(1, 3).unwrap() +} + +#[aoc(day17, part2)] +fn part2(input: &Grid) -> usize { + input.run(4, 10).unwrap() +} + + +#[cfg(test)] +mod tests { + use super::*; + + const EX: &str = r"2413432311323 +3215453535623 +3255245654254 +3446585845452 +4546657867536 +1438598798454 +4457876987766 +3637877979653 +4654967986887 +4564679986453 +1224686865563 +2546548887735 +4322674655533"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 102); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 94); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index bb09eb8..e9ec93f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod day17; mod day16; mod day15; mod day14;