Skip to content

Commit db3cc1b

Browse files
committed
2024: Add solution for Day 16
1 parent b6f8f3e commit db3cc1b

File tree

8 files changed

+330
-14
lines changed

8 files changed

+330
-14
lines changed

2024/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ cargo run --bin day01
2525
13. [Claw Contraption](day13) 🌟🌟
2626
14. [Restroom Redoubt](day14) 🌟🌟
2727
15. [](day15)
28-
16. [](day16)
28+
16. [Reindeer Maze](day16) 🌟🌟
2929
17. [](day17)
3030
18. [](day18)
3131
19. [](day19)

2024/day16/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
lib.workspace = true

2024/day16/example1.txt

Lines changed: 15 additions & 0 deletions
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+
###############

2024/day16/example2.txt

Lines changed: 17 additions & 0 deletions
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+
#################

2024/day16/input.txt

Lines changed: 141 additions & 0 deletions
Large diffs are not rendered by default.

2024/day16/src/main.rs

Lines changed: 154 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22
//! https://adventofcode.com/2024/day/16
33
44
use std::{fs, io};
5+
use std::collections::{BTreeMap, BTreeSet, BinaryHeap};
56
use std::path::Path;
7+
use std::str::FromStr;
8+
use lib::grid::{Grid, Pos};
9+
10+
const WALL: char = '#';
11+
const START: char = 'S';
12+
const END: char = 'E';
613

