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
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
🔒 Thread is locked until there’s at least 100 2 star entries on the global leaderboard
🔓 Thread has been unlocked after around 20 mins
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:
‘JJJJJ’: There is no other non-Joker here, so I messed up and ranked this the lowest because I ended up removing all counts.
‘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