|
class Bingo |
|
# Create a new Bingo board from a 2d array of integers. It |
|
# will convert to int if you pass strings or similar. |
|
# |
|
# @param board [Array<Array<Integer>>] 5x5 2d array of ints |
|
def initialize board |
|
raise "invalid board: must have exactly 5 rows" unless board.length == 5 |
|
@board = Array.new(5) |
|
board.each_with_index do |row, i| |
|
raise "invalid board: rows must have exactly 5 cols" unless row.length == 5 |
|
@board[i] = row.map { |n| Integer(n) } |
|
end |
|
@marks = [] |
|
end |
|
|
|
# Parse a Bingo board string and return new Bingo object |
|
# |
|
# @param boardStr [String] Bingo board string |
|
# @return [Bingo] new bingo board |
|
def self.parse boardStr |
|
Bingo.new(boardStr.strip.split("\n").map { |r| r.strip.split }) |
|
end |
|
|
|
# Evaluate whether a board has won or not. |
|
# |
|
# @return [TrueClass,FalseClass] |
|
def evaluate |
|
(@board + @board.transpose) |
|
.each { |group| return true if evaluateGroup(group) } |
|
|
|
false |
|
end |
|
|
|
# Mark a given number. Returns whether or not the board is won |
|
# |
|
# @param i [Integer] Next called number |
|
# @return [TrueClass,FalseClass] Whether new board state wins |
|
def mark i |
|
i = Integer(i) |
|
raise "invalid number: already called" if @marks.index(i) |
|
|
|
@marks << i |
|
evaluate |
|
end |
|
|
|
# Sum all unmarked numbers |
|
# |
|
# @return [Integer] sum of all unmarked positions |
|
def sumUnmarked |
|
@board.flatten.difference(@marks).sum |
|
end |
|
|
|
private |
|
|
|
# Evaluate whether a row of 5 has won or not |
|
# |
|
# @return [TrueClass,FalseClass] |
|
def evaluateGroup group |
|
return true if group.&(@marks).length == 5 |
|
|
|
false |
|
end |
|
end |
|
|
|
def day4_1 |
|
input = File.read('2021.4.txt').split("\n\n") |
|
marks = input.shift.split(',') |
|
boards = input.map { |boardStr| Bingo.parse(boardStr) } |
|
nested_iter(marks, boards) do |mark, board| |
|
return board.sumUnmarked * Integer(mark) if board.mark(mark) |
|
end |
|
end |
|
|
|
day4_1 |
|
|
|
def day4_2 |
|
input = File.read('2021.4.txt').split("\n\n") |
|
marks = input.shift.split(',') |
|
boards = input.map { |boardStr| Bingo.parse(boardStr) } |
|
lastBoard = nil |
|
marks.each do |mark| |
|
boards.delete_if { |board| board.mark(mark) } |
|
lastBoard = boards[0] if boards.length == 1 |
|
return lastBoard.sumUnmarked * Integer(mark) if boards.length == 0 |
|
end |
|
end |
|
|
|
day4_2 |