2021-12-04 12:49:48 +01:00
#![ feature(test) ]
extern crate test ;
use aoc2021 ::common ::* ;
use itertools ::Itertools ;
const DAY : usize = 4 ;
const BOARD_SIZE : usize = 5 ;
2021-12-04 13:38:17 +01:00
type Board = Vec < Vec < u8 > > ;
2021-12-04 12:49:48 +01:00
2021-12-04 16:00:05 +01:00
const WINNING_INDICES : [ [ usize ; BOARD_SIZE ] ; BOARD_SIZE * 2 ] = {
let mut out = [ [ 0 ; BOARD_SIZE ] ; BOARD_SIZE * 2 ] ;
let mut i = 0 ;
while i < BOARD_SIZE {
// tfw there are no for loops in const fn
let mut j = 0 ;
while j < BOARD_SIZE {
out [ i ] [ j ] = i * 5 + j ;
out [ BOARD_SIZE + i ] [ j ] = i + j * 5 ;
j + = 1 ;
}
i + = 1 ;
}
out
} ;
2021-12-04 12:49:48 +01:00
#[ derive(Debug, Clone) ]
struct BingoGame {
input_numbers : Vec < u8 > ,
boards : Vec < Board > ,
}
impl BingoGame {
fn mark_number ( & mut self , n : & u8 ) {
for board in self . boards . iter_mut ( ) {
2021-12-04 13:38:17 +01:00
// Actually using sets instead of vectors here takes twice as long,
// so just pretend these are actually sets.
2021-12-04 12:49:48 +01:00
for winning_set in board . iter_mut ( ) {
2021-12-04 13:38:17 +01:00
winning_set . retain ( | e | e ! = n ) ;
2021-12-04 12:49:48 +01:00
}
}
}
fn find_winner ( & self ) -> Option < & Board > {
self . boards . iter ( ) . find ( | b | has_won ( b ) )
}
2021-12-04 13:09:10 +01:00
// For assertions in the bechmark
#[ cfg(test) ]
fn len ( & self ) -> usize {
self . boards . len ( )
}
2021-12-04 12:49:48 +01:00
}
fn has_won ( board : & Board ) -> bool {
board . iter ( ) . any ( | s | s . is_empty ( ) )
}
2021-12-04 13:30:22 +01:00
fn parse_input ( raw : & str ) -> BingoGame {
2021-12-04 12:49:48 +01:00
let ( input_numbers , boards ) = raw . split_once ( " \n \n " ) . unwrap ( ) ;
2021-12-09 00:26:00 +01:00
let input_numbers = input_numbers . split ( ',' ) . map ( parse_num ) . collect ( ) ;
2021-12-04 12:49:48 +01:00
let boards = boards
. split ( " \n \n " )
2021-12-09 00:26:00 +01:00
. map ( | b | b . split_ascii_whitespace ( ) . map ( parse_num ) . collect ( ) )
2021-12-04 12:49:48 +01:00
. map ( | v : Vec < u8 > | {
2021-12-04 16:00:05 +01:00
debug_assert_eq! ( v . len ( ) , BOARD_SIZE * BOARD_SIZE ) ;
WINNING_INDICES . map ( | row_or_col | row_or_col . map ( | i | v [ i ] ) . to_vec ( ) ) . to_vec ( )
2021-12-04 12:49:48 +01:00
} )
. collect ( ) ;
BingoGame { input_numbers , boards }
}
fn board_score ( board : & Board , current_number : u8 ) -> usize {
2021-12-04 13:30:22 +01:00
let remainder : usize = board . iter ( ) . flatten ( ) . unique ( ) . map ( | & n | n as usize ) . sum ( ) ;
2021-12-04 12:49:48 +01:00
remainder * ( current_number as usize )
}
2021-12-04 13:30:22 +01:00
fn part1 ( parsed : & BingoGame ) -> usize {
2021-12-04 12:49:48 +01:00
let mut game = parsed . to_owned ( ) ;
for n in & game . input_numbers . clone ( ) {
game . mark_number ( n ) ;
if let Some ( board ) = game . find_winner ( ) {
return board_score ( board , * n ) ;
}
}
unreachable! ( " Game should have ended at some point " )
}
2021-12-04 13:30:22 +01:00
fn part2 ( parsed : & BingoGame ) -> usize {
2021-12-04 12:49:48 +01:00
let mut game = parsed . to_owned ( ) ;
for n in & game . input_numbers . clone ( ) {
game . mark_number ( n ) ;
2021-12-04 13:09:10 +01:00
if game . boards . len ( ) = = 1 & & has_won ( & game . boards [ 0 ] ) {
return board_score ( & game . boards [ 0 ] , * n ) ;
2021-12-04 12:49:48 +01:00
}
2021-12-04 13:09:10 +01:00
game . boards . retain ( | b | ! has_won ( b ) ) ;
2021-12-04 12:49:48 +01:00
}
unreachable! ( " Game should have ended at some point " )
}
fn main ( ) {
let input = parse_input ( & read_file ( DAY ) ) ;
println! ( " {input:?} " ) ;
println! ( " Part 1: {} " , part1 ( & input ) ) ;
println! ( " Part 2: {} " , part2 ( & input ) ) ;
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use aoc2021 ::* ;
const TEST_INPUT : & str = " 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! ( part1 ( ) = = 4512 ) ;
test! ( part2 ( ) = = 1924 ) ;
2021-12-04 13:09:10 +01:00
bench! ( part1 ( ) = = 74320 ) ;
bench! ( part2 ( ) = = 17884 ) ;
bench_input! ( BingoGame ::len = > 100 ) ;
2021-12-04 12:49:48 +01:00
}