aoc-2023/d04/main.py
2023-12-03 22:47:33 -08:00

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)