use crate::Day; use anyhow::Result; #[derive(Clone)] pub struct Day04 { numbers: Vec, boards: Vec, } #[derive(Clone, Eq, PartialEq, Debug)] struct Board(Vec>>); impl Day for Day04 { fn init(content: String) -> Result { let l = content.lines().collect::>(); let (a, b) = l.split_at(2); let first = a[0]; let nums = first.split(',').map(str::parse).collect::, _>>()?; let split = b.split(|x| x.is_empty()); let boards = split.map(|vecs| vecs.iter().map( |vec| vec.split_ascii_whitespace().map( |x| x.parse::().expect("foo") ).map(Option::Some).collect()).collect() ).map(Board).collect(); Ok(Self { numbers: nums, boards, }) } fn part1(&self) -> Result { let z = self.clone(); let mut boards = z.boards; for number in z.numbers { for board in &mut boards { let res = board.mark(number); if res { let s = board.unmarked_sum(); return Ok(format!("{}", s * number)); } } } Err(anyhow::Error::msg("could not find a working sum...")) } fn part2(&self) -> Result { let z = self.clone(); let mut boards = z.boards; for number in z.numbers { let len = boards.len(); for board in &mut boards { let res = board.mark(number); if res && len == 1 { let s = board.unmarked_sum(); return Ok(format!("{}", s * number)); } } boards = boards.iter().filter(|x| !x.complete()).cloned().collect(); } Err(anyhow::Error::msg("could not find a working sum...")) } } impl Board { fn mark(&mut self, num: u32) -> bool { #[allow(clippy::manual_flatten)] for columns in &mut self.0 { for row in columns.iter_mut() { if let Some(v) = row { if *v == num { *row = None; } } } } self.check_rows() || self.check_columns() } fn complete(&self) -> bool { self.check_rows() || self.check_columns() } fn check_rows(&self) -> bool { for columns in &self.0 { if columns.iter().all(Option::is_none) { return true; } } false } fn check_columns(&self) -> bool { let t = Board::transpose(&self.0); for rows in t { if rows.iter().all(Option::is_none) { return true; } } false } fn transpose(v: &[Vec]) -> Vec> { assert!(!v.is_empty()); let len = v[0].len(); let mut iters: Vec<_> = v.iter().map(|n| n.iter()).collect(); (0..len) .map(|_| { iters .iter_mut() .map(|n| n.next().unwrap()) .cloned() .collect::>() }) .collect() } fn unmarked_sum(&self) -> u32 { self.0.iter().map(|x| x.iter().flatten().sum::()).sum() } } #[cfg(test)] mod tests { use crate::{Day04, day_tests}; use crate::day04::Board; use crate::day::Day; use anyhow::Result; const INPUT: &str = r"7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 22 13 17 11 0 8 2 23 4 24 21 9 14 16 7 6 10 3 18 5 1 12 20 15 19 3 15 0 2 22 9 18 13 17 5 19 8 7 25 23 20 11 10 24 4 14 21 16 12 6 14 21 17 24 4 10 16 15 9 19 18 8 23 26 20 22 11 13 6 5 2 0 12 3 7"; #[test] fn board_mark_test() { let mut b = Board(vec![vec![Some(10)]]); let res = b.mark(10); assert_eq!(b.0[0][0], None); assert!(res) } #[test] fn board_rows_are_marked() { let b = Board(vec![vec![None, None, None, None], vec![Some(10), Some(10), Some(10), Some(10)]]); assert!(b.check_rows()) } #[test] fn board_rows_are_not_marked() { let b = Board(vec![vec![None, None, Some(10), None]]); assert!(!b.check_rows()) } #[test] fn board_cols_are_marked() { let b = Board(vec![vec![None], vec![None], vec![None], vec![None]]); assert!(b.check_columns()) } #[test] fn board_cols_are_not_marked() { let b = Board(vec![vec![None], vec![None], vec![Some(10)], vec![None]]); assert!(!b.check_columns()) } #[test] fn board_cols_are_not_rows() { let b = Board(vec![vec![None, Some(10)], vec![None, Some(10)], vec![None, Some(10)], vec![None, Some(10)]]); assert!(!b.check_rows()) } #[test] fn part1_test() -> Result<()> { let t = Day04::init(INPUT.to_string())?; let p1 = t.part1()?; assert_eq!("4512", p1); Ok(()) } #[test] fn part2_test() -> Result<()> { let t = Day04::init(INPUT.to_string())?; let p2 = t.part2()?; assert_eq!("1924", p2); Ok(()) } day_tests!(Day04, "04", "8136", "12738"); }