Skip to content

Commit c9a53d8

Browse files
committed
16th day
1 parent 09a2f6c commit c9a53d8

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

data/examples/16.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+
#################

src/bin/16.rs

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
advent_of_code::solution!(16);
2+
3+
use advent_of_code::maneatingape::grid::*;
4+
use advent_of_code::maneatingape::hash::*;
5+
use advent_of_code::maneatingape::heap::*;
6+
use advent_of_code::maneatingape::point::*;
7+
8+
fn parse_data(input: &str) -> Grid<u8> {
9+
Grid::parse(input)
10+
}
11+
12+
fn neighbors(
13+
grid: &Grid<u8>,
14+
position: Point,
15+
direction: Point,
16+
cost: u32,
17+
) -> Vec<(Point, Point, u32)> {
18+
let mut result = Vec::with_capacity(4);
19+
20+
for n_direction in [LEFT, RIGHT, UP, DOWN] {
21+
let n_position = position + n_direction;
22+
23+
if grid[n_position] != b'#' {
24+
let n_cost_diff = match (direction, n_direction) {
25+
(LEFT, LEFT) => 1,
26+
(LEFT, UP) => 1001,
27+
(LEFT, DOWN) => 1001,
28+
(LEFT, RIGHT) => 2001,
29+
30+
(RIGHT, RIGHT) => 1,
31+
(RIGHT, UP) => 1001,
32+
(RIGHT, DOWN) => 1001,
33+
(RIGHT, LEFT) => 2001,
34+
35+
(UP, UP) => 1,
36+
(UP, LEFT) => 1001,
37+
(UP, RIGHT) => 1001,
38+
(UP, DOWN) => 2001,
39+
40+
(DOWN, DOWN) => 1,
41+
(DOWN, LEFT) => 1001,
42+
(DOWN, RIGHT) => 1001,
43+
(DOWN, UP) => 2001,
44+
45+
_ => panic!("Invalid state"),
46+
};
47+
48+
result.push((n_position, n_direction, cost + n_cost_diff));
49+
}
50+
}
51+
52+
result
53+
}
54+
55+
fn find_shortest_path_cost(grid: Grid<u8>) -> u32 {
56+
let start_position = grid.find(b'S').unwrap();
57+
let start_direction = RIGHT;
58+
59+
let mut min_heap = MinHeap::new();
60+
let mut g_score = FastMap::new();
61+
62+
min_heap.push(0, (start_position, start_direction));
63+
g_score.insert((start_position, start_direction), 0);
64+
65+
while let Some((cost, (position, direction))) = min_heap.pop() {
66+
if grid[position] == b'E' {
67+
return cost;
68+
}
69+
70+
for (n_position, n_direction, n_cost) in neighbors(&grid, position, direction, cost) {
71+
if n_cost < *g_score.get(&(n_position, n_direction)).unwrap_or(&u32::MAX) {
72+
g_score.insert((n_position, n_direction), n_cost);
73+
min_heap.push(n_cost, (n_position, n_direction));
74+
}
75+
}
76+
}
77+
78+
u32::MAX
79+
}
80+
81+
fn find_all_shortest_paths_points(grid: Grid<u8>) -> FastSet<Point> {
82+
let start_position = grid.find(b'S').unwrap();
83+
let start_direction = RIGHT;
84+
let start_path = vec![start_position];
85+
86+
let mut min_heap = MinHeap::new();
87+
let mut g_score = FastMap::new();
88+
89+
min_heap.push(0, (start_position, start_direction, start_path));
90+
g_score.insert((start_position, start_direction), 0);
91+
92+
let mut first_winner_cost = u32::MAX;
93+
let mut all_shortest_paths_points = FastSet::new();
94+
95+
while let Some((cost, (position, direction, path))) = min_heap.pop() {
96+
if grid[position] == b'E' {
97+
if cost > first_winner_cost {
98+
return all_shortest_paths_points;
99+
} else {
100+
first_winner_cost = cost;
101+
all_shortest_paths_points.extend(path.iter());
102+
}
103+
}
104+
105+
for (n_position, n_direction, n_cost) in neighbors(&grid, position, direction, cost) {
106+
if n_cost <= *g_score.get(&(n_position, n_direction)).unwrap_or(&u32::MAX) {
107+
g_score.insert((n_position, n_direction), n_cost);
108+
109+
let mut n_path = Vec::with_capacity(path.len() + 1);
110+
n_path.extend(path.iter());
111+
n_path.push(n_position);
112+
113+
min_heap.push(n_cost, (n_position, n_direction, n_path));
114+
}
115+
}
116+
}
117+
118+
all_shortest_paths_points
119+
}
120+
121+
pub fn part_one(input: &str) -> Option<u32> {
122+
let grid = parse_data(input);
123+
124+
let result = find_shortest_path_cost(grid);
125+
126+
Some(result)
127+
}
128+
129+
pub fn part_two(input: &str) -> Option<u32> {
130+
let grid = parse_data(input);
131+
132+
let result = find_all_shortest_paths_points(grid).len() as u32;
133+
134+
Some(result)
135+
}
136+
137+
#[cfg(test)]
138+
mod tests {
139+
use super::*;
140+
141+
#[test]
142+
fn test_part_one() {
143+
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
144+
assert_eq!(result, Some(11048));
145+
}
146+
147+
#[test]
148+
fn test_part_two() {
149+
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
150+
assert_eq!(result, Some(64));
151+
}
152+
}

0 commit comments

Comments
 (0)