Skip to content

Commit 1267349

Browse files
committed
Optimize 2024 day 6
When checking for cycles, advance to the next obstruction instead of a single step, and cache how many steps there are until the next obstruction. For my input, the cache can be used ~90% of the time, and the hit rate is ~98% when it can be used. This reduces runtime from ~6ms to ~1ms.
1 parent a76009e commit 1267349

File tree

1 file changed

+44
-9
lines changed

1 file changed

+44
-9
lines changed

crates/year2024/src/day06.rs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ impl Day06 {
5959
let mut dir = 0;
6060
let mut visited = vec![0u8; self.grid.len()];
6161
let mut obstructions = vec![false; self.grid.len()];
62+
let mut cached_step_counts = vec![[0; 4]; self.grid.len()];
6263
loop {
6364
visited[pos.y * self.cols + pos.x] |= 1 << dir;
6465

@@ -72,7 +73,7 @@ impl Day06 {
7273
} else {
7374
if !obstructions[next.y * self.cols + next.x]
7475
&& visited[next.y * self.cols + next.x] == 0
75-
&& self.check_cycle(next, pos, dir, &visited)
76+
&& self.check_cycle(next, pos, dir, &visited, &mut cached_step_counts)
7677
{
7778
obstructions[next.y * self.cols + next.x] = true;
7879
}
@@ -93,6 +94,7 @@ impl Day06 {
9394
pos: Point2D<usize>,
9495
dir: usize,
9596
visited: &[u8],
97+
cache: &mut [[isize; 4]],
9698
) -> bool {
9799
let (mut power, mut lambda) = (1, 1);
98100
let (mut tortoise_pos, mut tortoise_dir) = (pos, dir);
@@ -107,17 +109,50 @@ impl Day06 {
107109
}
108110
lambda += 1;
109111

110-
let next = hare_pos.wrapping_add_signed(DIRECTIONS[hare_dir]);
111-
if next.x >= self.cols || next.y >= self.rows {
112-
// No cycle, hare has left the grid
113-
return false;
114-
}
115-
if self.grid[next.y * self.cols + next.x] == b'#' || next == obstruction {
116-
hare_dir = (hare_dir + 1) % 4;
112+
// Advance to the next obstruction
113+
if hare_pos.x == obstruction.x || hare_pos.y == obstruction.y {
114+
// On the same X or Y line as the temporary obstruction, loop without caching
115+
loop {
116+
let next = hare_pos.wrapping_add_signed(DIRECTIONS[hare_dir]);
117+
if next.x >= self.cols || next.y >= self.rows {
118+
// No cycle, hare has left the grid
119+
return false;
120+
}
121+
if self.grid[next.y * self.cols + next.x] == b'#' || next == obstruction {
122+
break;
123+
}
124+
hare_pos = next;
125+
}
117126
} else {
118-
hare_pos = next;
127+
// Temporary obstruction can be ignored as not on the same X or Y line as it
128+
let cached_count = &mut cache[hare_pos.y * self.cols + hare_pos.x][hare_dir];
129+
if *cached_count > 0 {
130+
// Advanced by the previously cached count
131+
hare_pos = hare_pos.wrapping_add_signed(DIRECTIONS[hare_dir] * *cached_count);
132+
if hare_pos.x >= self.cols || hare_pos.y >= self.rows {
133+
// No cycle, hare has left the grid
134+
return false;
135+
}
136+
} else {
137+
// Loop, caching the step count until the next obstruction
138+
loop {
139+
let next = hare_pos.wrapping_add_signed(DIRECTIONS[hare_dir]);
140+
if next.x >= self.cols || next.y >= self.rows {
141+
// No cycle, hare has left the grid
142+
*cached_count += 1;
143+
return false;
144+
}
145+
if self.grid[next.y * self.cols + next.x] == b'#' {
146+
break;
147+
}
148+
hare_pos = next;
149+
*cached_count += 1;
150+
}
151+
}
119152
}
120153

154+
hare_dir = (hare_dir + 1) % 4;
155+
121156
if visited[hare_pos.y * self.cols + hare_pos.x] & (1 << hare_dir) != 0 {
122157
// Cycle, hare has reached a previous state from before adding the obstacle
123158
return true;

0 commit comments

Comments
 (0)