advent_of_code/year2021/
day11.rs

1use crate::input::Input;
2
3pub fn solve(input: &Input) -> Result<u64, String> {
4    const MAX_STEPS_PART_TWO: usize = 100_000;
5
6    let mut board = Board::parse(input.text)?;
7
8    for step in 1..=input.part_values(100, MAX_STEPS_PART_TWO) {
9        let before = board.num_flashes;
10
11        board.advance();
12
13        if input.is_part_two() && (board.num_flashes - before) == 100 {
14            return Ok(step as u64);
15        }
16    }
17
18    if input.is_part_two() {
19        return Err(format!(
20            "No simultaneous flash within {MAX_STEPS_PART_TWO} steps"
21        ));
22    }
23
24    Ok(board.num_flashes)
25}
26
27struct Board {
28    cells: [u8; Self::WIDTH * Self::WIDTH],
29    num_flashes: u64,
30}
31
32impl Board {
33    const WIDTH: usize = 10;
34
35    fn parse(s: &str) -> Result<Self, String> {
36        let mut board = Self {
37            cells: [0; Self::WIDTH * Self::WIDTH],
38            num_flashes: 0,
39        };
40
41        if s.lines().count() != 10 {
42            return Err("Board is not 10 rows".to_string());
43        }
44
45        for (y, line) in s.lines().enumerate() {
46            if line.len() != 10 {
47                return Err("Not every row in the board is 10 wide".to_string());
48            }
49            for (x, b) in line.bytes().enumerate() {
50                if !b.is_ascii_digit() {
51                    return Err("Not every character is an ASCII digit".to_string());
52                }
53                board.set(x, y, b - b'0');
54            }
55        }
56
57        Ok(board)
58    }
59
60    const fn at(&self, x: usize, y: usize) -> u8 {
61        self.cells[x + (y * Self::WIDTH)]
62    }
63
64    fn set(&mut self, x: usize, y: usize, value: u8) {
65        self.cells[x + (y * Self::WIDTH)] = value;
66    }
67
68    fn bump(&mut self, x: usize, y: usize) {
69        let current_value = self.at(x, y);
70        if current_value == 0 {
71            return;
72        }
73
74        self.set(x, y, current_value + 1);
75
76        if current_value + 1 > 9 {
77            self.set(x, y, 0);
78            self.num_flashes += 1;
79
80            for dy in -1..=1 {
81                for dx in -1..=1 {
82                    if (dx, dy) != (0, 0) {
83                        let n_x = x as i32 + dx;
84                        let n_y = y as i32 + dy;
85                        if (0..10).contains(&n_x) && (0..10).contains(&n_y) {
86                            self.bump(n_x as usize, n_y as usize);
87                        }
88                    }
89                }
90            }
91        }
92    }
93
94    fn advance(&mut self) {
95        self.cells.iter_mut().for_each(|c| *c += 1);
96
97        for y in 0..10 {
98            for x in 0..10 {
99                if self.at(x, y) > 9 {
100                    self.bump(x, y);
101                }
102            }
103        }
104    }
105}
106
107#[test]
108pub fn tests() {
109    use crate::input::{test_part_one, test_part_two};
110
111    let example = "5483143223
1122745854711
1135264556173
1146141336146
1156357385478
1164167524645
1172176841721
1186882881134
1194846848554
1205283751526";
121    test_part_one!(example => 1656);
122    test_part_two!(example => 195);
123
124    let real_input = include_str!("day11_input.txt");
125    test_part_one!(real_input => 1546);
126    test_part_two!(real_input => 471);
127}