90 lines
2.6 KiB
Python
90 lines
2.6 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copyright (c) 2021 Akumatic
|
|
#
|
|
# https://adventofcode.com/2021/day/04
|
|
|
|
class Draws:
|
|
def __init__(self, numbers: list):
|
|
self.draws = numbers[:]
|
|
self.last_idx = -1
|
|
self.draws_left = self.last_idx < len(numbers)
|
|
|
|
def draw(self) -> int:
|
|
if not self.draws_left:
|
|
return None
|
|
self.last_idx += 1
|
|
self.draws_left = self.last_idx < len(self.draws)
|
|
return self.draws[self.last_idx]
|
|
|
|
class Bingo:
|
|
def __init__(self, numbers: list):
|
|
self.field = numbers[:]
|
|
self.drawn = [False] * 25
|
|
self.last_number = None
|
|
self.winner = False
|
|
|
|
def draw(self, number: int):
|
|
if number in self.field:
|
|
self.last_number = number
|
|
idx = self.field.index(number)
|
|
self.drawn[idx] = True
|
|
self.check_for_win()
|
|
|
|
def check_for_win(self) -> bool:
|
|
for i in range(0, 5):
|
|
if all(self.drawn[i*5 + j] for j in range(5)) or all(self.drawn[j*5 + i] for j in range(5)):
|
|
self.winner = True
|
|
return True
|
|
return False
|
|
|
|
def calculate_score(self):
|
|
return sum(self.field[i] for i in range(25) if not self.drawn[i]) * self.last_number
|
|
|
|
def read_file(filename: str = "input.txt") -> list:
|
|
with open(f"{__file__.rstrip('code.py')}{filename}", "r") as f:
|
|
lines = [line for line in f.read().split("\n")]
|
|
|
|
draws = Draws([int(x) for x in lines[0].split(",")])
|
|
|
|
fields = list()
|
|
cur_nums = list()
|
|
for i in range(2, len(lines), 6):
|
|
for j in range(5):
|
|
cur_nums += [int(x) for x in lines[i + j].lstrip().strip().split()]
|
|
fields.append(Bingo(cur_nums))
|
|
cur_nums.clear()
|
|
|
|
return draws, fields
|
|
|
|
def part1(draws: Draws, fields: list) -> int:
|
|
winner = None
|
|
while draws.draws_left:
|
|
num = draws.draw()
|
|
for f in fields:
|
|
f.draw(num)
|
|
if f.winner and winner is None:
|
|
winner = f
|
|
|
|
if winner:
|
|
return winner.calculate_score()
|
|
return -1
|
|
|
|
def part2(draws: Draws, fields: list) -> int:
|
|
while draws.draws_left:
|
|
playing_fields = [field for field in fields if not field.winner]
|
|
if len(playing_fields) == 1:
|
|
break
|
|
|
|
num = draws.draw()
|
|
for field in playing_fields:
|
|
field.draw(num)
|
|
|
|
last = playing_fields[0]
|
|
while not last.winner:
|
|
last.draw(draws.draw())
|
|
return last.calculate_score()
|
|
|
|
if __name__ == "__main__":
|
|
draws, fields = read_file()
|
|
print(f"Part 1: {part1(draws, fields)}")
|
|
print(f"Part 2: {part2(draws, fields)}") |