-
Notifications
You must be signed in to change notification settings - Fork 9
/
day11.rs
92 lines (76 loc) · 2.67 KB
/
day11.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! # Dumbo Octopus
//!
//! This puzzle resembles the [`Day 9`] flood fill a little. Since there are only 100 octopuses
//! a fixed size array is used both to track current energy levels and a second array to track
//! if an octopus has flashed this turn. Each time an octopus flashes it bumps its neighbors
//! energy levels, which can propagate recursively through the entire grid.
//!
//! [`Day 9`]: crate::year2021::day09
/// Pad the 10x10 grid by 1 on either side so that we can avoid boundary checks.
type Input = [u8; 144];
/// Offsets for each of the eight neighbors. The last four offsets will wrap around when
/// added to `u8` to give a smaller index.
const NEIGHBORS: [u8; 8] = [1, 11, 12, 13, 243, 244, 245, 255];
pub fn parse(input: &str) -> Input {
let bytes: Vec<_> = input.lines().map(str::as_bytes).collect();
let mut grid = [0; 144];
for y in 0..10 {
for x in 0..10 {
grid[12 * (y + 1) + (x + 1)] = bytes[y][x] - b'0';
}
}
grid
}
pub fn part1(input: &Input) -> usize {
let (total, _) = simulate(input, |_, steps| steps < 100);
total
}
pub fn part2(input: &Input) -> usize {
let (_, steps) = simulate(input, |flashes, _| flashes < 100);
steps
}
fn simulate(input: &Input, predicate: impl Fn(usize, usize) -> bool) -> (usize, usize) {
let mut grid = *input;
let mut flashed = [true; 144];
let mut todo = Vec::with_capacity(100);
let mut flashes = 0;
let mut steps = 0;
let mut total = 0;
while predicate(flashes, steps) {
flashes = 0;
// Bump each octopus' energy level by one. If it flashes then add to `todo` queue.
for y in 0..10 {
for x in 0..10 {
let index = 12 * (y + 1) + (x + 1);
if grid[index] < 9 {
grid[index] += 1;
flashed[index] = false;
} else {
grid[index] = 0;
flashed[index] = true;
todo.push(index as u8);
}
}
}
// Process each flash, possibly adding more to the queue.
while let Some(index) = todo.pop() {
flashes += 1;
for offset in NEIGHBORS {
let next = index.wrapping_add(offset) as usize;
if flashed[next] {
continue;
}
if grid[next] < 9 {
grid[next] += 1;
} else {
grid[next] = 0;
flashed[next] = true;
todo.push(next as u8);
}
}
}
steps += 1;
total += flashes;
}
(total, steps)
}