209 lines
5.2 KiB
Rust
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");
|
|
} |