Skip to content

Commit 24351d7

Browse files
committed
Day 20
1 parent 8119c89 commit 24351d7

File tree

3 files changed

+182
-1
lines changed

3 files changed

+182
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
3030
| [Day 17](./src/bin/17.rs) | `1.7µs` | `361.3µs` |
3131
| [Day 18](./src/bin/18.rs) | `49.6µs` | `58.9µs` |
3232
| [Day 19](./src/bin/19.rs) | `204.0µs` | `693.5µs` |
33+
| [Day 20](./src/bin/20.rs) | `325.0µs` | `4.6ms` |
3334

34-
**Total: 14.46ms**
35+
**Total: 19.39ms**
3536
<!--- benchmarking table --->
3637

3738
---

data/examples/20.txt

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

src/bin/20.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
use mygrid::{direction::ORTHOGONAL, grid::Grid, point::Point};
2+
use rayon::iter::ParallelIterator;
3+
4+
advent_of_code::solution!(20);
5+
6+
#[inline]
7+
fn parse_input(input: &str) -> (Grid<char>, Point, Point) {
8+
let mut grid = Grid::new_char_grid_from_str(input);
9+
let start = grid.find_position_of(&'S').unwrap();
10+
let end = grid.find_position_of(&'E').unwrap();
11+
grid[start] = '.';
12+
grid[end] = '.';
13+
(grid, start, end)
14+
}
15+
16+
#[inline]
17+
fn dijkstra(grid: &Grid<char>, start: Point, end: Point) -> Grid<i64> {
18+
let mut cost = Grid::new(grid.width, grid.height, i64::MAX);
19+
let mut state = Some((0, start));
20+
21+
while let Some((dst, pos)) = state {
22+
cost[pos] = dst;
23+
24+
if pos == end {
25+
break;
26+
}
27+
28+
let next_cost = dst + 1;
29+
30+
let next_pos = ORTHOGONAL
31+
.iter()
32+
.map(|&d| pos + d)
33+
.filter(|p| grid.is_in_bounds(*p))
34+
.filter(|&p| grid[p] != '#')
35+
.filter(|&p| cost[p] > next_cost)
36+
.next();
37+
38+
match next_pos {
39+
Some(p) => state = Some((next_cost, p)),
40+
None => panic!("No next position"),
41+
}
42+
}
43+
44+
cost
45+
}
46+
47+
#[inline]
48+
fn diamond_iter<const DIAMOND_RADIUS: isize>(
49+
start_pos: Point,
50+
) -> impl Iterator<Item = (Point, i64)> {
51+
// this could be more efficient tbh
52+
(0..(DIAMOND_RADIUS * 2 + 1))
53+
.into_iter()
54+
.flat_map(move |i| {
55+
let line = start_pos.line - DIAMOND_RADIUS + i;
56+
(0..(DIAMOND_RADIUS * 2 + 1))
57+
.into_iter()
58+
.map(move |j: isize| {
59+
let col = start_pos.column - DIAMOND_RADIUS + j;
60+
let pos = Point::new(line, col);
61+
let moves =
62+
(start_pos.line - pos.line).abs() + (start_pos.column - pos.column).abs();
63+
(pos, moves as i64)
64+
})
65+
})
66+
.filter(|&(_, moves)| moves <= DIAMOND_RADIUS as i64)
67+
}
68+
69+
fn solve<const CHEAT_MOVES: isize>(input: &str, min_gain: i64) -> Option<i64> {
70+
let (grid, start, end) = parse_input(input);
71+
let cost = dijkstra(&grid, start, end);
72+
73+
let count = cost
74+
// makes p1 slightly slower, but p2 much faster
75+
.par_iter_item_and_position()
76+
.filter(|&(_, c)| *c != i64::MAX)
77+
.map(|(start_pos, &start_cost)| {
78+
let count = diamond_iter::<CHEAT_MOVES>(start_pos)
79+
.filter(|&(pos, _)| cost.is_in_bounds(pos))
80+
.filter(|&(pos, _)| cost[pos] != i64::MAX)
81+
.filter(|&(pos, moves)| cost[pos] > start_cost + moves as i64)
82+
.filter(|&(pos, moves)| cost[pos] - (start_cost + moves as i64) >= min_gain)
83+
.count() as i64;
84+
85+
count
86+
})
87+
.sum();
88+
Some(count)
89+
}
90+
91+
pub fn part_one(input: &str) -> Option<i64> {
92+
solve::<2>(input, 100)
93+
}
94+
95+
pub fn part_two(input: &str) -> Option<i64> {
96+
solve::<20>(input, 100)
97+
}
98+
99+
#[cfg(test)]
100+
mod tests {
101+
use super::*;
102+
103+
#[test]
104+
fn test_part_one_0() {
105+
let input = advent_of_code::template::read_file("examples", DAY);
106+
let result = solve::<2>(&input, 0);
107+
assert_eq!(result, Some(44));
108+
}
109+
110+
#[test]
111+
fn test_part_one_64() {
112+
let input = advent_of_code::template::read_file("examples", DAY);
113+
let result = solve::<2>(&input, 64);
114+
assert_eq!(result, Some(1));
115+
}
116+
117+
#[test]
118+
fn test_part_one_40() {
119+
let input = advent_of_code::template::read_file("examples", DAY);
120+
let result = solve::<2>(&input, 40);
121+
assert_eq!(result, Some(2));
122+
}
123+
124+
#[test]
125+
fn test_part_one_20() {
126+
let input = advent_of_code::template::read_file("examples", DAY);
127+
let result = solve::<2>(&input, 20);
128+
assert_eq!(result, Some(5));
129+
}
130+
131+
#[test]
132+
fn test_part_two_50() {
133+
let input = advent_of_code::template::read_file("examples", DAY);
134+
let result = solve::<20>(&input, 50);
135+
assert_eq!(result, Some(285));
136+
}
137+
138+
#[test]
139+
fn test_part_two_64() {
140+
let input = advent_of_code::template::read_file("examples", DAY);
141+
let result = solve::<20>(&input, 64);
142+
assert_eq!(result, Some(86));
143+
}
144+
145+
#[test]
146+
fn test_part_two_70() {
147+
let input = advent_of_code::template::read_file("examples", DAY);
148+
let result = solve::<20>(&input, 70);
149+
assert_eq!(result, Some(41));
150+
}
151+
152+
#[test]
153+
fn test_part_two_74() {
154+
let input = advent_of_code::template::read_file("examples", DAY);
155+
let result = solve::<20>(&input, 74);
156+
assert_eq!(result, Some(7));
157+
}
158+
159+
#[test]
160+
fn test_part_two_76() {
161+
let input = advent_of_code::template::read_file("examples", DAY);
162+
let result = solve::<20>(&input, 76);
163+
assert_eq!(result, Some(3));
164+
}
165+
}

0 commit comments

Comments
 (0)