Advent of Code 2023 Day 7

Advent of Code
Author

Ryan Heslin

Published

December 7, 2023

An easy-ish day, suprisingly. The puzzle asks you to simulate hands in a simplified version of poker. Hands are ranked by card composition (one pair, two pair, three of a kind, etc.). Ties are broken by directly comparing card ranks.

Part 1 simply asked to rank a series of hands. While the details were fiddly, it wasn’t too hard to write a class to do so:

class Hand:
    joker = "J"
    ranks = {
        (5,): 7,
        (4, 1): 6,
        (3, 2): 5,
        (3, 1, 1): 4,
        (2, 2, 1): 3,
        (2, 1, 1, 1): 2,
        (1, 1, 1, 1, 1): 1,
    }
    n_cards = 5

    def __init__(self, cards, suits, joker=False):
        # if joker:
        #     breakpoint()
        self.cards = tuple(suits[c] for c in cards)
        self._count = Counter(cards)
        self.rank = self.ranks[tuple(sorted(self._count.values(), reverse=True))]

Part 2 “reveals” that the "J" cards are actually jokers, not jacks. For purposes of ranking hands, jokers can assume whichever card value results in the greatest rank. When comparing hands of the same rank, they have the lowest card value.

Brute-force would have sufficed to find the best joker substitutions, but I thought of a more elegant way. Given the hand type and number of jokers, the best possible hand value after the substitution could always be computed. For example, a two pair with a pair of jokers converts into a four of a kind by replacing the jokers with the card used in the other pair:

if joker and (jokers := self._count[self.joker]) > 0:
    match (self.rank, jokers):
        case (1, 1):
            self.rank = 2
        case (2, 1) | (2, 2):
            self.rank = 4
        # two pair
        case (3, 1):
            self.rank = 5
        case (3, 2):
            self.rank = 6
        # should be impossible
        case (3, 3):
            print("check")
            self.rank = 7
        # 3 of a kind
        case (4, 1) | (4, 3):
            self.rank = 6
        # 4 of a kind, full house
        case (5, _) | (6, _):
            self.rank = 7
        case _:
            pass