use std::collections::HashMap;
fn time_bingo<RI: IntoIterator<Item = CI>, CI: IntoIterator<Item = u8>>(
draws: &HashMap<u8, usize>,
board: RI,
) -> usize {
let mut col_notes: [usize; 5] = [0; 5];
let mut soonest_row = usize::MAX;
for row in board {
let mut this_row = 0;
for (x, c) in row.into_iter().enumerate() {
let t = draws.get(&c).copied().unwrap_or(usize::MAX);
this_row = this_row.max(t);
col_notes[x] = col_notes[x].max(t);
}
soonest_row = soonest_row.min(this_row);
}
std::iter::once(soonest_row)
.chain(col_notes.iter().copied())
.min()
.unwrap()
}
fn score_board<RI: IntoIterator<Item = CI>, CI: IntoIterator<Item = u8>>(
draws: &HashMap<u8, usize>,
board: RI,
step: usize,
) -> usize {
board
.into_iter()
.flat_map(IntoIterator::into_iter)
.filter(|n| match draws.get(n) {
None => true,
Some(s) => s > &step,
})
.map(<u8 as Into<usize>>::into)
.sum()
}
fn parse_draws(line: &str) -> HashMap<u8, usize> {
line.split(',')
.flat_map(|x| x.parse().ok())
.enumerate()
.map(|(i, x)| (x, i))
.collect()
}
fn parse_boards<I: Iterator<Item = S>, S: AsRef<str>>(input: &mut I) -> Vec<Vec<Vec<u8>>> {
input
.filter(|l| l.as_ref().len() > 0)
.groups_of(5)
.map(|chunk| {
chunk
.iter()
.map(|line| {
line.as_ref()
.split_whitespace()
.flat_map(|ns| ns.parse().ok())
.collect()
})
.collect()
})
.collect()
}
fn main() {
use std::io::BufRead;
let filename = std::env::args().nth(1).expect("Expected filename");
let file = std::io::BufReader::new(
std::fs::File::open(<String as AsRef<std::path::Path>>::as_ref(&filename)).unwrap(),
);
let mut lines = file.lines();
let draws = lines
.next()
.map(|l| parse_draws(&l.unwrap()))
.expect("File is empty");
let boards = parse_boards(&mut lines.flat_map(Result::ok));
let (score,step) = boards.iter()
.map(|board| {
let step = time_bingo(&draws, board.iter().map(|r| r.iter().copied()));
(score_board(&draws,board.iter().map(|r| r.iter().copied()),step), step)
})
.min_by(|(_,a),(_,b)| a.cmp(b))
.unwrap();
println!("Time: {}", step);
println!("Score: {}", score);
let (last_called,_) = draws.iter()
.filter(|(_,w)| *w == &step)
.next()
.unwrap();
println!("Last called: {}", last_called);
println!("Product: {}", score * <u8 as Into<usize>>::into(*last_called));
}
struct Groups<I> {
inner: I,
group_size: usize,
}
impl<I: Sized + Iterator> Iterator for Groups<I> {
type Item = Vec<<I as Iterator>::Item>;
fn next(&mut self) -> Option<Self::Item> {
if self.group_size == 0 {
None
} else {
let mut result = Vec::with_capacity(self.group_size);
for _ in 0..self.group_size {
match self.inner.next() {
Some(a) => {
result.push(a);
}
None => {
break;
}
}
}
if result.len() == 0 {
self.group_size = 0;
None
} else {
Some(result)
}
}
}
}
trait Groupable: Sized + Iterator {
fn groups_of(self, size: usize) -> Groups<Self>;
}
impl<I: Sized + Iterator> Groupable for I {
fn groups_of(self, size: usize) -> Groups<Self> {
Groups {
inner: self,
group_size: size,
}
}
}