mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
140 lines
3.2 KiB
Rust
140 lines
3.2 KiB
Rust
use std::collections::HashMap;
|
|
use std::io::Read;
|
|
|
|
use crate::common::LineIter;
|
|
|
|
#[derive(Debug)]
|
|
struct BingoCard {
|
|
ticks: [[bool; 5]; 5],
|
|
mapping: HashMap<u8, (u8, u8)>,
|
|
}
|
|
|
|
impl BingoCard {
|
|
pub fn cross(&mut self, num: u8) -> bool {
|
|
if let Some(&(x, y)) = self.mapping.get(&num) {
|
|
self.ticks[y as usize][x as usize] = true;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn has_won(&self) -> bool {
|
|
// Check horizontal lines
|
|
if self.ticks.iter().any(|s| s.iter().all(|&b| b)) {
|
|
return true;
|
|
}
|
|
|
|
// Check vertical lines
|
|
(0..5).any(|x| self.ticks.iter().all(|row| row[x]))
|
|
|
|
// Diagonals do not count
|
|
}
|
|
|
|
pub fn remaining(&self) -> u32 {
|
|
self.mapping
|
|
.iter()
|
|
.filter_map(|(&num, &(x, y))| {
|
|
if self.ticks[y as usize][x as usize] {
|
|
None
|
|
} else {
|
|
Some(num as u32)
|
|
}
|
|
})
|
|
.sum()
|
|
}
|
|
}
|
|
|
|
fn read_input(input: &mut dyn Read) -> (Vec<u8>, Vec<BingoCard>) {
|
|
let mut reader = LineIter::new(input);
|
|
|
|
let numbers: Vec<u8> = reader
|
|
.next()
|
|
.unwrap()
|
|
.split(',')
|
|
.map(|num| num.parse().unwrap())
|
|
.collect();
|
|
|
|
let mut bingo_cards = Vec::new();
|
|
|
|
while reader.next().is_some() {
|
|
let mut mapping = HashMap::with_capacity(25);
|
|
|
|
for y in 0..5 {
|
|
reader
|
|
.next()
|
|
.unwrap()
|
|
.split(' ')
|
|
.filter_map(|s| if s.is_empty() { None } else { s.parse().ok() })
|
|
.enumerate()
|
|
.for_each(|(x, num)| {
|
|
mapping.insert(num, (x as u8, y));
|
|
});
|
|
}
|
|
|
|
let card = BingoCard {
|
|
mapping,
|
|
ticks: Default::default(),
|
|
};
|
|
|
|
bingo_cards.push(card);
|
|
}
|
|
|
|
(numbers, bingo_cards)
|
|
}
|
|
|
|
pub fn part1(input: &mut dyn Read) -> String {
|
|
let (numbers, mut bingo_cards) = read_input(input);
|
|
|
|
for number in numbers {
|
|
for card in &mut bingo_cards {
|
|
if card.cross(number) && card.has_won() {
|
|
return (number as u32 * card.remaining()).to_string();
|
|
}
|
|
}
|
|
}
|
|
|
|
panic!("None of the cards won")
|
|
}
|
|
|
|
pub fn part2(input: &mut dyn Read) -> String {
|
|
let (numbers, mut bingo_cards) = read_input(input);
|
|
let mut bingo_won = vec![false; bingo_cards.len()];
|
|
let mut num_won = 0;
|
|
let to_win = bingo_cards.len();
|
|
|
|
for num in numbers {
|
|
for (won, card) in bingo_won.iter_mut().zip(bingo_cards.iter_mut()) {
|
|
if !*won && card.cross(num) && card.has_won() {
|
|
*won = true;
|
|
num_won += 1;
|
|
|
|
if num_won == to_win {
|
|
return (num as u32 * card.remaining()).to_string();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
panic!("Not all cards won!")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::test_implementation;
|
|
|
|
use super::*;
|
|
|
|
const SAMPLE: &[u8] = include_bytes!("samples/04.txt");
|
|
|
|
#[test]
|
|
fn sample_part1() {
|
|
test_implementation(part1, SAMPLE, 4512)
|
|
}
|
|
|
|
#[test]
|
|
fn sample_part2() {
|
|
test_implementation(part2, SAMPLE, 1924)
|
|
}
|
|
}
|