from collections import Counter from helpers import Helper helper = Helper(debug=True) debug = helper.debug load_input = helper.load_input CARD_VALUES = ["J"] for i in range(2,10): CARD_VALUES.append(str(i)) CARD_VALUES.extend(["T", "Q", "K", "A"]) HAND_VALUES = [ "High card", "One pair", "Two pair", "Three of a kind", "Full House", "Four of a kind", "Five of a kind" ] class Hand: def __init__(self, cards, bid): self.cards = cards self.bid = bid @property def value(self): card_counter = Counter(self.cards) card_counts = card_counter.most_common() # We have to separate J being the most common card out, # or else it might get counted twice. if card_counts[0][0] == "J": # Five jacks is five of a kind. # Four jacks is five of a kind too, because the other card counts. # Three jacks plus two of another card is five of a kind. if (card_counts[0][1] == 5 or card_counts[0][1] == 4 or (card_counts[0][1] == 3 and card_counts[1][1] == 2) ): return "Five of a kind" # Three jacks is four of a kind, because the next most common card counts. # Two jacks plus two of another card is four of a kind. if (card_counts[0][1] == 3 or (card_counts[0][1] == 2 and card_counts[1][1] == 2) ): return "Four of a kind" # Weirdly, you can only get a full house if there's only one jack... # and if that's the case, it won't be at the front of most_common(). if card_counts[0][1] == 2: return "Three of a kind" # If J is at the front of most_common(), and there's only one of it, # then there's no more than 1 of any other card. So we have a pair. return "One pair" # Okay, done with J being at the front. # There are five cards of a kind, or (three/four) cards of a kind and # (one/two) J. # If there are only two of a kind at the front, then there aren't more # than two of any other kind, so we can't get to 5. if card_counts[0][1] == 5 or card_counts[0][1] + card_counter["J"] == 5: return "Five of a kind" # There are four cards of a kind without a J, or (two/three) cards of # a kind and (one/two) J. if card_counts[0][1] == 4 or card_counts[0][1] + card_counter["J"] == 4: return "Four of a kind" # There are three cards of a kind without a J, or two cards of a kind and # exactly one J. if card_counts[0][1] == 3 or card_counts[0][1] + card_counter["J"] == 3: # We know the most common card isn't a J; we already covered that # in a separate branch. # If the most common count is 3 and there's a J, we already covered # that too, with four and five of a kind. # If the most common count is 2 and there are 2 Js, we already covered # that with four of a kind. # So if the most common count is 2 and there's a J, it can't be in second # position, and if the most common count is 3 then there can't be a J. # So we can discard the possibility that J is in second position. if card_counts[1][1] == 2: return "Full House" return "Three of a kind" # There are two of the most common card without any Js, or one and a single J. if card_counts[0][1] == 2 or card_counts[0][1] + card_counter["J"] == 2: # Same logic as above. If J were the second-most-common card we'd have # already encountered it. if card_counts[1][1] == 2: return "Two pair" return "One pair" return "High card" def __lt__(self, other): # They have different hand values if self.value != other.value: return HAND_VALUES.index(self.value) < HAND_VALUES.index(other.value) # They have the same hand value # So check each card in sequence for i in range(len(self.cards)): if self.cards[i] != other.cards[i]: # They have different nth cards return CARD_VALUES.index(self.cards[i]) < CARD_VALUES.index(other.cards[i]) # They're the same return False def __str__(self): return f"Camel Cards hand: {self.cards} (value {self.value}, bid {self.bid})" def __repr__(self): return self.__str__() def main(): lines = load_input(7) hands = [] for line in lines: cards, bid = line.split() hands.append(Hand( cards = cards, bid = bid )) hands.sort() print(hands[:10]) total_winnings = 0 for i, hand in enumerate(hands): total_winnings += ((i+1) * int(hand.bid)) print(f"Total winnings: {total_winnings}") if __name__ == "__main__": main()