summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--4.py68
1 files changed, 43 insertions, 25 deletions
diff --git a/4.py b/4.py
index b740d7b..5f15a8d 100644
--- a/4.py
+++ b/4.py
@@ -1,21 +1,44 @@
from utils import open_day
-from dataclasses import dataclass
-@dataclass
-class Cell:
- num: int
- marked: bool = False
-
-Board = list[list[Cell]]
+class Board:
+ cells: list[int | None]
+ width: int
+ height: int
+ col_hits: list[int]
+ row_hits: list[int]
+ has_bingo: bool
+ unmarked_sum: int
+ def __init__(self, cells: list[list[int]]):
+ self.width = len(cells[0])
+ self.height = len(cells)
+ self.col_hits = [0 for _ in range(self.width)]
+ self.row_hits = [0 for _ in range(self.height)]
+ self.unmarked_sum = 0
+ self.cells = []
+ for row in cells:
+ for cell in row:
+ self.unmarked_sum += cell
+ self.cells.append(cell)
+ self.has_bingo = False
+ def check_bingo(self):
+ self.has_bingo = (
+ any(hits == self.height for hits in self.col_hits) or
+ any(hits == self.width for hits in self.row_hits)
+ )
+ def call(self, num: int):
+ for y in range(self.height):
+ for x in range(self.width):
+ cell = self.cells[x + self.width * y]
+ if cell == num:
+ self.col_hits[x] += 1
+ self.row_hits[y] += 1
+ self.unmarked_sum -= cell
+ self.check_bingo()
+ return
+ @staticmethod
+ def from_string(s: str) -> 'Board':
+ return Board([[int(n) for n in l.split()] for l in s.split('\n')])
-def bingo(board: Board) -> bool:
- for row in board:
- if all(cell.marked for cell in row):
- return True
- for x in range(len(board[0])):
- if all(board[y][x].marked for y in range(len(board))):
- return True
- return False
def solve(nums: list[int], boards: list[Board]) -> tuple[int, int]:
won: set[int] = set()
@@ -25,23 +48,18 @@ def solve(nums: list[int], boards: list[Board]) -> tuple[int, int]:
i: int
board: Board
for i, board in enumerate(boards):
- total = 0
- for row in board:
- for cell in row:
- if cell.num == num:
- cell.marked = True
- if not cell.marked:
- total += cell.num
- if i not in won and bingo(board):
+ if i in won: continue
+ board.call(num)
+ if board.has_bingo:
won.add(i)
- wins.append(num * total)
+ wins.append(num * board.unmarked_sum)
return wins[0], wins[-1]
nums: str | list[int]
boards: list[str] | list[Board]
nums, *boards = open_day(4).read().rstrip().split('\n\n')
nums = list(map(int, nums.split(',')))
-boards = [[[Cell(int(n)) for n in l.split()] for l in b.split('\n')] for b in boards]
+boards = [Board.from_string(b) for b in boards]
part1, part2 = solve(nums, boards)
print(part1)