from collections import defaultdict, Counter with open("input.txt") as f: hands = f.read().split("\n") hands = [(hand.split()[0], int(hand.split()[1])) for hand in hands] def rank(hands): ranked = defaultdict(list) for hand in hands: r = get_rank(hand) ranked[r].append(hand) return ranked def get_rank(hand): c = Counter(hand[0]) counts = c.most_common(2) # extract counts first = counts[0][1] second = counts[1][1] if len(counts) == 2 else 0 # discriminate based on counts d = (first, second) match d: case (1,1): return "HC" # high card case (2,1): return "1P" # one pair case (2,2): return "2P" # two pair case (3,1): return "3K" # three of a kind case (3,2): return "FH" # full house case (4,1): return "4K" # four of a kind case (5,0): return "5K" # five of a kind def order(hand: str): """ Convert to base-15 for proper sorting """ # A = ten # B = jack # C = queen # D = king # E = ace return hand.replace( "A", "E" ).replace( "K", "D" ).replace( "Q", "C" ).replace( "J", "B" ).replace( "T", "A" ) def part1(hands): ranked = rank(hands) ranked_hands = [] ranked_hands += sorted(ranked["HC"], key=lambda hand: order(hand[0])) ranked_hands += sorted(ranked["1P"], key=lambda hand: order(hand[0])) ranked_hands += sorted(ranked["2P"], key=lambda hand: order(hand[0])) ranked_hands += sorted(ranked["3K"], key=lambda hand: order(hand[0])) ranked_hands += sorted(ranked["FH"], key=lambda hand: order(hand[0])) ranked_hands += sorted(ranked["4K"], key=lambda hand: order(hand[0])) ranked_hands += sorted(ranked["5K"], key=lambda hand: order(hand[0])) answer = sum( [ hand[1] * (rank + 1) for rank, hand in enumerate(ranked_hands) ] ) print(answer) part1(hands) ### Part 2 def order2(hand: str): """ Convert to base-15 for proper sorting """ # A = ten # B = jack # C = queen # D = king # E = ace return hand.replace( "A", "E" ).replace( "K", "D" ).replace( "Q", "C" ).replace( "J", "1" # jokers are worth less than 2 ).replace( "T", "A" ) def get_rank2(hand): c1 = Counter(hand[0]) c2 = Counter(hand[0].replace("J", "")) jokers_count = c1.get("J") or 0 counts = c2.most_common(2) if not counts: counts = [('', 0),('', 0)] # prevent index error when 5 jokers # extract counts first = counts[0][1] + jokers_count # convert jokers to highest-scoring card second = counts[1][1] if len(counts) == 2 else 0 # discriminate based on counts d = (first, second) match d: case (1,1): return "HC" # high card case (2,1): return "1P" # one pair case (2,2): return "2P" # two pair case (3,1): return "3K" # three of a kind case (3,2): return "FH" # full house case (4,1): return "4K" # four of a kind case (5,0): return "5K" # five of a kind def rank2(hands): ranked = defaultdict(list) for hand in hands: r = get_rank2(hand) ranked[r].append(hand) return ranked def part2(hands): ranked = rank2(hands) ranked_hands = [] ranked_hands += sorted(ranked["HC"], key=lambda hand: order2(hand[0])) ranked_hands += sorted(ranked["1P"], key=lambda hand: order2(hand[0])) ranked_hands += sorted(ranked["2P"], key=lambda hand: order2(hand[0])) ranked_hands += sorted(ranked["3K"], key=lambda hand: order2(hand[0])) ranked_hands += sorted(ranked["FH"], key=lambda hand: order2(hand[0])) ranked_hands += sorted(ranked["4K"], key=lambda hand: order2(hand[0])) ranked_hands += sorted(ranked["5K"], key=lambda hand: order2(hand[0])) answer = sum( [ hand[1] * (rank + 1) for rank, hand in enumerate(ranked_hands) ] ) print(answer) part2(hands)