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)
}