Skip to content

Commit 917caf5

Browse files
committed
AoC 2024 Day 16 - rust
1 parent 76c4601 commit 917caf5

File tree

6 files changed

+243
-12
lines changed

6 files changed

+243
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
| ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
1111
| 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) | | | | | | | | | |
1212
| 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) | | | | | | | | | | |
13-
| 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) | | | | | | | | | | |
13+
| 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) | | | | | | | | | |
1414
<!-- @END:ImplementationsTable:2024@ -->
1515

1616
## 2023

src/main/rust/AoC2023_17/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl AoC2023_17 {
5555
},
5656
|r#move| r#move.cell == end,
5757
adjacent,
58-
|r#move| r#move.cost,
58+
|_, r#move| r#move.cost,
5959
) as u32
6060
}
6161
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "AoC2024_16"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../aoc" }
8+
itertools = "0.11"
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#![allow(non_snake_case)]
2+
3+
use aoc::geometry::{Direction, Turn};
4+
use aoc::graph::AStar;
5+
use aoc::grid::{Cell, CharGrid, Grid};
6+
use aoc::Puzzle;
7+
use itertools::Itertools;
8+
9+
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
10+
struct State {
11+
pos: Cell,
12+
dir: Direction,
13+
}
14+
15+
struct AoC2024_16;
16+
17+
impl AoC2024_16 {
18+
fn adjacent(&self, grid: &CharGrid, state: State) -> Vec<State> {
19+
let mut dirs = vec![state.dir];
20+
[Turn::Right, Turn::Left]
21+
.into_iter()
22+
.map(|t| state.dir.turn(t))
23+
.for_each(|d| dirs.push(d));
24+
dirs.into_iter()
25+
.map(|dir| State {
26+
pos: state.pos.try_at(dir).unwrap(),
27+
dir,
28+
})
29+
.filter(|state| grid.get(&state.pos) != '#')
30+
.collect_vec()
31+
}
32+
}
33+
34+
impl aoc::Puzzle for AoC2024_16 {
35+
type Input = CharGrid;
36+
type Output1 = usize;
37+
type Output2 = usize;
38+
39+
aoc::puzzle_year_day!(2024, 16);
40+
41+
fn parse_input(&self, lines: Vec<String>) -> Self::Input {
42+
CharGrid::from(&lines.iter().map(AsRef::as_ref).collect::<Vec<_>>())
43+
}
44+
45+
fn part_1(&self, grid: &Self::Input) -> Self::Output1 {
46+
let start = Cell::at(grid.height() - 2, 1);
47+
let end = Cell::at(1, grid.width() - 2);
48+
AStar::distance(
49+
State {
50+
pos: start,
51+
dir: Direction::Right,
52+
},
53+
|state| state.pos == end,
54+
|state| self.adjacent(grid, state),
55+
|curr, next| if curr.dir == next.dir { 1 } else { 1001 },
56+
)
57+
}
58+
59+
fn part_2(&self, grid: &Self::Input) -> Self::Output2 {
60+
let start = Cell::at(grid.height() - 2, 1);
61+
let end = Cell::at(1, grid.width() - 2);
62+
let result = AStar::all(
63+
State {
64+
pos: start,
65+
dir: Direction::Right,
66+
},
67+
|state| state.pos == end,
68+
|state| self.adjacent(grid, state),
69+
|curr, next| if curr.dir == next.dir { 1 } else { 1001 },
70+
);
71+
Direction::capital()
72+
.into_iter()
73+
.flat_map(|dir| {
74+
result.get_paths(State { pos: end, dir }).into_iter()
75+
})
76+
.flat_map(|path| path.into_iter())
77+
.map(|state| state.pos)
78+
.unique()
79+
.count()
80+
}
81+
82+
fn samples(&self) {
83+
aoc::puzzle_samples! {
84+
self, part_1, TEST1, 7036,
85+
self, part_1, TEST2, 11048,
86+
self, part_2, TEST1, 45,
87+
self, part_2, TEST2, 64
88+
};
89+
}
90+
}
91+
92+
fn main() {
93+
AoC2024_16 {}.run(std::env::args());
94+
}
95+
96+
const TEST1: &str = "\
97+
###############
98+
#.......#....E#
99+
#.#.###.#.###.#
100+
#.....#.#...#.#
101+
#.###.#####.#.#
102+
#.#.#.......#.#
103+
#.#.#####.###.#
104+
#...........#.#
105+
###.#.#####.#.#
106+
#...#.....#.#.#
107+
#.#.#.###.#.#.#
108+
#.....#...#.#.#
109+
#.###.#.#.#.#.#
110+
#S..#.....#...#
111+
###############
112+
";
113+
const TEST2: &str = "\
114+
#################
115+
#...#...#...#..E#
116+
#.#.#.#.#.#.#.#.#
117+
#.#.#.#...#...#.#
118+
#.#.#.#.###.#.#.#
119+
#...#.#.#.....#.#
120+
#.#.#.#.#.#####.#
121+
#.#...#.#.#.....#
122+
#.#.#####.#.###.#
123+
#.#.#.......#...#
124+
#.#.###.#####.###
125+
#.#.#...#.....#.#
126+
#.#.#.#####.###.#
127+
#.#.#.........#.#
128+
#.#.#.#########.#
129+
#S#.............#
130+
#################
131+
";
132+
133+
#[cfg(test)]
134+
mod tests {
135+
use super::*;
136+
137+
#[test]
138+
pub fn samples() {
139+
AoC2024_16 {}.samples();
140+
}
141+
}

