200 lines
5.6 KiB
Rust
200 lines
5.6 KiB
Rust
use std::collections::HashSet;
|
|
use std::fmt;
|
|
use std::fs;
|
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
struct BingoBoard {
|
|
values: [[usize; 5]; 5],
|
|
called: [[bool; 5]; 5],
|
|
}
|
|
|
|
impl BingoBoard {
|
|
fn build_board(input: &[&str]) -> Self {
|
|
let mut values = [[0; 5]; 5];
|
|
for (i, l) in input.iter().enumerate() {
|
|
for (j, v) in l
|
|
.split_whitespace()
|
|
.map(|n| {
|
|
n.parse::<usize>()
|
|
.expect(format!("Could not parse {}", n).as_str())
|
|
})
|
|
.enumerate()
|
|
{
|
|
values[i][j] = v;
|
|
}
|
|
}
|
|
|
|
BingoBoard {
|
|
values: values,
|
|
called: [[false; 5]; 5],
|
|
}
|
|
}
|
|
|
|
fn mark_board(&mut self, number: usize) {
|
|
for (r, row) in self.values.iter().enumerate() {
|
|
for (c, value) in row.iter().enumerate() {
|
|
if *value == number {
|
|
self.called[r][c] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// does winning things
|
|
fn is_winner(&self) -> bool {
|
|
// Check rows
|
|
if self.called.iter().any(|row| row.iter().all(|sq| *sq)) {
|
|
return true;
|
|
}
|
|
|
|
let board_size = self.called.len();
|
|
for col in 0..board_size {
|
|
if self.called.iter().all(|row| row[col]) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
fn sum_unmarked(self) -> usize {
|
|
self.values
|
|
.iter()
|
|
.flatten()
|
|
.zip(self.called.iter().flatten())
|
|
.map(|pair| {
|
|
let (val, called) = pair;
|
|
if *called {
|
|
0
|
|
} else {
|
|
*val
|
|
}
|
|
})
|
|
.reduce(|acc, v| acc + v)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for BingoBoard {
|
|
// This trait requires `fmt` with this exact signature.
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
// Write strictly the first element into the supplied output
|
|
// stream: `f`. Returns `fmt::Result` which indicates whether the
|
|
// operation succeeded or failed. Note that `write!` uses syntax which
|
|
// is very similar to `println!`.
|
|
|
|
let message = self
|
|
.values
|
|
.iter()
|
|
.zip(self.called.iter())
|
|
.map(|x| {
|
|
let (val_row, called_row) = x;
|
|
val_row
|
|
.iter()
|
|
.zip(called_row.iter())
|
|
.map(|v| {
|
|
let (val, called) = v;
|
|
let result: String;
|
|
if *called {
|
|
result = format!("[{}]", val);
|
|
} else {
|
|
result = format!("{}", val);
|
|
}
|
|
|
|
format!("{: >5}", result)
|
|
})
|
|
.collect::<Vec<String>>()
|
|
.join("")
|
|
})
|
|
.collect::<Vec<String>>()
|
|
.join("\n");
|
|
write!(f, "{}", message)
|
|
}
|
|
}
|
|
|
|
fn part1() {
|
|
let file_contents = fs::read_to_string("input.txt").expect("No input found");
|
|
let mut lines = file_contents.lines();
|
|
|
|
let numbers: Vec<usize> = lines
|
|
.next()
|
|
.expect("No line found in input")
|
|
.split(",")
|
|
.map(|n| n.parse().expect(format!("Unknown number: {}", n).as_str()))
|
|
.collect();
|
|
|
|
let board_input: Vec<&str> = lines.filter(|l| l != &"").collect();
|
|
|
|
let mut boards: Vec<BingoBoard> = board_input
|
|
.chunks(5)
|
|
.map(|c| BingoBoard::build_board(c))
|
|
.collect();
|
|
|
|
let mut score = 0;
|
|
for (turn, num) in numbers.iter().enumerate() {
|
|
println!("Turn {}: Called {}", turn, num);
|
|
for (board_num, board) in boards.iter_mut().enumerate() {
|
|
board.mark_board(*num);
|
|
println!("Board {}\n{}\n", board_num, board);
|
|
if board.is_winner() {
|
|
println!("Winner!");
|
|
score = board.sum_unmarked() * num;
|
|
break;
|
|
}
|
|
}
|
|
if score != 0 {
|
|
println!("Final score is {}", score);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn part2() {
|
|
let file_contents = fs::read_to_string("input.txt").expect("No input found");
|
|
let mut lines = file_contents.lines();
|
|
|
|
let numbers: Vec<usize> = lines
|
|
.next()
|
|
.expect("No line found in input")
|
|
.split(",")
|
|
.map(|n| n.parse().expect(format!("Unknown number: {}", n).as_str()))
|
|
.collect();
|
|
|
|
let board_input: Vec<&str> = lines.filter(|l| l != &"").collect();
|
|
|
|
let mut boards: Vec<BingoBoard> = board_input
|
|
.chunks(5)
|
|
.map(|c| BingoBoard::build_board(c))
|
|
.collect();
|
|
|
|
let mut score = 0;
|
|
let mut winners = HashSet::new();
|
|
for (turn, num) in numbers.iter().enumerate() {
|
|
println!("Turn {}: Called {}", turn, num);
|
|
for (board_num, board) in boards.iter_mut().enumerate() {
|
|
// Skip existing winners
|
|
if winners.contains(&board_num) {
|
|
continue;
|
|
}
|
|
board.mark_board(*num);
|
|
println!("Board {}\n{}\n", board_num, board);
|
|
if board.is_winner() {
|
|
score = board.sum_unmarked() * num;
|
|
println!("Winner! Score {}", score);
|
|
winners.insert(board_num);
|
|
}
|
|
}
|
|
if winners.len() == boards.len() {
|
|
break;
|
|
}
|
|
}
|
|
if score != 0 {
|
|
println!("Final score is {}", score);
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
part1();
|
|
part2();
|
|
}
|