Skip to content

Commit b9bbb06

Browse files
committed
2024 day 6
1 parent 3c65386 commit b9bbb06

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
....#.....
2+
.........#
3+
..........
4+
..#.......
5+
.......#..
6+
..........
7+
.#..^.....
8+
........#.
9+
#.........
10+
......#...

crates/year2024/src/day06.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use utils::grid;
2+
use utils::point::Point2D;
3+
use utils::prelude::*;
4+
5+
/// Finding obstructions to cause infinite loops.
6+
#[derive(Clone, Debug)]
7+
pub struct Day06 {
8+
pub rows: usize,
9+
pub cols: usize,
10+
pub grid: Vec<u8>,
11+
pub start: Point2D<usize>,
12+
}
13+
14+
const DIRECTIONS: [Point2D<isize>; 4] = [Point2D::DOWN, Point2D::RIGHT, Point2D::UP, Point2D::LEFT];
15+
16+
impl Day06 {
17+
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
18+
let (rows, cols, mut grid) = grid::from_str(input, |b| match b {
19+
b'.' | b'#' | b'^' => Some(b),
20+
_ => None,
21+
})?;
22+
23+
let start_index = grid.iter().position(|&c| c == b'^').unwrap();
24+
let start = Point2D::new(start_index % cols, start_index / cols);
25+
grid[start_index] = b'.';
26+
27+
Ok(Self {
28+
rows,
29+
cols,
30+
grid,
31+
start,
32+
})
33+
}
34+
35+
#[must_use]
36+
pub fn part1(&self) -> usize {
37+
let mut pos = self.start;
38+
let mut dir = 0;
39+
let mut visited = vec![false; self.grid.len()];
40+
loop {
41+
visited[pos.y * self.cols + pos.x] = true;
42+
43+
let next = pos.wrapping_add_signed(DIRECTIONS[dir]);
44+
if next.x >= self.cols || next.y >= self.rows {
45+
break;
46+
}
47+
if self.grid[next.y * self.cols + next.x] == b'#' {
48+
dir = (dir + 1) % 4;
49+
} else {
50+
pos = next;
51+
}
52+
}
53+
visited.iter().filter(|&&c| c).count()
54+
}
55+
56+
#[must_use]
57+
pub fn part2(&self) -> usize {
58+
let mut pos = self.start;
59+
let mut dir = 0;
60+
let mut visited = vec![0u8; self.grid.len()];
61+
let mut obstructions = vec![false; self.grid.len()];
62+
loop {
63+
visited[pos.y * self.cols + pos.x] |= 1 << dir;
64+
65+
let next = pos.wrapping_add_signed(DIRECTIONS[dir]);
66+
if next.x >= self.cols || next.y >= self.rows {
67+
break;
68+
}
69+
70+
if self.grid[next.y * self.cols + next.x] == b'#' {
71+
dir = (dir + 1) % 4;
72+
} else {
73+
if !obstructions[next.y * self.cols + next.x]
74+
&& visited[next.y * self.cols + next.x] == 0
75+
&& self.check_cycle(next, pos, dir, &visited)
76+
{
77+
obstructions[next.y * self.cols + next.x] = true;
78+
}
79+
80+
pos = next;
81+
}
82+
}
83+
obstructions.iter().filter(|&&c| c).count()
84+
}
85+
86+
// Combination of two algorithms starting from the current position:
87+
// 1) Checking against previously visited states/if position leaves grid
88+
// 2) The start of Brent's algorithm for cycle detection as used in 2017 day 6
89+
// This also avoids allocating/zeroing/copying a new visited vec
90+
fn check_cycle(
91+
&self,
92+
obstruction: Point2D<usize>,
93+
pos: Point2D<usize>,
94+
dir: usize,
95+
visited: &[u8],
96+
) -> bool {
97+
let (mut power, mut lambda) = (1, 1);
98+
let (mut tortoise_pos, mut tortoise_dir) = (pos, dir);
99+
let (mut hare_pos, mut hare_dir) = (pos, dir);
100+
101+
loop {
102+
if power == lambda {
103+
tortoise_pos = hare_pos;
104+
tortoise_dir = hare_dir;
105+
power *= 2;
106+
lambda = 0;
107+
}
108+
lambda += 1;
109+
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;
117+
} else {
118+
hare_pos = next;
119+
}
120+
121+
if visited[hare_pos.y * self.cols + hare_pos.x] & (1 << hare_dir) != 0 {
122+
// Cycle, hare has reached a previous state from before adding the obstacle
123+
return true;
124+
}
125+
if hare_pos == tortoise_pos && hare_dir == tortoise_dir {
126+
// Cycle, hare and tortoise are in the same state
127+
return true;
128+
}
129+
}
130+
}
131+
}
132+
133+
examples!(Day06 -> (usize, usize) [
134+
{file: "day06_example0.txt", part1: 41, part2: 6},
135+
]);

crates/year2024/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ utils::year!(2024 => year2024, ${
77
3 => day03::Day03,
88
4 => day04::Day04,
99
5 => day05::Day05,
10+
6 => day06::Day06,
1011
});

0 commit comments

Comments
 (0)