AoC2021/src/day04.rs

209 lines
5.2 KiB
Rust

use crate::Day;
use anyhow::Result;
#[derive(Clone)]
pub struct Day04 {
numbers: Vec<u32>,
boards: Vec<Board>,
}
#[derive(Clone, Eq, PartialEq, Debug)]
struct Board(Vec<Vec<Option<u32>>>);
impl Day for Day04 {
fn init(content: String) -> Result<Self> {
let l = content.lines().collect::<Vec<&str>>();
let (a, b) = l.split_at(2);
let first = a[0];
let nums = first.split(',').map(str::parse).collect::<Result<Vec<_>, _>>()?;
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::<u32>().expect("foo")
).map(Option::Some).collect()).collect()
).map(Board).collect();
Ok(Self {
numbers: nums,
boards,
})
}
fn part1(&self) -> Result<String> {
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<String> {
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<T: Clone>(v: &[Vec<T>]) -> Vec<Vec<T>> {
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::<Vec<T>>()
})
.collect()
}
fn unmarked_sum(&self) -> u32 {
self.0.iter().map(|x| x.iter().flatten().sum::<u32>()).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");
}