From 000002004770803e6e37af6db916ac55081815bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Jane=C5=BEi=C4=8D?= Date: Mon, 16 Dec 2024 16:04:29 +0100 Subject: [PATCH] solution: day 16 --- data/examples/16.txt | 17 ++++++ src/bin/16.rs | 119 ++++++++++++++++++++++++++++++++++++++ src/grid_vec/direction.rs | 45 ++++++++++++++ src/grid_vec/grid.rs | 66 +++++++++++++++++++++ src/grid_vec/mod.rs | 9 +++ src/grid_vec/point.rs | 91 +++++++++++++++++++++++++++++ src/lib.rs | 1 + 7 files changed, 348 insertions(+) create mode 100644 data/examples/16.txt create mode 100644 src/bin/16.rs create mode 100644 src/grid_vec/direction.rs create mode 100644 src/grid_vec/grid.rs create mode 100644 src/grid_vec/mod.rs create mode 100644 src/grid_vec/point.rs diff --git a/data/examples/16.txt b/data/examples/16.txt new file mode 100644 index 000000000..bc61c57 --- /dev/null +++ b/data/examples/16.txt @@ -0,0 +1,17 @@ +################# +#...#...#...#..E# +#.#.#.#.#.#.#.#.# +#.#.#.#...#...#.# +#.#.#.#.###.#.#.# +#...#.#.#.....#.# +#.#.#.#.#.#####.# +#.#...#.#.#.....# +#.#.#####.#.###.# +#.#.#.......#...# +#.#.###.#####.### +#.#.#...#.....#.# +#.#.#.#####.###.# +#.#.#.........#.# +#.#.#.#########.# +#S#.............# +################# diff --git a/src/bin/16.rs b/src/bin/16.rs new file mode 100644 index 000000000..c2f1cd8 --- /dev/null +++ b/src/bin/16.rs @@ -0,0 +1,119 @@ +use std::{cmp::Reverse, collections::BinaryHeap}; + +use aoc::grid_vec::{Direction, Grid, Point}; +use hashbrown::{HashMap, HashSet}; + +fn parse_input(input: &str) -> (Grid, Point, Point) { + let mut grid: Grid = input.parse().unwrap(); + let start = grid.find(b'S').unwrap(); + let end = grid.find(b'E').unwrap(); + + grid[start] = b'.'; + grid[end] = b'.'; + + (grid, start, end) +} + +type Prev = HashMap<(Point, Direction), HashSet<(Point, Direction)>>; +type Visited = HashMap<(Point, Direction), usize>; + +fn dijkstra(grid: &Grid, start: Point) -> (Visited, Prev) { + let mut prev: Prev = HashMap::new(); + let mut visited: Visited = HashMap::new(); + + let mut bh = BinaryHeap::new(); + bh.push(Reverse((0, start, Direction::EAST, start, Direction::EAST))); + + while let Some(Reverse((len, loc, dir, ploc, pdir))) = bh.pop() { + let best = visited.entry((loc, dir)).or_insert(usize::MAX); + + if len > *best { + continue; + } + + prev.entry((loc, dir)).or_default().insert((ploc, pdir)); + + *best = len; + + let n = loc + dir; + + if grid.get(&n).filter(|&&c| c == b'.').is_some() && !visited.contains_key(&(n, dir)) { + bh.push(Reverse((len + 1, n, dir, loc, dir))); + } + + let rl = dir.rotate_left(); + if !visited.contains_key(&(loc, rl)) { + bh.push(Reverse((len + 1000, loc, rl, loc, dir))); + } + + let rr = dir.rotate_right(); + if !visited.contains_key(&(loc, rr)) { + bh.push(Reverse((len + 1000, loc, rr, loc, dir))); + } + } + + (visited, prev) +} + +pub fn part_one(input: &str) -> Option { + let (grid, start, end) = parse_input(input); + dijkstra(&grid, start) + .0 + .into_iter() + .filter(|(k, _)| k.0 == end) + .map(|(_, v)| v) + .min() +} + +pub fn part_two(input: &str) -> Option { + let (grid, start, end) = parse_input(input); + + let (visited, prev) = dijkstra(&grid, start); + + let mut stack = Vec::new(); + stack.push( + prev.keys() + .filter(|&&k| k.0 == end) + .min_by_key(|&k| visited.get(k)) + .copied() + .unwrap(), + ); + + let mut visited = HashSet::new(); + let mut seats = HashSet::new(); + + while let Some((loc, dir)) = stack.pop() { + if !visited.insert((loc, dir)) { + continue; + } + + if let Some(prevs) = prev.get(&(loc, dir)) { + stack.extend(prevs); + } + + seats.insert(loc); + } + + seats.len().into() +} + +aoc::solution!(16); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_part_one() { + assert_eq!( + part_one(&aoc::template::read_file("examples", 16)), + Some(11048) + ); + } + #[test] + fn test_part_two() { + assert_eq!( + part_two(&aoc::template::read_file("examples", 16)), + Some(64) + ); + } +} diff --git a/src/grid_vec/direction.rs b/src/grid_vec/direction.rs new file mode 100644 index 000000000..4a78d57 --- /dev/null +++ b/src/grid_vec/direction.rs @@ -0,0 +1,45 @@ +use std::fmt::Display; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub struct Direction { + pub i: isize, + pub j: isize, +} + +impl Display for Direction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {})", self.i, self.j) + } +} + +impl Direction { + pub const NORTH: Self = Direction { i: -1, j: 0 }; + pub const EAST: Self = Direction { i: 0, j: 1 }; + pub const SOUTH: Self = Direction { i: 1, j: 0 }; + pub const WEST: Self = Direction { i: 0, j: -1 }; + + pub fn new(i: isize, j: isize) -> Self { + Self { i, j } + } + + pub fn rotate_left(self) -> Self { + Self { + i: -self.j, + j: self.i, + } + } + + pub fn rotate_right(self) -> Self { + Self { + i: self.j, + j: -self.i, + } + } +} + +#[macro_export] +macro_rules! dir { + ($i:literal, $j:literal) => { + Direction { i: $i, j: $j } + }; +} diff --git a/src/grid_vec/grid.rs b/src/grid_vec/grid.rs new file mode 100644 index 000000000..a734684 --- /dev/null +++ b/src/grid_vec/grid.rs @@ -0,0 +1,66 @@ +use std::{ + fmt::Display, + ops::{Index, IndexMut}, + str::FromStr, +}; + +use super::Point; + +#[derive(Debug, Clone)] +pub struct Grid { + grid: Vec>, +} + +impl FromStr for Grid { + type Err = (); + fn from_str(s: &str) -> Result { + let grid = s.lines().map(|x| x.as_bytes().to_vec()).collect(); + Ok(Self { grid }) + } +} + +impl Display for Grid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for row in self.grid.iter() { + for cell in row.iter() { + write!(f, "{}", *cell as char)?; + } + writeln!(f)?; + } + Ok(()) + } +} + +impl Index for Grid { + type Output = u8; + fn index(&self, index: Point) -> &Self::Output { + &self.grid[index.i][index.j] + } +} + +impl IndexMut for Grid { + fn index_mut(&mut self, index: Point) -> &mut Self::Output { + &mut self.grid[index.i][index.j] + } +} + +impl Grid { + pub fn find(&self, f: u8) -> Option { + let (i, j, _) = self + .grid + .iter() + .enumerate() + .flat_map(|(i, x)| x.iter().copied().enumerate().map(move |(j, y)| (i, j, y))) + .find(|x| x.2 == f)?; + + Some(Point::new(i, j)) + } + + pub fn get(&self, p: &Point) -> Option<&u8> { + self.grid.get(p.i).and_then(|r| r.get(p.j)) + } + + pub fn get_mut(&mut self, p: &Point) -> Option<&mut u8> { + self.grid.get_mut(p.i).and_then(|r| r.get_mut(p.j)) + } +} diff --git a/src/grid_vec/mod.rs b/src/grid_vec/mod.rs new file mode 100644 index 000000000..7bc7850 --- /dev/null +++ b/src/grid_vec/mod.rs @@ -0,0 +1,9 @@ +mod grid; +mod direction; +mod point; + +pub use grid::Grid; +pub use direction::Direction; +pub use point::Point; + + diff --git a/src/grid_vec/point.rs b/src/grid_vec/point.rs new file mode 100644 index 000000000..39c702a --- /dev/null +++ b/src/grid_vec/point.rs @@ -0,0 +1,91 @@ +use super::Direction; + +use std::{ + fmt::Display, + ops::{Add, AddAssign}, +}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub struct Point { + pub i: usize, + pub j: usize, +} + +impl Display for Point { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {})", self.i, self.j) + } +} + +impl Add for Point { + type Output = Self; + fn add(self, rhs: Direction) -> Self::Output { + Self { + i: self.i.wrapping_add(rhs.i as usize), + j: self.j.wrapping_add(rhs.j as usize), + } + } +} + +impl AddAssign for Point { + fn add_assign(&mut self, rhs: Direction) { + *self = *self + rhs + } +} + +impl Point { + pub fn new(i: usize, j: usize) -> Self { + Self { i, j } + } + + pub fn n(self) -> Self { + Self { + i: self.i.wrapping_sub(1), + j: self.j, + } + } + + pub fn s(self) -> Self { + Self { + i: self.i.wrapping_add(1), + j: self.j, + } + } + + pub fn e(self) -> Self { + Self { + i: self.i, + j: self.j.wrapping_sub(1), + } + } + + pub fn w(self) -> Self { + Self { + i: self.i, + j: self.j.wrapping_add(1), + } + } + + pub fn ne(self) -> Self { + self.n().e() + } + + pub fn nw(self) -> Self { + self.n().w() + } + + pub fn se(self) -> Self { + self.s().e() + } + + pub fn sw(self) -> Self { + self.s().w() + } +} + +#[macro_export] +macro_rules! pnt { + ($i:literal, $j:literal) => { + Point { i: $i, j: $j } + }; +} diff --git a/src/lib.rs b/src/lib.rs index d103939..ccd2b4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![feature(pattern)] +pub mod grid_vec; pub mod parsers; pub mod template;