(function :GameState readState [file] (let [lines (filter (Util.readLines file)) :Map> boardsByNumber (new Map)] (objectWith [ numbersToCall (map (.split (lines.shift) ",") Std.parseInt) boards (for group (groups lines 5) (let [numbers (map (apply concat (for line group (filter (line.split " ")))) Std.parseInt) board (object uncalled (for line (groups numbers 5) (for number line (Some number))) called (for y (range 5) (for x (range 5) None)) won false)] (doFor number numbers (unless (boardsByNumber.exists number) (dictSet boardsByNumber number [])) (.push (dictGet boardsByNumber number) board)) board)) ] boardsByNumber))) // Return true if the board wins as a result of the given number being called (function :Bool stepBoard [:Board board numberCalled] (doFor [y row] (enumerate board.uncalled) (doFor [x square] (enumerate row) (whenLet [(when (= numberCalled number) (Some number)) square] (setNth row x None) (setNth (nth board.called y) x (Some number))))) (boardWon board)) (function :Bool boardWon [:Board board] (let [:Array>> linesOfFive (cast (concat board.called (for x (range 5) (for row board.called (nth row x)))))] (doFor line linesOfFive (let [&mut won true] (doFor square line (case square (None (set won false) (break)) (otherwise))) (when won (return true)))) (return false))) // Return the winning score(s) when a board(s) win (function :Null> stepState [:GameState state] // Keep returning the last score if there are no more numbers to call (let [numberCalled (state.numbersToCall.shift) winningScores []] (when (state.boardsByNumber.exists numberCalled) (doFor board (dictGet state.boardsByNumber numberCalled) (unless board.won (when (stepBoard board numberCalled) (set board.won true) (winningScores.push (boardScore board numberCalled)))))) winningScores)) (function :Int boardScore [:Board board numberCalled] (* numberCalled (apply + (for row board.uncalled (apply + (for square row (case square ((Some v) v) (otherwise 0)))))))) (function winningScore [file] (let [state (readState file)] (loop // Assume that for potential solutions to the AOC problem, only one board wins at a time. (whenLet [[score] (stepState state)] (return score)))) (throw "")) (function lastWinningScore [file] (let [state (readState file) &mut finishedBoards 0] (loop (whenLet [winningScores (stepState state)] (+= finishedBoards winningScores.length) (when (= finishedBoards state.boards.length) (return (first winningScores)))))) (throw ""))