use { anyhow::{ anyhow, Context, Result, }, std::{ env::args, fmt, fmt::Display, fs::File, io::{ BufRead, BufReader, }, path::Path, }, }; #[derive(Clone)] struct BingoBoard { size: usize, values: Vec, called: Vec, completed: Option, } impl BingoBoard { fn new<'a, I: IntoIterator + Copy>(grid: I) -> Self { let size = 5; let values = (&grid).into_iter().copied().collect(); let called = vec![false; grid.into_iter().count()]; let completed = None; Self { size, values, called, completed, } } fn play(&mut self, num: &u8) -> Result { if self.completed.is_some() { return Ok(false); } for y in 0..self.size { for x in 0..self.size { let value = self.values.get(y * self.size + x) .ok_or(anyhow!("Invalid BingoBoard"))?; if value == num { self.called[y * self.size + x] = true; } } } for y in 0..self.size { let mut count = 0; for x in 0..self.size { if *self.called.get(y * self.size + x).ok_or(anyhow!("Invalid BingoBoard"))? { count += 1; } if count == self.size { self.completed = Some(*num); return Ok(true); } } } for x in 0..self.size { let mut count = 0; for y in 0..self.size { if *self.called.get(y * self.size + x).ok_or(anyhow!("Invalid BingoBoard"))? { count += 1; } if count == self.size { self.completed = Some(*num); return Ok(true); } } } Ok(false) } fn sum_unmarked(&self) -> Result { let mut sum = 0; for y in 0..self.size { for x in 0..self.size { let value = self.values.get(y * self.size + x) .ok_or(anyhow!("Invalid BingoBoard"))?; if ! *self.called.get(y * self.size + x).ok_or(anyhow!("Invalid BingoBoard"))? { sum += *value as usize; } } } Ok(sum) } } impl Display for BingoBoard { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = String::new(); for y in 0..self.size { for x in 0..self.size { let value = self.values.get(y * self.size + x).ok_or(std::fmt::Error)?; if *self.called.get(y * self.size + x).ok_or(std::fmt::Error)? { s += &format!("[{:2}] ", value); } else { s += &format!(" {:2} ", value); } } s += "\n"; } if let Some(won) = self.completed { s += &format!("Winning number: {}\n", won); } write!(f, "{}", s) } } #[derive(Clone)] struct BingoGame { numbers: Vec, boards: Vec, } impl BingoGame { fn new() -> Self { Self { numbers: vec![], boards: vec![], } } } fn read_input>(filename: T) -> Result { let reader = BufReader::new(File::open(filename)?); let mut game = BingoGame::new(); let mut boards: Vec = vec![]; let mut lines = reader.lines(); let draw_line = lines.next().ok_or(anyhow!("Could not parse draw_line"))? .map_err(|err| anyhow!("{}", err))?; let mut buffer: Vec = vec![]; for line in lines { let l = line?; if l.is_empty() { if ! buffer.is_empty() { let board = BingoBoard::new(&buffer); boards.push(board); } buffer = vec![]; } else { buffer.append(&mut l.split_whitespace().map(|v| v.parse() .map_err(|_| anyhow!("Parse error"))).collect::>()?); } } let board = BingoBoard::new(&buffer); boards.push(board); game.numbers = draw_line.split(',').map(|v| v.parse().map_err(|err| anyhow!("{}", err))) .collect::>()?; game.boards = boards; Ok(game) } fn part1(game: &BingoGame) -> Result { let mut game_mut = game.clone(); for num in &game_mut.numbers { for board in &mut game_mut.boards { if board.play(num)? { // println!("{}", board); return Ok(board.sum_unmarked()? * *num as usize); } } } Err(anyhow!("No winner")) } fn part2(game: &BingoGame) -> Result { let mut game_mut = game.clone(); let mut last_winner = None; for num in &game_mut.numbers { for (index, board) in &mut game_mut.boards.iter_mut().enumerate() { if board.play(num)? { last_winner = Some(index); } } } if let Some(winner) = last_winner { let board = &game_mut.boards[winner]; let unmarked_sum = board.sum_unmarked()?; let won = board.completed.ok_or(anyhow!("No winning number"))? as usize; // println!("Winning board: {} ({} * {})", winner + 1, unmarked_sum, won); // println!("{}", board); Ok(unmarked_sum * won) } else { Err(anyhow!("No winner")) } } fn main() -> Result<()> { let ( do_part_1, do_part_2 ) = aoc::do_parts(); let filename = args().nth(1).ok_or(anyhow!("Missing input filename"))?; let input = read_input(filename).context("Could not read input")?; if do_part_1 { let solution = part1(&input).context("No solution for part 1")?; println!("Part1, solution found to be: {}", solution); } if do_part_2 { let solution = part2(&input).context("No solution for part 2")?; println!("Part2, solution found to be: {}", solution); } Ok(()) }