Skip to content

Commit a3b27dd

Browse files
committed
Day 18
1 parent db761ad commit a3b27dd

File tree

3 files changed

+162
-1
lines changed

3 files changed

+162
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
2828
| [Day 15](./src/bin/15.rs) | `119.7µs` | `591.0µs` |
2929
| [Day 16](./src/bin/16.rs) | `158.9µs` | `202.6µs` |
3030
| [Day 17](./src/bin/17.rs) | `1.7µs` | `361.3µs` |
31+
| [Day 18](./src/bin/18.rs) | `85.5µs` | `60.2µs` |
3132

32-
**Total: 13.45ms**
33+
**Total: 13.60ms**
3334
<!--- benchmarking table --->
3435

3536
---

data/examples/18.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
5,4
2+
4,2
3+
4,5
4+
3,0
5+
2,1
6+
6,3
7+
2,4
8+
1,5
9+
0,6
10+
3,3
11+
2,6
12+
5,1
13+
1,2
14+
5,5
15+
2,5
16+
6,5
17+
1,4
18+
0,4
19+
6,4
20+
1,1
21+
6,1
22+
1,0
23+
0,5
24+
1,6
25+
2,0

src/bin/18.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use std::{collections::VecDeque, u32};
2+
3+
use mygrid::{direction::ORTHOGONAL, grid::Grid, point::Point};
4+
5+
advent_of_code::solution!(18);
6+
7+
fn parse_points(input: &str) -> Vec<Point> {
8+
input
9+
.lines()
10+
.filter(|line| !line.is_empty())
11+
.map(|line| {
12+
let (col, line) = line.split_once(',').unwrap();
13+
Point::new(line.parse().unwrap(), col.parse().unwrap())
14+
})
15+
.collect()
16+
}
17+
18+
fn fill_min_dst_grid(grid: &Grid<char>, min_dst: &mut Grid<u32>, start: (Point, u32), end: Point) {
19+
let mut q = VecDeque::new();
20+
q.push_back(start);
21+
22+
while let Some((current, dst)) = q.pop_front() {
23+
min_dst[current] = min_dst[current].min(dst);
24+
if current == end {
25+
continue;
26+
}
27+
ORTHOGONAL
28+
.iter()
29+
.map(|&dir| current + dir)
30+
.filter(|&p| grid.is_in_bounds(p))
31+
.filter(|&p| grid[p] == '.')
32+
.for_each(|p| {
33+
if min_dst[p] > min_dst[current] + 1 {
34+
min_dst[p] = min_dst[current] + 1;
35+
q.push_back((p, min_dst[p]));
36+
}
37+
})
38+
}
39+
}
40+
41+
fn find_shortest_path(input: &str, width: usize, height: usize, take: usize) -> Option<u32> {
42+
let mut grid = Grid::new(width, height, '.');
43+
let points = parse_points(input);
44+
points.iter().take(take).for_each(|&p| {
45+
grid[p] = '#';
46+
});
47+
48+
let start = Point::new(0, 0);
49+
let end = Point::new(width as isize - 1, height as isize - 1);
50+
let mut min_dst = Grid::new(width, height, u32::MAX);
51+
min_dst[start] = 0;
52+
53+
fill_min_dst_grid(&grid, &mut min_dst, (start, 0), end);
54+
55+
Some(min_dst[end])
56+
}
57+
58+
pub fn part_one(input: &str) -> Option<u32> {
59+
find_shortest_path(input, 71, 71, 1024)
60+
}
61+
62+
// work in reverse, fill them all in and find the first point that makes it possible to reach the end
63+
fn find_cutoff(input: &str, width: usize, height: usize, _initial_take: usize) -> Option<Point> {
64+
let mut grid = Grid::new(width, height, '.');
65+
let points = parse_points(input);
66+
67+
points.iter().for_each(|&p| {
68+
grid[p] = '#';
69+
});
70+
71+
let start = Point::new(0, 0);
72+
let end = Point::new(width as isize - 1, height as isize - 1);
73+
let mut min_dst = Grid::new(width, height, u32::MAX);
74+
min_dst[start] = 0;
75+
76+
fill_min_dst_grid(&grid, &mut min_dst, (start, 0), end);
77+
78+
let mut idx = points.len();
79+
while min_dst[end] == u32::MAX {
80+
idx -= 1;
81+
82+
let p = points[idx];
83+
grid[p] = '.';
84+
85+
let min_neighbour_dst = ORTHOGONAL
86+
.iter()
87+
.map(|&dir| p + dir)
88+
.filter(|&p| grid.is_in_bounds(p))
89+
.filter(|&p| grid[p] == '.')
90+
.map(|p| min_dst[p])
91+
.min()
92+
.unwrap_or(u32::MAX);
93+
94+
if min_neighbour_dst == u32::MAX {
95+
continue;
96+
}
97+
let dst = min_neighbour_dst + 1;
98+
99+
fill_min_dst_grid(&grid, &mut min_dst, (p, dst), end);
100+
}
101+
102+
Some(points[idx])
103+
}
104+
105+
pub fn part_two(input: &str) -> Option<String> {
106+
let cutoff = find_cutoff(input, 71, 71, 1024);
107+
cutoff.map(|p| format!("{},{}", p.column, p.line))
108+
}
109+
110+
#[cfg(test)]
111+
mod tests {
112+
use super::*;
113+
114+
#[test]
115+
fn test_part_one() {
116+
let result = find_shortest_path(
117+
&advent_of_code::template::read_file("examples", DAY),
118+
7,
119+
7,
120+
12,
121+
);
122+
assert_eq!(result, Some(22));
123+
}
124+
125+
#[test]
126+
fn test_part_two() {
127+
let result = find_cutoff(
128+
&advent_of_code::template::read_file("examples", DAY),
129+
7,
130+
7,
131+
12,
132+
);
133+
assert_eq!(result, Some(Point::new(1, 6)));
134+
}
135+
}

0 commit comments

Comments
 (0)