From 15fccb9f5ed31e2e85aeffae69390145166ba941 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Sun, 17 Dec 2023 18:09:44 +0100 Subject: [PATCH] day(17): add solution Signed-off-by: Matej Focko --- samples/day17.txt | 13 +++++ src/bin/day17.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 samples/day17.txt create mode 100644 src/bin/day17.rs diff --git a/samples/day17.txt b/samples/day17.txt new file mode 100644 index 0000000..f400d6e --- /dev/null +++ b/samples/day17.txt @@ -0,0 +1,13 @@ +2413432311323 +3215453535623 +3255245654254 +3446585845452 +4546657867536 +1438598798454 +4457876987766 +3637877979653 +4654967986887 +4564679986453 +1224686865563 +2546548887735 +4322674655533 diff --git a/src/bin/day17.rs b/src/bin/day17.rs new file mode 100644 index 0000000..8e7457f --- /dev/null +++ b/src/bin/day17.rs @@ -0,0 +1,135 @@ +use std::collections::HashSet; + +use aoc_2023::*; + +type Output1 = u32; +type Output2 = Output1; + +fn left(v: &Vector2D) -> Vector2D { + Vector2D::new(v.y(), -v.x()) +} + +fn right(v: &Vector2D) -> Vector2D { + Vector2D::new(-v.y(), v.x()) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct State { + heat_loss: u32, + distance: isize, + position: Vector2D, + direction: Vector2D, +} + +impl State { + fn step(&self, m: &[Vec], direction: Vector2D, steps: isize) -> State { + let position = self.position + (direction * steps); + let distance = self.distance + steps; + let heat_loss = self.heat_loss + + (1..=steps) + .map(|s| m[self.position + direction * s]) + .sum::(); + + State { + heat_loss, + distance, + position, + direction, + } + } +} + +struct Day17 { + map: Vec>, +} + +impl Day17 { + fn astar(&self, min_steps: isize, max_steps: isize) -> u32 { + let (width, height) = (self.map[0].len(), self.map.len()); + let goal = Vector2D::new(width as isize - 1, height as isize - 1); + + let mut queue: MinHeap = MinHeap::new(); + queue.push(State { + heat_loss: 0, + distance: 0, + position: Vector2D::new(0, 0), + direction: Vector2D::new(1, 0), + }); + queue.push(State { + heat_loss: 0, + distance: 0, + position: Vector2D::new(0, 0), + direction: Vector2D::new(0, 1), + }); + + let mut seen: HashSet<(Vector2D, Vector2D)> = HashSet::new(); + while let Some(s) = queue.pop() { + if s.position == goal { + return s.heat_loss; + } + + if seen.contains(&(s.position, s.direction)) { + continue; + } + seen.insert((s.position, s.direction)); + + for steps in min_steps..=max_steps { + for d in [left(&s.direction), right(&s.direction)] { + if !in_range(&self.map, &(s.position + d * steps)) { + continue; + } + + queue.push(s.step(&self.map, d, steps)); + } + } + } + + unreachable!() + } +} + +impl Solution for Day17 { + fn new>(pathname: P) -> Self { + Self { + map: file_to_string(pathname) + .lines() + .map(|l| l.chars().map(|c| c.to_digit(10).unwrap()).collect()) + .collect(), + } + } + + fn part_1(&mut self) -> Output1 { + self.astar(1, 3) + } + + fn part_2(&mut self) -> Output2 { + self.astar(4, 10) + } +} + +fn main() -> Result<()> { + Day17::main() +} + +test_sample!(day_17, Day17, 102, 94); + +#[cfg(test)] +mod day_17_extended { + use super::*; + + #[test] + fn test_left_rotate() { + assert_eq!(left(&Vector2D::new(1, 0)), Vector2D::new(0, -1)); + assert_eq!(left(&Vector2D::new(0, -1)), Vector2D::new(-1, 0)); + assert_eq!(left(&Vector2D::new(-1, 0)), Vector2D::new(0, 1)); + assert_eq!(left(&Vector2D::new(0, 1)), Vector2D::new(1, 0)); + } + + #[test] + fn test_right_rotate() { + assert_eq!(right(&Vector2D::new(1, 0)), Vector2D::new(0, 1)); + assert_eq!(right(&Vector2D::new(0, -1)), Vector2D::new(1, 0)); + assert_eq!(right(&Vector2D::new(-1, 0)), Vector2D::new(0, -1)); + assert_eq!(right(&Vector2D::new(0, 1)), Vector2D::new(-1, 0)); + } +}