Skip to content

Commit 9b51a7d

Browse files
committed
Day 16
1 parent 7f82e80 commit 9b51a7d

File tree

5 files changed

+310
-7
lines changed

5 files changed

+310
-7
lines changed

.cargo/config.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ read = "run --quiet --release -- read"
55

66
solve = "run --quiet --release -- solve"
77
all = "run --quiet --release -- all"
8-
time = "run --quiet --release -- all --release --time"
8+
time = "run --quiet --release -- time"

aoc2024/data/examples/16-2.txt

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#################
2+
#...#...#...#..E#
3+
#.#.#.#.#.#.#.#.#
4+
#.#.#.#...#...#.#
5+
#.#.#.#.###.#.#.#
6+
#...#.#.#.....#.#
7+
#.#.#.#.#.#####.#
8+
#.#...#.#.#.....#
9+
#.#.#####.#.###.#
10+
#.#.#.......#...#
11+
#.#.###.#####.###
12+
#.#.#...#.....#.#
13+
#.#.#.#####.###.#
14+
#.#.#.........#.#
15+
#.#.#.#########.#
16+
#S#.............#
17+
#################

aoc2024/data/examples/16.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
###############
2+
#.......#....E#
3+
#.#.###.#.###.#
4+
#.....#.#...#.#
5+
#.###.#####.#.#
6+
#.#.#.......#.#
7+
#.#.#####.###.#
8+
#...........#.#
9+
###.#.#####.#.#
10+
#...#.....#.#.#
11+
#.#.#.###.#.#.#
12+
#.....#...#.#.#
13+
#.###.#.#.#.#.#
14+
#S..#.....#...#
15+
###############

aoc2024/src/bin/16.rs

