### Objective

Day 4 was bingo day, and we need to find the score of a winning bingo board

First we need to parse a bingo game from the input

```
def bingo_game(puzzle_input: List[List[str]]) -> Tuple[BingoNumbers, Board]:
numbers = list(atoms(puzzle_input[0][0], sep=','))
boards = [[list(map(int, row.split()))
for row in raw_board]
for raw_board in puzzle_input[1:]]
print(f'There are {len(set(numbers))} numbers and {len(boards)} boards')
return numbers, boards
```

Then there are a couple of functions to find if a board wins, and the score of a board.

```
def wins(board: Board, called: List[str]):
for row in board:
if set(row).issubset(set(called)):
return True
for column in columns(board):
if set(column).issubset(set(called)):
return True
def board_score(board, called):
return sum([sum([int(c)
for c in row if c not in called])
for row in board])
```

### Part 1

In **part 1**, we just play the game and return the score of the winning board.

```
def day4_1(puzzle_input):
numbers, boards = puzzle_input
for i in range(5, len(numbers)):
called_numbers = numbers[:i + 1]
for board in boards:
if wins(board, called_numbers):
winning_number = int(called_numbers[-1])
return winning_number * board_score(board, called_numbers)
```

### Part 2

For **part 2** we need the last winning board

```
def day4_2(puzzle_input):
numbers, boards = puzzle_input
winning_boards = set()
for i in range(5, len(numbers)):
called_numbers = numbers[:i + 1]
for board_number, board in enumerate(boards):
if board_number in winning_boards:
continue
if wins(board, called_numbers):
winning_number = int(called_numbers[-1])
winning_boards.add(board_number)
if len(winning_boards) == 100:
final_board = boards[board_number]
final_score = winning_number * board_score(final_board, called_numbers)
return final_score
return None
```