diff --git a/README.md b/README.md index f95d0460..34f79345 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ | ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | python3 | [✓](src/main/python/AoC2024_01.py) | [✓](src/main/python/AoC2024_02.py) | [✓](src/main/python/AoC2024_03.py) | [✓](src/main/python/AoC2024_04.py) | [✓](src/main/python/AoC2024_05.py) | [✓](src/main/python/AoC2024_06.py) | [✓](src/main/python/AoC2024_07.py) | [✓](src/main/python/AoC2024_08.py) | [✓](src/main/python/AoC2024_09.py) | [✓](src/main/python/AoC2024_10.py) | [✓](src/main/python/AoC2024_11.py) | [✓](src/main/python/AoC2024_12.py) | [✓](src/main/python/AoC2024_13.py) | [✓](src/main/python/AoC2024_14.py) | [✓](src/main/python/AoC2024_15.py) | [✓](src/main/python/AoC2024_16.py) | | | | | | | | | | | java | [✓](src/main/java/AoC2024_01.java) | [✓](src/main/java/AoC2024_02.java) | [✓](src/main/java/AoC2024_03.java) | [✓](src/main/java/AoC2024_04.java) | [✓](src/main/java/AoC2024_05.java) | [✓](src/main/java/AoC2024_06.java) | [✓](src/main/java/AoC2024_07.java) | [✓](src/main/java/AoC2024_08.java) | | [✓](src/main/java/AoC2024_10.java) | [✓](src/main/java/AoC2024_11.java) | [✓](src/main/java/AoC2024_12.java) | | [✓](src/main/java/AoC2024_14.java) | [✓](src/main/java/AoC2024_15.java) | | | | | | | | | | | -| rust | [✓](src/main/rust/AoC2024_01/src/main.rs) | [✓](src/main/rust/AoC2024_02/src/main.rs) | [✓](src/main/rust/AoC2024_03/src/main.rs) | [✓](src/main/rust/AoC2024_04/src/main.rs) | [✓](src/main/rust/AoC2024_05/src/main.rs) | [✓](src/main/rust/AoC2024_06/src/main.rs) | [✓](src/main/rust/AoC2024_07/src/main.rs) | [✓](src/main/rust/AoC2024_08/src/main.rs) | | [✓](src/main/rust/AoC2024_10/src/main.rs) | | | [✓](src/main/rust/AoC2024_13/src/main.rs) | [✓](src/main/rust/AoC2024_14/src/main.rs) | [✓](src/main/rust/AoC2024_15/src/main.rs) | | | | | | | | | | | +| rust | [✓](src/main/rust/AoC2024_01/src/main.rs) | [✓](src/main/rust/AoC2024_02/src/main.rs) | [✓](src/main/rust/AoC2024_03/src/main.rs) | [✓](src/main/rust/AoC2024_04/src/main.rs) | [✓](src/main/rust/AoC2024_05/src/main.rs) | [✓](src/main/rust/AoC2024_06/src/main.rs) | [✓](src/main/rust/AoC2024_07/src/main.rs) | [✓](src/main/rust/AoC2024_08/src/main.rs) | | [✓](src/main/rust/AoC2024_10/src/main.rs) | | | [✓](src/main/rust/AoC2024_13/src/main.rs) | [✓](src/main/rust/AoC2024_14/src/main.rs) | [✓](src/main/rust/AoC2024_15/src/main.rs) | [✓](src/main/rust/AoC2024_16/src/main.rs) | | | | | | | | | | ## 2023 diff --git a/src/main/rust/AoC2023_17/src/main.rs b/src/main/rust/AoC2023_17/src/main.rs index 024d3db4..074d9f76 100644 --- a/src/main/rust/AoC2023_17/src/main.rs +++ b/src/main/rust/AoC2023_17/src/main.rs @@ -55,7 +55,7 @@ impl AoC2023_17 { }, |r#move| r#move.cell == end, adjacent, - |r#move| r#move.cost, + |_, r#move| r#move.cost, ) as u32 } } diff --git a/src/main/rust/AoC2024_16/Cargo.toml b/src/main/rust/AoC2024_16/Cargo.toml new file mode 100644 index 00000000..acd90a33 --- /dev/null +++ b/src/main/rust/AoC2024_16/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "AoC2024_16" +version = "0.1.0" +edition = "2021" + +[dependencies] +aoc = { path = "../aoc" } +itertools = "0.11" diff --git a/src/main/rust/AoC2024_16/src/main.rs b/src/main/rust/AoC2024_16/src/main.rs new file mode 100644 index 00000000..de23c9ec --- /dev/null +++ b/src/main/rust/AoC2024_16/src/main.rs @@ -0,0 +1,141 @@ +#![allow(non_snake_case)] + +use aoc::geometry::{Direction, Turn}; +use aoc::graph::AStar; +use aoc::grid::{Cell, CharGrid, Grid}; +use aoc::Puzzle; +use itertools::Itertools; + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +struct State { + pos: Cell, + dir: Direction, +} + +struct AoC2024_16; + +impl AoC2024_16 { + fn adjacent(&self, grid: &CharGrid, state: State) -> Vec { + let mut dirs = vec![state.dir]; + [Turn::Right, Turn::Left] + .into_iter() + .map(|t| state.dir.turn(t)) + .for_each(|d| dirs.push(d)); + dirs.into_iter() + .map(|dir| State { + pos: state.pos.try_at(dir).unwrap(), + dir, + }) + .filter(|state| grid.get(&state.pos) != '#') + .collect_vec() + } +} + +impl aoc::Puzzle for AoC2024_16 { + type Input = CharGrid; + type Output1 = usize; + type Output2 = usize; + + aoc::puzzle_year_day!(2024, 16); + + fn parse_input(&self, lines: Vec) -> Self::Input { + CharGrid::from(&lines.iter().map(AsRef::as_ref).collect::>()) + } + + fn part_1(&self, grid: &Self::Input) -> Self::Output1 { + let start = Cell::at(grid.height() - 2, 1); + let end = Cell::at(1, grid.width() - 2); + AStar::distance( + State { + pos: start, + dir: Direction::Right, + }, + |state| state.pos == end, + |state| self.adjacent(grid, state), + |curr, next| if curr.dir == next.dir { 1 } else { 1001 }, + ) + } + + fn part_2(&self, grid: &Self::Input) -> Self::Output2 { + let start = Cell::at(grid.height() - 2, 1); + let end = Cell::at(1, grid.width() - 2); + let result = AStar::all( + State { + pos: start, + dir: Direction::Right, + }, + |state| state.pos == end, + |state| self.adjacent(grid, state), + |curr, next| if curr.dir == next.dir { 1 } else { 1001 }, + ); + Direction::capital() + .into_iter() + .flat_map(|dir| { + result.get_paths(State { pos: end, dir }).into_iter() + }) + .flat_map(|path| path.into_iter()) + .map(|state| state.pos) + .unique() + .count() + } + + fn samples(&self) { + aoc::puzzle_samples! { + self, part_1, TEST1, 7036, + self, part_1, TEST2, 11048, + self, part_2, TEST1, 45, + self, part_2, TEST2, 64 + }; + } +} + +fn main() { + AoC2024_16 {}.run(std::env::args()); +} + +const TEST1: &str = "\ +############### +#.......#....E# +#.#.###.#.###.# +#.....#.#...#.# +#.###.#####.#.# +#.#.#.......#.# +#.#.#####.###.# +#...........#.# +###.#.#####.#.# +#...#.....#.#.# +#.#.#.###.#.#.# +#.....#...#.#.# +#.###.#.#.#.#.# +#S..#.....#...# +############### +"; +const TEST2: &str = "\ +################# +#...#...#...#..E# +#.#.#.#.#.#.#.#.# +#.#.#.#...#...#.# +#.#.#.#.###.#.#.# +#...#.#.#.....#.# +#.#.#.#.#.#####.# +#.#...#.#.#.....# +#.#.#####.#.###.# +#.#.#.......#...# +#.#.###.#####.### +#.#.#...#.....#.# +#.#.#.#####.###.# +#.#.#.........#.# +#.#.#.#########.# +#S#.............# +################# +"; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn samples() { + AoC2024_16 {}.samples(); + } +} diff --git a/src/main/rust/Cargo.lock b/src/main/rust/Cargo.lock index 8bff331d..bc934bc8 100644 --- a/src/main/rust/Cargo.lock +++ b/src/main/rust/Cargo.lock @@ -477,6 +477,14 @@ dependencies = [ "aoc", ] +[[package]] +name = "AoC2024_16" +version = "0.1.0" +dependencies = [ + "aoc", + "itertools", +] + [[package]] name = "aho-corasick" version = "1.0.2" @@ -740,9 +748,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -759,18 +767,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -885,9 +893,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", diff --git a/src/main/rust/aoc/src/graph.rs b/src/main/rust/aoc/src/graph.rs index ef3cca18..0b3025a8 100644 --- a/src/main/rust/aoc/src/graph.rs +++ b/src/main/rust/aoc/src/graph.rs @@ -107,6 +107,11 @@ pub struct Result { paths: HashMap, } +pub struct AllResults { + start: T, + predecessors: HashMap>, +} + impl Result where T: Eq + Copy + Hash, @@ -142,6 +147,25 @@ where } } +impl AllResults +where + T: Eq + Copy + Hash, +{ + pub fn get_paths(&self, t: T) -> Vec> { + if t == self.start { + return vec![vec![self.start]]; + } + let mut paths = vec![]; + for predecessor in self.predecessors.get(&t).unwrap_or(&Vec::new()) { + for mut path in self.get_paths(*predecessor) { + path.push(t); + paths.push(path); + } + } + paths + } +} + impl AStar where T: Eq + Copy + Hash, @@ -187,11 +211,61 @@ where } } + pub fn all( + start: T, + is_end: impl Fn(T) -> bool, + adjacent: impl Fn(T) -> Vec, + cost: impl Fn(T, T) -> usize, + ) -> AllResults { + let mut q: BinaryHeap> = BinaryHeap::new(); + q.push(State { + node: start, + distance: 0, + }); + let mut distances: HashMap = HashMap::new(); + distances.insert(start, 0); + let mut predecessors: HashMap> = HashMap::new(); + while let Some(state) = q.pop() { + if is_end(state.node) { + break; + } + let total = *distances.get(&state.node).unwrap_or(&usize::MAX); + if state.distance > total { + continue; + } + adjacent(state.node).iter().for_each(|n| { + let risk = total + cost(state.node, *n); + let dist_n = *distances.get(n).unwrap_or(&usize::MAX); + match risk.cmp(&dist_n) { + Ordering::Less => { + distances.insert(*n, risk); + predecessors.entry(*n).insert_entry(vec![state.node]); + q.push(State { + node: *n, + distance: risk, + }); + } + Ordering::Equal => { + predecessors + .entry(*n) + .and_modify(|e| e.push(state.node)) + .or_insert(vec![state.node]); + } + _ => (), + } + }); + } + AllResults { + start, + predecessors, + } + } + pub fn distance( start: T, is_end: impl Fn(T) -> bool, adjacent: impl Fn(T) -> Vec, - cost: impl Fn(T) -> usize, + cost: impl Fn(T, T) -> usize, ) -> usize { let mut q: BinaryHeap> = BinaryHeap::new(); q.push(State { @@ -209,7 +283,7 @@ where continue; } adjacent(state.node).iter().for_each(|n| { - let risk = total + cost(*n); + let risk = total + cost(state.node, *n); if risk < *distances.get(n).unwrap_or(&usize::MAX) { distances.insert(*n, risk); q.push(State {