714
fn main() {
815
let input = Input::from_file(format!("{}/input.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
9-
//let input = Input::from_file(format!("{}/example1.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
10-
println!("{input:?}");
16+
//let input = Input::from_file(format!("{}/example2.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
17+
println!("{}", input.grid);
1118

1219
// Part 1
1320
println!("Part 1: {}", part1(&input));
@@ -16,25 +23,146 @@ fn main() {
1623
println!("Part 2: {}", part2(&input));
1724
}
1825

26+
fn find_start_end(grid: &Grid) -> (Pos, Pos) {
27+
let mut start = Pos::default();
28+
let mut end = Pos::default();
29+
30+
for pos in grid.positions() {
31+
match grid[pos] {
32+
START => start = pos,
33+
END => end = pos,
34+
_ => (),
35+
}
36+
}
37+
38+
(start, end)
39+
}
40+
41+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
42+
enum Direction {
43+
East,
44+
South,
45+
West,
46+
North,
47+
}
48+
49+
impl Direction {
50+
pub fn clockwise(self) -> Direction {
51+
match self {
52+
Direction::East => Direction::South,
53+
Direction::South => Direction::West,
54+
Direction::West => Direction::North,
55+
Direction::North => Direction::East,
56+
}
57+
}
58+
59+
pub fn anticlockwise(self) -> Direction {
60+
match self {
61+
Direction::East => Direction::North,
62+
Direction::North => Direction::West,
63+
Direction::West => Direction::South,
64+
Direction::South => Direction::East,
65+
}
66+
}
67+
}
68+
69+
impl From<Direction> for Pos {
70+
fn from(direction: Direction) -> Self {
71+
match direction {
72+
Direction::East => Pos::new([1, 0]),
73+
Direction::South => Pos::new([0, 1]),
74+
Direction::West => Pos::new([-1, 0]),
75+
Direction::North => Pos::new([0, -1]),
76+
}
77+
}
78+
}
79+
1980
fn part1(input: &Input) -> usize {
81+
let (start, end) = find_start_end(&input.grid);
82+
83+
let mut best: BTreeMap<(Pos, Direction), usize> = [((start, Direction::East), 0)].into_iter().collect();
84+
let mut edge: BinaryHeap<(usize, (Pos, Direction), BTreeSet<Pos>)> = [(0, (start, Direction::East), BTreeSet::new())].into_iter().collect();
85+
86+
while let Some((_, cur, path)) = edge.pop() {
87+
if cur.0 == end {
88+
return best[&cur];
89+
}
90+
91+
for next in [(cur.0 + cur.1.into(), cur.1), (cur.0, cur.1.clockwise()), (cur.0, cur.1.anticlockwise())] {
92+
if input.grid[next.0] == WALL {
93+
continue;
94+
}
95+
96+
let new_score = best[&cur] + (next.1 != cur.1).then_some(1000).unwrap_or(1);
97+
if best.contains_key(&next) && best[&next] < new_score {
98+
continue;
99+
}
100+
101+
let mut new_path: BTreeSet<Pos> = path.clone();
102+
new_path.insert(next.0);
103+
104+
best.insert(next, new_score);
105+
edge.push((usize::MAX - new_score, next, new_path));
106+
}
107+
}
108+
20109
0
21110
}
22111

23112
fn part2(input: &Input) -> usize {
24-
0
113+
let (start, end) = find_start_end(&input.grid);
114+
115+
let mut best: BTreeMap<(Pos, Direction), usize> = [((start, Direction::East), 0)].into_iter().collect();
116+
let start_path: BTreeSet<Pos> = [start].into_iter().collect();
117+
let mut edge: BinaryHeap<(usize, (Pos, Direction), BTreeSet<Pos>)> = [(0, (start, Direction::East), start_path)].into_iter().collect();
118+
119+
let mut best_score = None;
120+
let mut all_path = BTreeSet::new();
121+
122+
while let Some((_, cur, path)) = edge.pop() {
123+
if cur.0 == end {
124+
all_path.extend(path.iter().copied());
125+
126+
if let Some(best_score) = best_score {
127+
if best[&cur] > best_score {
128+
break;
129+
}
130+
}
131+
best_score = Some(best[&cur]);
132+
}
133+
134+
for next in [(cur.0 + cur.1.into(), cur.1), (cur.0, cur.1.clockwise()), (cur.0, cur.1.anticlockwise())] {
135+
if input.grid[next.0] == WALL {
136+
continue;
137+
}
138+
139+
let new_score = best[&cur] + (next.1 != cur.1).then_some(1000).unwrap_or(1);
140+
if best.contains_key(&next) && best[&next] < new_score {
141+
continue;
142+
}
143+
144+
let mut new_path: BTreeSet<Pos> = path.clone();
145+
new_path.insert(next.0);
146+
147+
best.insert(next, new_score);
148+
edge.push((usize::MAX - new_score, next, new_path));
149+
}
150+
}
151+
152+
all_path.len()
25153
}
26154

27155
#[derive(Debug, Clone)]
28156
struct Input {
29-
values: Vec<String>,
157+
pub grid: Grid,
30158
}
31159

32160
impl Input {
33161
fn from_file(path: impl AsRef<Path>) -> io::Result<Self> {
34162
let input = fs::read_to_string(path)?;
35-
let values = input.lines().map(str::to_string).collect();
163+
let grid = Grid::from_str(&input)?;
36164

37-
Ok(Self { values })
165+
Ok(Self { grid })
38166
}
39167
}
40168

@@ -43,30 +171,44 @@ mod test {
43171
use super::*;
44172

45173
#[test]
46-
fn test_part1() {
174+
fn test_part1_example1() {
47175
let input = Input::from_file("example1.txt").unwrap();
48176

49-
assert_eq!(part1(&input), 0);
177+
assert_eq!(part1(&input), 7036);
178+
}
179+
180+
#[test]
181+
fn test_part1_example2() {
182+
let input = Input::from_file("example2.txt").unwrap();
183+
184+
assert_eq!(part1(&input), 11048);
50185
}
51186

52187
#[test]
53188
fn test_part1_solution() {
54189
let input = Input::from_file("input.txt").unwrap();
55190

56-
assert_eq!(part1(&input), 0);
191+
assert_eq!(part1(&input), 102460);
57192
}
58193

59194
#[test]
60-
fn test_part2() {
195+
fn test_part2_example1() {
61196
let input = Input::from_file("example1.txt").unwrap();
62197

63-
assert_eq!(part2(&input), 0);
198+
assert_eq!(part2(&input), 45);
199+
}
200+
201+
#[test]
202+
fn test_part2_example2() {
203+
let input = Input::from_file("example2.txt").unwrap();
204+
205+
assert_eq!(part2(&input), 64);
64206
}
65207

66208
#[test]
67209
fn test_part2_solution() {
68210
let input = Input::from_file("input.txt").unwrap();
69211

70-
assert_eq!(part2(&input), 0);
212+
assert_eq!(part2(&input), 527);
71213
}
72214
}

2024/lib/src/grid.rs

Whitespace-only changes.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ These are mostly written in [Rust](https://www.rust-lang.org/).
77
I can also be found on BlueSky: [@dcoles.net](https://bsky.app/profile/dcoles.net)
88

99
## Solutions
10-
- [Advent of Code 2024](2024#readme): 28🌟
10+
- [Advent of Code 2024](2024#readme): 30🌟
1111
- [Advent of Code 2023](2023#readme): 45🌟
1212
- [Advent of Code 2022](2022#readme): 50🌟!
1313
- [Advent of Code 2021](2021#readme): 50🌟!

0 commit comments

Comments
 (0)