81 lines
2.0 KiB
Python
81 lines
2.0 KiB
Python
|
from collections.abc import Iterable
|
||
|
from pathlib import Path
|
||
|
|
||
|
|
||
|
class Card:
|
||
|
num: int
|
||
|
winning: set[int]
|
||
|
picks: set[int]
|
||
|
|
||
|
def __init__(self, num: int, winning: set[int], picks: set[int]) -> None:
|
||
|
self.num = num
|
||
|
self.winning = winning
|
||
|
self.picks = picks
|
||
|
self.won = 0
|
||
|
|
||
|
def matching(self) -> int:
|
||
|
return len(self.winning & self.picks)
|
||
|
|
||
|
def score(self) -> int:
|
||
|
return int(2**(self.matching()-1))
|
||
|
|
||
|
def cards_won(self) -> Iterable[int]:
|
||
|
# print(f"Card {self.num} wins cards {[self.num + x + 1 for x in range(self.matching())]}")
|
||
|
for x in range(self.matching()):
|
||
|
# print(f"Card {self.num} wins card {self.num + x + 1}")
|
||
|
yield self.num + x + 1
|
||
|
|
||
|
|
||
|
def parse_line(line: str) -> Card:
|
||
|
card_str, _, nums = line.partition(":")
|
||
|
card_num = int(card_str.partition(" ")[2])
|
||
|
|
||
|
winning_str, _, picks_str = nums.partition("|")
|
||
|
winning = {int(n.strip()) for n in winning_str.split(" ") if n}
|
||
|
picks = {int(n.strip()) for n in picks_str.split(" ") if n.strip()}
|
||
|
|
||
|
return Card(card_num, winning, picks)
|
||
|
|
||
|
|
||
|
def part1(input: Path) -> int:
|
||
|
total = 0
|
||
|
with input.open() as f:
|
||
|
for line in f:
|
||
|
card = parse_line(line)
|
||
|
score = card.score()
|
||
|
# print(f"Card {card.num} is worth {score} points")
|
||
|
total += score
|
||
|
|
||
|
return total
|
||
|
|
||
|
|
||
|
def play(cards: list[Card], card_num: int):
|
||
|
card = cards[card_num - 1]
|
||
|
card.won += 1
|
||
|
for won in card.cards_won():
|
||
|
play(cards, won)
|
||
|
|
||
|
|
||
|
def part2(input: Path) -> int:
|
||
|
cards: list[Card] = []
|
||
|
with input.open() as f:
|
||
|
cards = [parse_line(line) for line in f]
|
||
|
|
||
|
for card in range(len(cards)):
|
||
|
play(cards, card + 1)
|
||
|
|
||
|
total = 0
|
||
|
for card in cards:
|
||
|
# print(f"Card {card.num} was won {card.won} times")
|
||
|
total += card.won
|
||
|
|
||
|
return total
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
result1 = part1(Path("input.txt"))
|
||
|
print("part 1", result1)
|
||
|
|
||
|
result2 = part2(Path("input.txt"))
|
||
|
print("part 2", result2)
|