+252
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
use std::{
2+
cmp::Ordering,
3+
collections::{BinaryHeap, HashMap, HashSet},
4+
};
5+
6+
use aoc_utils::*;
7+
8+
advent_of_code::solution!(16);
9+
10+
#[derive(Clone, Eq, PartialEq)]
11+
struct State {
12+
cost: usize,
13+
position: Point,
14+
direction: Dir,
15+
}
16+
17+
impl Ord for State {
18+
fn cmp(&self, other: &Self) -> Ordering {
19+
other.cost.cmp(&self.cost)
20+
}
21+
}
22+
23+
impl PartialOrd for State {
24+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
25+
Some(self.cmp(other))
26+
}
27+
}
28+
29+
fn dijkstra(map: &[Vec<char>], start: Point, goal: Point) -> Option<u64> {
30+
let mut dist: HashMap<(Point, Dir), usize> = HashMap::new();
31+
32+
let mut heap = BinaryHeap::new();
33+
34+
let bounds = Bounds(map.len() - 1, map[0].len() - 1);
35+
36+
heap.push(State {
37+
cost: 0,
38+
position: start,
39+
direction: Dir::Right, // dummy value
40+
});
41+
42+
while let Some(State {
43+
cost,
44+
position,
45+
direction,
46+
}) = heap.pop()
47+
{
48+
if position == goal {
49+
return Some(cost as u64);
50+
}
51+
52+
if let Some(found) = dist.get(&(position, direction)) {
53+
if *found < cost {
54+
continue;
55+
}
56+
}
57+
58+
if let Some(next) = direction.next(position, bounds) {
59+
if map[next.0][next.1] != '#' {
60+
heap.push(State {
61+
cost: cost + 1,
62+
position: next,
63+
direction,
64+
});
65+
}
66+
}
67+
68+
for new_dir in vec![direction.cw(), direction.ccw()].into_iter() {
69+
let np = new_dir
70+
.next(position, bounds)
71+
.expect("np should always be valid due to wall");
72+
if map[np.0][np.1] == '#' {
73+
continue;
74+
}
75+
let next = State {
76+
cost: cost + 1000,
77+
position,
78+
direction: new_dir,
79+
};
80+
let new_cost = next.cost;
81+
82+
if let Some(found) = dist.get(&(position, new_dir)) {
83+
if new_cost < *found {
84+
heap.push(next);
85+
dist.insert((position, new_dir), new_cost);
86+
}
87+
} else {
88+
heap.push(next);
89+
dist.insert((position, new_dir), new_cost);
90+
}
91+
}
92+
}
93+
None
94+
}
95+
96+
#[derive(Clone, Eq, PartialEq)]
97+
struct StateP2 {
98+
cost: usize,
99+
position: Point,
100+
direction: Dir,
101+
path: Vec<Point>,
102+
}
103+
104+
impl Ord for StateP2 {
105+
fn cmp(&self, other: &Self) -> Ordering {
106+
other.cost.cmp(&self.cost)
107+
}
108+
}
109+
110+
impl PartialOrd for StateP2 {
111+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
112+
Some(self.cmp(other))
113+
}
114+
}
115+
116+
fn dijkstra_part2(map: &[Vec<char>], start: Point, goal: Point) -> Option<u64> {
117+
let mut dist: HashMap<(Point, Dir), usize> = HashMap::new();
118+
119+
let mut heap = BinaryHeap::new();
120+
121+
let bounds = Bounds(map.len() - 1, map[0].len() - 1);
122+
123+
heap.push(StateP2 {
124+
cost: 0,
125+
position: start,
126+
direction: Dir::Right, // dummy value
127+
path: vec![start],
128+
});
129+
130+
let mut res = None::<u64>;
131+
let mut best: HashSet<Point> = HashSet::new();
132+
133+
while let Some(StateP2 {
134+
cost,
135+
position,
136+
direction,
137+
path,
138+
}) = heap.pop()
139+
{
140+
if position == goal {
141+
if let Some(c) = res {
142+
if cost as u64 > c {
143+
return Some(best.len() as u64);
144+
} else {
145+
path.into_iter().for_each(|p| {
146+
best.insert(p);
147+
});
148+
continue;
149+
}
150+
} else {
151+
res = Some(cost as u64);
152+
path.into_iter().for_each(|p| {
153+
best.insert(p);
154+
});
155+
continue;
156+
}
157+
}
158+
159+
if let Some(found) = dist.get(&(position, direction)) {
160+
if *found < cost {
161+
continue;
162+
}
163+
}
164+
165+
if let Some(next) = direction.next(position, bounds) {
166+
if map[next.0][next.1] != '#' {
167+
let mut new_path = path.clone();
168+
new_path.push(next);
169+
heap.push(StateP2 {
170+
cost: cost + 1,
171+
position: next,
172+
direction,
173+
path: new_path,
174+
});
175+
}
176+
}
177+
178+
for new_dir in vec![direction.cw(), direction.ccw()].into_iter() {
179+
let np = new_dir
180+
.next(position, bounds)
181+
.expect("np should always be valid due to wall");
182+
if map[np.0][np.1] == '#' {
183+
continue;
184+
}
185+
let next = StateP2 {
186+
cost: cost + 1000,
187+
position,
188+
direction: new_dir,
189+
path: path.clone(),
190+
};
191+
let new_cost = next.cost;
192+
193+
if let Some(found) = dist.get(&(position, new_dir)) {
194+
if new_cost <= *found {
195+
heap.push(next);
196+
dist.insert((position, new_dir), new_cost);
197+
}
198+
} else {
199+
heap.push(next);
200+
dist.insert((position, new_dir), new_cost);
201+
}
202+
}
203+
}
204+
None
205+
}
206+
207+
pub fn part_one(input: &str) -> Option<u64> {
208+
let map = input.c_map();
209+
let start = find_point(&map, 'S');
210+
let end = find_point(&map, 'E');
211+
dijkstra(&map, start, end)
212+
}
213+
214+
pub fn part_two(input: &str) -> Option<u64> {
215+
let map = input.c_map();
216+
let start = find_point(&map, 'S');
217+
let end = find_point(&map, 'E');
218+
dijkstra_part2(&map, start, end)
219+
}
220+
221+
#[cfg(test)]
222+
mod tests {
223+
use super::*;
224+
225+
#[test]
226+
fn test_part_one() {
227+
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
228+
assert_eq!(result, Some(7036));
229+
}
230+
231+
#[test]
232+
fn test_part_one_two() {
233+
let result = part_one(&advent_of_code::template::read_file_part(
234+
"examples", DAY, 2,
235+
));
236+
assert_eq!(result, Some(11048));
237+
}
238+
239+
#[test]
240+
fn test_part_two() {
241+
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
242+
assert_eq!(result, Some(45));
243+
}
244+
245+
#[test]
246+
fn test_part_two_two() {
247+
let result = part_two(&advent_of_code::template::read_file_part(
248+
"examples", DAY, 2,
249+
));
250+
assert_eq!(result, Some(64));
251+
}
252+
}

aoc_utils/src/lib.rs

+25-6
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,34 @@ use itertools::Itertools;
44
use regex::{Captures, Regex};
55
use tinyvec::{ArrayVec, array_vec};
66

7-
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
8-
pub struct Point(usize, usize);
9-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10-
pub struct Bounds(usize, usize);
7+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
8+
pub struct Point(pub usize, pub usize);
119

12-
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
13-
pub struct IPoint(i64, i64);
10+
pub fn find_point<U>(map: &[Vec<U>], val: U) -> Point
11+
where
12+
U: PartialEq,
13+
{
14+
map.iter()
15+
.enumerate()
16+
.find_map(|(row, v)| {
17+
v.iter().enumerate().find_map(|(col, item)| {
18+
if *item == val {
19+
Some(Point(row, col))
20+
} else {
21+
None
22+
}
23+
})
24+
})
25+
.expect("Should find point")
26+
}
1427

1528
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29+
pub struct Bounds(pub usize, pub usize);
30+
31+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
32+
pub struct IPoint(pub i64, pub i64);
33+
34+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1635
pub enum Dir {
1736
Up,
1837
Down,

0 commit comments

Comments
 (0)