use std::mem::{self, MaybeUninit}; const BOARD_SIZE: usize = 5; #[derive(Clone, Debug)] struct Board { cells: [[(u8, bool); BOARD_SIZE]; BOARD_SIZE], } fn part1(numbers: &[u8], mut boards: Vec) { for n in numbers { for board in boards.iter_mut() { board.check_off(*n); if board.is_bingo() { println!( "Solution to part 1: {}", board.sum_non_checked() * *n as u32 ); return; } } } } fn part2(numbers: &[u8], mut boards: Vec) { let mut last_board_score = 0; for n in numbers { for board in boards.iter_mut() { if !board.is_bingo() { board.check_off(*n); let board_score = board.sum_non_checked() * *n as u32; if board.is_bingo() && board_score != 0 { last_board_score = board_score; } } } } println!("Solution to part 2: {}", last_board_score); } pub fn run(input: Vec) { // Read the order in which the numbers will be drawn let numbers: Vec = input[0].split(',').map(|s| s.parse().unwrap()).collect(); // Turn all the remaining lines into boards, each board get's it's number of // rows + 1 for the empty line below it, which will be ignored. let boards: Vec = input[2..] .chunks(BOARD_SIZE + 1) .map(|c| Board::from_strings(c)) .collect(); part1(&numbers, boards.clone()); part2(&numbers, boards); } impl Board { pub fn from_strings(strings: &[String]) -> Self { Self { cells: { let mut cells: [[MaybeUninit<(u8, bool)>; BOARD_SIZE]; BOARD_SIZE] = unsafe { MaybeUninit::uninit().assume_init() }; for (s, row) in strings.iter().zip(cells.iter_mut()) { for (c, cell) in s.split_whitespace().zip(row.iter_mut()) { cell.write((c.parse().unwrap(), false)); } } unsafe { mem::transmute::<_, [[(u8, bool); BOARD_SIZE]; BOARD_SIZE]>(cells) } }, } } pub fn check_off(&mut self, number: u8) { for row in &mut self.cells { for (n, checked) in row { if number == *n { *checked = true; } } } } pub fn is_bingo(&self) -> bool { /* self.is_diagonal_bingo() || */ self.is_row_bingo() || self.is_column_bingo() } fn is_diagonal_bingo(&self) -> bool { let top_left_to_bot_right = (0..BOARD_SIZE).all(|i| self.cells[i][i].1); let bot_left_to_top_right = (0..BOARD_SIZE).all(|i| self.cells[i][i].1); top_left_to_bot_right || bot_left_to_top_right } fn is_row_bingo(&self) -> bool { self.cells.iter().any(|row| row.iter().all(|c| c.1)) } fn is_column_bingo(&self) -> bool { for col in 0..BOARD_SIZE { if (0..BOARD_SIZE).all(|i| self.cells[i][col].1) { return true; } } false } pub fn sum_non_checked(&self) -> u32 { self.cells .iter() .flatten() .filter_map(|&(n, checked)| { if !checked { Some(n as u32) } else { None } }) .sum() } }