src/main/rust/Cargo.lock

Lines changed: 16 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/rust/aoc/src/graph.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ pub struct Result<T> {
107107
paths: HashMap<T, T>,
108108
}
109109

110+
pub struct AllResults<T> {
111+
start: T,
112+
predecessors: HashMap<T, Vec<T>>,
113+
}
114+
110115
impl<T> Result<T>
111116
where
112117
T: Eq + Copy + Hash,
@@ -142,6 +147,25 @@ where
142147
}
143148
}
144149

150+
impl<T> AllResults<T>
151+
where
152+
T: Eq + Copy + Hash,
153+
{
154+
pub fn get_paths(&self, t: T) -> Vec<Vec<T>> {
155+
if t == self.start {
156+
return vec![vec![self.start]];
157+
}
158+
let mut paths = vec![];
159+
for predecessor in self.predecessors.get(&t).unwrap_or(&Vec::new()) {
160+
for mut path in self.get_paths(*predecessor) {
161+
path.push(t);
162+
paths.push(path);
163+
}
164+
}
165+
paths
166+
}
167+
}
168+
145169
impl<T> AStar<T>
146170
where
147171
T: Eq + Copy + Hash,
@@ -187,11 +211,61 @@ where
187211
}
188212
}
189213

214+
pub fn all(
215+
start: T,
216+
is_end: impl Fn(T) -> bool,
217+
adjacent: impl Fn(T) -> Vec<T>,
218+
cost: impl Fn(T, T) -> usize,
219+
) -> AllResults<T> {
220+
let mut q: BinaryHeap<State<T>> = BinaryHeap::new();
221+
q.push(State {
222+
node: start,
223+
distance: 0,
224+
});
225+
let mut distances: HashMap<T, usize> = HashMap::new();
226+
distances.insert(start, 0);
227+
let mut predecessors: HashMap<T, Vec<T>> = HashMap::new();
228+
while let Some(state) = q.pop() {
229+
if is_end(state.node) {
230+
break;
231+
}
232+
let total = *distances.get(&state.node).unwrap_or(&usize::MAX);
233+
if state.distance > total {
234+
continue;
235+
}
236+
adjacent(state.node).iter().for_each(|n| {
237+
let risk = total + cost(state.node, *n);
238+
let dist_n = *distances.get(n).unwrap_or(&usize::MAX);
239+
match risk.cmp(&dist_n) {
240+
Ordering::Less => {
241+
distances.insert(*n, risk);
242+
predecessors.entry(*n).insert_entry(vec![state.node]);
243+
q.push(State {
244+
node: *n,
245+
distance: risk,
246+
});
247+
}
248+
Ordering::Equal => {
249+
predecessors
250+
.entry(*n)
251+
.and_modify(|e| e.push(state.node))
252+
.or_insert(vec![state.node]);
253+
}
254+
_ => (),
255+
}
256+
});
257+
}
258+
AllResults {
259+
start,
260+
predecessors,
261+
}
262+
}
263+
190264
pub fn distance(
191265
start: T,
192266
is_end: impl Fn(T) -> bool,
193267
adjacent: impl Fn(T) -> Vec<T>,
194-
cost: impl Fn(T) -> usize,
268+
cost: impl Fn(T, T) -> usize,
195269
) -> usize {
196270
let mut q: BinaryHeap<State<T>> = BinaryHeap::new();
197271
q.push(State {
@@ -209,7 +283,7 @@ where
209283
continue;
210284
}
211285
adjacent(state.node).iter().for_each(|n| {
212-
let risk = total + cost(*n);
286+
let risk = total + cost(state.node, *n);
213287
if risk < *distances.get(n).unwrap_or(&usize::MAX) {
214288
distances.insert(*n, risk);
215289
q.push(State {

0 commit comments

Comments
 (0)