this post was submitted on 07 Dec 2023
16 points (100.0% liked)

Advent Of Code

920 readers
116 users here now

An unofficial home for the advent of code community on programming.dev!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

AoC 2024

Solution Threads

M T W T F S S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 1 year ago
MODERATORS
 

Day 7: Camel Cards

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • Code block support is not fully rolled out yet but likely will be in the middle of the event. Try to share solutions as both code blocks and using something such as https://topaz.github.io/paste/ , pastebin, or github (code blocks to future proof it for when 0.19 comes out and since code blocks currently function in some apps and some instances as well if they are running a 0.19 beta)

FAQ


๐Ÿ”’ Thread is locked until there's at least 100 2 star entries on the global leaderboard

๐Ÿ”“ Thread has been unlocked after around 20 mins

you are viewing a single comment's thread
view the rest of the comments
[โ€“] [email protected] 2 points 1 year ago* (last edited 1 year ago)

Language: Python

This was fun. More enjoyable than I initially thought (though I've done card sorting code before).

Part 1

This was pretty straightforward: create a histogram of the cards in each hand to determine their type, and if there is a tie-breaker, compare each card pairwise. I use the Counter class from collections to do the counting, and then had a dictionary/table to convert labels to numeric values for comparison. I used a very OOP approach and wrote a magic method for comparing hands and used that with Python's builtin sort. I even got to use Enum!

LABELS = {l: v for v, l in enumerate('23456789TJQKA', 2)}

class HandType(IntEnum):
    FIVE_OF_A_KIND  = 6
    FOUR_OF_A_KIND  = 5
    FULL_HOUSE      = 4
    THREE_OF_A_KIND = 3
    TWO_PAIR        = 2
    ONE_PAIR        = 1
    HIGH_CARD       = 0

class Hand:
    def __init__(self, cards=str, bid=str):
        self.cards  = cards
        self.bid    = int(bid)
        counts      = Counter(self.cards)
        self.type   = (
            HandType.FIVE_OF_A_KIND  if len(counts) == 1 else
            HandType.FOUR_OF_A_KIND  if len(counts) == 2 and any(l for l, count in counts.items() if count == 4) else
            HandType.FULL_HOUSE      if len(counts) == 2 and any(l for l, count in counts.items() if count == 3) else
            HandType.THREE_OF_A_KIND if len(counts) == 3 and any(l for l, count in counts.items() if count == 3) else
            HandType.TWO_PAIR        if len(counts) == 3 and any(l for l, count in counts.items() if count == 2) else
            HandType.ONE_PAIR        if len(counts) == 4 and any(l for l, count in counts.items() if count == 2) else
            HandType.HIGH_CARD
        )

    def __lt__(self, other):
        if self.type == other.type:
            for s_label, o_label in zip(self.cards, other.cards):
                if LABELS[s_label] == LABELS[o_label]:
                    continue
                return LABELS[s_label] < LABELS[o_label]
            return False
        return self.type < other.type

    def __repr__(self):
        return f'Hand(cards={self.cards},bid={self.bid},type={self.type})'

def read_hands(stream=sys.stdin) -> list[Hand]:
    return [Hand(*line.split()) for line in stream]

def main(stream=sys.stdin) -> None:
    hands    = sorted(read_hands(stream))
    winnings = sum(rank * hand.bid for rank, hand in enumerate(hands, 1))
    print(winnings)

Part 2

For the second part, I just had to add some post-processing code to convert the jokers into actual cards. The key insight is to find the highest and most numerous non-Joker card and convert all the Jokers to that card label.

This had two edge cases that tripped me up:

  1. 'JJJJJ': There is no other non-Joker here, so I messed up and ranked this the lowest because I ended up removing all counts.

  2. 'JJJ12': This also messed me up b/c the Joker was the most numerous card, and I didn't handle that properly.

Once I fixed the post-processing code though, everything else remained the same. Below, I only show the parts that changed from Part A.

LABELS = {l: v for v, l in enumerate('J23456789TQKA', 1)}

...

class Hand:
    def __init__(self, cards=str, bid=str):
        self.cards  = cards
        self.bid    = int(bid)
        counts      = Counter(self.cards)

        if 'J' in counts and len(counts) > 1:
            max_label = max(set(counts) - {'J'}, key=lambda l: (counts[l], LABELS[l]))
            counts[max_label] += counts['J']
            del counts['J']

        self.type   = (...)

GitHub Repo