advent/2021/src/day4.rs
2021-12-07 10:37:24 -08:00

212 lines
6.1 KiB
Rust

use std::fmt;
use color_eyre::eyre;
use eyre::eyre;
struct Ratchet<T, F>
where T: Copy,
F: Fn(T) -> u8,
{
value: Option<T>,
getter: F,
}
impl<T, F> Ratchet<T, F>
where T: Copy,
F: Fn(T) -> u8
{
fn new(getter: F) -> Self {
Ratchet {
value: None,
getter,
}
}
fn inc(&mut self, other: T) {
match self.value {
Some(v) if (self.getter)(other) <= (self.getter)(v) => (),
_ => self.value = Some(other),
};
}
fn dec(&mut self, other: T) {
match self.value {
Some(v) if (self.getter)(other) >= (self.getter)(v) => (),
_ => self.value = Some(other),
};
}
}
#[derive(Copy, Clone, Default, Debug)]
struct Square {
value: u8,
draw_time: Option<u8>,
}
#[derive(Copy, Clone, Default, Debug)]
struct Board {
squares: [[Square; 5]; 5],
bingo_time: Option<u8>,
bingo_value: Option<u8>
}
impl Board {
fn from(squares: &[Square]) -> eyre::Result<Self> {
let mut it = squares.iter();
let mut b = Self::default();
for row in 0..5 {
for col in 0..5 {
match it.next() {
Some(sq) => b.squares[row][col] = *sq,
None => return Err(eyre!("Not enough values to populate board")),
}
}
}
Ok(b)
}
fn set_bingo(&mut self) {
let mut board_bingo = Ratchet::new(|sq: Square| sq.draw_time.unwrap()); // these unwraps are safe because we will only call inc() or dec() after ensuring there is a value there
let mut cols_bingo: [bool; 5] = [true; 5];
let mut cols_last_drawn: Vec<Ratchet<Square, _>> = (0..5)
.map(|_i| Ratchet::new(|sq: Square| sq.draw_time.unwrap()))
.collect();
// let mut cols_last_drawn: [Ratchet<Square, _>; 5] = [Ratchet::new(|sq: Square| sq.draw_time.unwrap()); 5];
for row in self.squares {
let mut row_bingo = true;
let mut row_last_drawn = Ratchet::new(|sq: Square| sq.draw_time.unwrap());
for (col, sq) in row.iter().enumerate() {
match sq.draw_time {
Some(_) => {
row_last_drawn.inc(*sq);
cols_last_drawn[col].inc(*sq);
},
None => {
row_bingo = false;
cols_bingo[col] = false;
break;
}
}
}
if row_bingo {
let bingo_sq = row_last_drawn.value.unwrap(); // we know there will be a value here if row_bingo is true
board_bingo.dec(bingo_sq); //
}
}
for col in 0..5 {
if cols_bingo[col] {
let bingo_sq = cols_last_drawn[col].value.unwrap(); // similar to above
board_bingo.dec(bingo_sq);
}
}
if let Some(sq) = board_bingo.value {
self.bingo_time = sq.draw_time;
self.bingo_value = Some(sq.value);
}
}
fn score(&self) -> eyre::Result<isize> {
let bingo_time = match self.bingo_time {
Some(t) => t,
None => return Err(eyre!("Cannot find the score of a non-winning board")),
};
let mut total = 0;
for row in self.squares {
for sq in row {
if sq.draw_time.is_some() && sq.draw_time.unwrap() > bingo_time {
total += sq.value as isize;
}
}
}
let multiplier = self.bingo_value.unwrap() as isize;
Ok(total * multiplier)
}
}
impl fmt::Display for Board {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for row in self.squares {
write!(f, "|")?;
for (i, sq) in row.iter().enumerate() {
let dt = sq.draw_time.map(|dt| format!("{}", dt)).unwrap_or(String::from("__"));
write!(f, "({}: {})", sq.value, dt)?;
if i < 4 {
write!(f, ", ")?;
}
}
write!(f, "|\n")?;
}
if self.bingo_time.is_some() {
write!(f, "Bingo: ({}: {})", self.bingo_value.unwrap(), self.bingo_time.unwrap())
}
else {
write!(f, "Bingo: (__: __)")
}
}
}
fn load(data: &str) -> eyre::Result<Vec<Board>> {
let mut lines = data.lines().peekable();
let mut draws: [Option<u8>; 100] = [None; 100]; // indices are the number drawn, values are the position in which it was drawn
for (i, draw) in lines.next().unwrap().split(',').enumerate() {
let num = draw.parse::<usize>()?;
draws[num] = Some(i.try_into().unwrap()); // will panic if there are more than 256 items in this iterator, but there aren't
}
let mut boards = Vec::new();
let mut squares = Vec::new();
for line in lines {
for s in line.split_whitespace() {
let value = s.parse::<u8>()?; // shadows the loop variable I guess?
let sq = Square {value, draw_time: draws[value as usize]};
squares.push(sq);
}
if squares.len() == 25 {
boards.push(Board::from(&squares)?);
boards.last_mut().unwrap().set_bingo();
squares.clear();
}
}
Ok(boards)
}
fn part1(boards: &[Board]) -> eyre::Result<Board> {
let winner = boards.iter()
.filter(|b| b.bingo_time.is_some())
.min_by_key(|b| b.bingo_time.unwrap());
match winner {
Some(w) => Ok(*w),
None => Err(eyre!("Could not find a winning board")),
}
}
fn part2(boards: &[Board]) -> eyre::Result<Board> {
let winner = boards.iter()
.filter(|b| b.bingo_time.is_some())
.max_by_key(|b| b.bingo_time.unwrap());
match winner {
Some(w) => Ok(*w),
None => Err(eyre!("Could not find a winning board")),
}
}
pub fn run(data: &str) -> eyre::Result<(isize, isize)> {
let boards = load(data)?;
let winner = part1(&boards)?;
let loser = part2(&boards)?;
Ok((winner.score()?, loser.score()?))
}