Skip to content

Commit 09ee9c8

Browse files
authored
Populate grid lazily (#40)
Rather than populating 2704 cells up front, then running the maze until all (reachable) cells have been visited, it is more efficient to populate the grid as we explore, and to quit exploring as soon as we have our answer. Cuts runtime from 6.4us to 3.1us on my machine. It is not worth the overhead of trying to steer the part 1 solution with an A* search.
1 parent 7ec1a2f commit 09ee9c8

File tree

1 file changed

+20
-20
lines changed

1 file changed

+20
-20
lines changed

src/year2016/day13.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
//! [BFS](https://en.wikipedia.org/wiki/Breadth-first_search) solving both part one and part
55
//! two simultaneously.
66
//!
7-
//! As we start at (1, 1) and the most steps that we are interested in is 50, we can bound
8-
//! the maze to 2 + 50 = 52 in each dimension and used a fixed size array.
7+
//! As we start at (1, 1) and the most steps that we are interested in for part 2 is 50, while
8+
//! part 1 requires more than 50 steps but should easily be reachable without exceeding bounds,
9+
//! we can bound the maze to 2 + 50 = 52 in each dimension and use a fixed size array. Rather
10+
//! than filling the array up front, we can lazily populate it as the horizon expands.
911
1012
use crate::util::parse::*;
1113
use std::collections::VecDeque;
@@ -14,49 +16,47 @@ type Input = (u32, u32);
1416

1517
pub fn parse(input: &str) -> Input {
1618
let favorite: usize = input.unsigned();
17-
let mut maze = [[false; 52]; 52];
1819

19-
for (x, row) in maze.iter_mut().enumerate() {
20-
for (y, cell) in row.iter_mut().enumerate() {
21-
let n = (x * x) + (3 * x) + (2 * x * y) + y + (y * y) + favorite;
22-
*cell = n.count_ones().is_multiple_of(2);
20+
// Lazy evaluation: set maze[x][y] to true once a point is visited
21+
let mut maze = [[false; 52]; 52];
22+
maze[1][1] = true;
23+
let mut at = |x: usize, y: usize| -> bool {
24+
if maze[x][y] {
25+
return false;
2326
}
24-
}
27+
maze[x][y] = true;
28+
let n = (x * x) + (3 * x) + (2 * x * y) + y + (y * y) + favorite;
29+
n.count_ones().is_multiple_of(2)
30+
};
2531

26-
let mut part_one = 0;
2732
let mut part_two = 0;
2833
let mut todo = VecDeque::new();
2934

3035
todo.push_back((1, 1, 0));
31-
maze[1][1] = false;
3236

3337
while let Some((x, y, cost)) = todo.pop_front() {
3438
if x == 31 && y == 39 {
35-
part_one = cost;
39+
return (cost, part_two);
3640
}
3741
if cost <= 50 {
3842
part_two += 1;
3943
}
4044

41-
if x > 0 && maze[x - 1][y] {
45+
if x > 0 && at(x - 1, y) {
4246
todo.push_back((x - 1, y, cost + 1));
43-
maze[x - 1][y] = false;
4447
}
45-
if y > 0 && maze[x][y - 1] {
48+
if y > 0 && at(x, y - 1) {
4649
todo.push_back((x, y - 1, cost + 1));
47-
maze[x][y - 1] = false;
4850
}
49-
if x < 51 && maze[x + 1][y] {
51+
if at(x + 1, y) {
5052
todo.push_back((x + 1, y, cost + 1));
51-
maze[x + 1][y] = false;
5253
}
53-
if y < 51 && maze[x][y + 1] {
54+
if at(x, y + 1) {
5455
todo.push_back((x, y + 1, cost + 1));
55-
maze[x][y + 1] = false;
5656
}
5757
}
5858

59-
(part_one, part_two)
59+
unreachable!();
6060
}
6161

6262
pub fn part1(input: &Input) -> u32 {

0 commit comments

Comments
 (0)