import { Direction , Vec2 , Vec2String } from '@lib/model' ; export class Bingo { numbers = new Map < Vec2String , number > ( ) ; coords = new Map < number , Vec2 [ ] > ( ) ; markedNumbers = new Set < Vec2String > ( ) ; size : number ; justMarked ? : number ; won = false ; constructor ( board ) { this . size = board . length ; for ( let y = 0 ; y < board . length ; y ++ ) { const row = board [ y ] ; for ( let x = 0 ; x < row . length ; x ++ ) { const number = row [ x ] ; const coord = new Vec2 ( x , y ) ; this . numbers . set ( coord . toString ( ) , number ) ; if ( this . coords . has ( number ) ) { this . coords . get ( number ) ?. push ( coord ) ; } else { this . coords . set ( number , [ coord ] ) ; } } } } markNumber ( n ) { const locations = this . coords . get ( n ) ; if ( locations ) { this . justMarked = n ; this . markedNumbers . addAll ( locations . map ( ( location ) => location . toString ( ) ) ) ; } } isWinning ( ) { for ( const location of this . markedNumbers ) { const v = new Vec2 ( location ) ; for ( const direction of Direction . cardinalDirections ) { let next = v . clone ( ) ; let c = 0 ; while ( this . markedNumbers . has ( next . toString ( ) ) ) { next = next . add ( direction ) ; c ++ ; } if ( c === this . size ) { this . won = true ; return true ; } } } return false ; } score ( ) { const unmarkedNumbersSum = [ ... this . numbers . entries ( ) ] . filter ( ( [ coord ] ) => ! this . markedNumbers . has ( coord ) ) . map ( ( [ , number ] ) => { return number ; } ) . sum ( ) ; return unmarkedNumbersSum * ( this . justMarked ?? 1 ) ; } static run ( boards , numbers ) { for ( const number of numbers ) { for ( const board of boards ) { board . markNumber ( number ) ; if ( board . isWinning ( ) ) { return board ; } } } return undefined ; } static runUntilLast ( boards , numbers ) { let lastWinner = undefined ; for ( const number of numbers ) { for ( const board of boards ) { if ( board . won ) { continue ; } board . markNumber ( number ) ; if ( board . isWinning ( ) ) { lastWinner = board ; } } } return lastWinner ; } static parse ( input ) { const [ numbersSegment , ... boardSegments ] = input . split ( '\n\n' ) ; const numbers = numbersSegment . splitToInt ( { delimiter : /,/ } ) ; const boards = boardSegments . map ( ( boardSegment ) => boardSegment . lines ( ) . map ( ( line ) => line . splitToInt ( ) ) ) . map ( ( board ) => new Bingo ( board ) ) ; return { numbers , boards } ; } }