advent-of-code 2022.0.46

Solutions to Advent of Code
Documentation
use crate::input::Input;

pub fn solve(input: &Input) -> Result<u64, String> {
    const MAX_STEPS_PART_TWO: usize = 100_000;

    let mut board = Board::parse(input.text)?;

    for step in 1..=input.part_values(100, MAX_STEPS_PART_TWO) {
        let before = board.num_flashes;

        board.advance();

        if input.is_part_two() && (board.num_flashes - before) == 100 {
            return Ok(step as u64);
        }
    }

    if input.is_part_two() {
        return Err(format!(
            "No simultaneous flash within {MAX_STEPS_PART_TWO} steps"
        ));
    }

    Ok(board.num_flashes)
}

struct Board {
    cells: [u8; Self::WIDTH * Self::WIDTH],
    num_flashes: u64,
}

impl Board {
    const WIDTH: usize = 10;

    fn parse(s: &str) -> Result<Self, String> {
        let mut board = Self {
            cells: [0; Self::WIDTH * Self::WIDTH],
            num_flashes: 0,
        };

        if s.lines().count() != 10 {
            return Err("Board is not 10 rows".to_string());
        }

        for (y, line) in s.lines().enumerate() {
            if line.len() != 10 {
                return Err("Not every row in the board is 10 wide".to_string());
            }
            for (x, b) in line.bytes().enumerate() {
                if !b.is_ascii_digit() {
                    return Err("Not every character is an ASCII digit".to_string());
                }
                board.set(x, y, b - b'0');
            }
        }

        Ok(board)
    }

    const fn at(&self, x: usize, y: usize) -> u8 {
        self.cells[x + (y * Self::WIDTH)]
    }

    fn set(&mut self, x: usize, y: usize, value: u8) {
        self.cells[x + (y * Self::WIDTH)] = value;
    }

    fn bump(&mut self, x: usize, y: usize) {
        let current_value = self.at(x, y);
        if current_value == 0 {
            return;
        }

        self.set(x, y, current_value + 1);

        if current_value + 1 > 9 {
            self.set(x, y, 0);
            self.num_flashes += 1;

            for dy in -1..=1 {
                for dx in -1..=1 {
                    if (dx, dy) != (0, 0) {
                        let n_x = x as i32 + dx;
                        let n_y = y as i32 + dy;
                        if (0..10).contains(&n_x) && (0..10).contains(&n_y) {
                            self.bump(n_x as usize, n_y as usize);
                        }
                    }
                }
            }
        }
    }

    fn advance(&mut self) {
        self.cells.iter_mut().for_each(|c| *c += 1);

        for y in 0..10 {
            for x in 0..10 {
                if self.at(x, y) > 9 {
                    self.bump(x, y);
                }
            }
        }
    }
}

#[test]
pub fn tests() {
    use crate::input::{test_part_one, test_part_two};

    let example = "5483143223
2745854711
5264556173
6141336146
6357385478
4167524645
2176841721
6882881134
4846848554
5283751526";
    test_part_one!(example => 1656);
    test_part_two!(example => 195);

    let real_input = include_str!("day11_input.txt");
    test_part_one!(real_input => 1546);
    test_part_two!(real_input => 471);
}