| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 | from copy import deepcopy
from helpers import Helper
helper = Helper(debug=True)
load_input = helper.load_input
debug = helper.debug
class Card:
    """ A single scratch-off card for AOC 2023 day 4. """
    
    def __init__(self, input_line):
        card_id, numbers = input_line.split(": ")
        winning, have = numbers.split(" | ")
        
        self.idnum = int(card_id.split()[1])
        self.winning = [int(n) for n in winning.split()]
        self.have = [int(n) for n in have.split()]
    
    @property
    def num_winners(self) -> int:
        winners = 0
        for number in self.have:
            if number in self.winning:
                winners += 1
        
        return winners
    @property    
    def score(self) -> int:
        return 2**(self.num_winners-1) if self.num_winners > 0 else 0
def main():
    lines = load_input(4)
    cards = []
    
    for line in lines:
        cards.append(Card(input_line=line))
    
    # For every matching number #M (that's ordinal, not the actual matching number) 
    # on card N, I get a copy of card N+M. So if card 1 has three matching numbers,
    # I get copies of cards 2 (1+1), 3 (1+2), and 4 (1+3).
    # Don't process this iteratively; you will quickly overwhelm the interpreter.
    # You don't actually need a new copy of each card. You just need to know
    # how many copies of each card you have.
    cards_dicts = []
    for card in cards:
        # Each card has a count of how many of it we have
        cards_dicts.append({"card": card, "count": 1})
    for i, card in enumerate(cards_dicts):
        # NOT the score, that way lies madness and IndexErrors
        winning_nums = card["card"].num_winners 
        current_add = 1
        while winning_nums > 0:
            # Each copy of the current card adds 1 count to each target card
            cards_dicts[i + current_add]["count"] += card["count"]
            winning_nums -= 1
            current_add += 1
    print(f"Total cards: {sum([v['count'] for v in cards_dicts])}")
if __name__ == "__main__":
    main()
 |