| 1 | 1 | def best_hands(hands): |
| 2 | | pass |
| 2 | if not hands: |
| 3 | return [] |
| 4 | |
| 5 | # Edge Case: Handle empty hands list |
| 6 | evaluated_hands = [] |
| 7 | for hand in hands: |
| 8 | # Edge Case: Handle malformed hand strings |
| 9 | cards = hand.split() |
| 10 | if len(cards) != 5: |
| 11 | continue |
| 12 | |
| 13 | # Edge Case: Handle invalid card formats |
| 14 | valid_cards = True |
| 15 | for card in cards: |
| 16 | if not is_valid_card(card): |
| 17 | valid_cards = False |
| 18 | break |
| 19 | if not valid_cards: |
| 20 | continue |
| 21 | |
| 22 | # Edge Case: Handle duplicate cards in hand |
| 23 | if len(set(cards)) != 5: |
| 24 | continue |
| 25 | |
| 26 | evaluated_hands.append((hand, evaluate_hand(cards))) |
| 27 | |
| 28 | # Edge Case: No valid hands |
| 29 | if not evaluated_hands: |
| 30 | return [] |
| 31 | |
| 32 | # Find the best hand(s) |
| 33 | best_rank = max(evaluated_hands, key=lambda x: x[1][0])[1][0] |
| 34 | best_hands = [hand for hand, eval_result in evaluated_hands if eval_result[0] == best_rank] |
| 35 | |
| 36 | # If there's a tie, we need to compare the tie-breaker values |
| 37 | if len(best_hands) > 1: |
| 38 | best_tie_breaker = max( |
| 39 | [eval_result for hand, eval_result in evaluated_hands if eval_result[0] == best_rank], |
| 40 | key=lambda x: x[1] |
| 41 | )[1] |
| 42 | best_hands = [ |
| 43 | hand for hand, eval_result in evaluated_hands |
| 44 | if eval_result[0] == best_rank and eval_result[1] == best_tie_breaker |
| 45 | ] |
| 46 | |
| 47 | return best_hands |
| 48 | |
| 49 | def is_valid_card(card): |
| 50 | if not card: |
| 51 | return False |
| 52 | # Edge Case: Handle card with invalid format |
| 53 | if len(card) < 2 or len(card) > 3: |
| 54 | return False |
| 55 | |
| 56 | # Edge Case: Handle invalid rank |
| 57 | rank = card[:-1] |
| 58 | suit = card[-1] |
| 59 | |
| 60 | valid_ranks = {'2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'} |
| 61 | valid_suits = {'H', 'D', 'C', 'S'} |
| 62 | |
| 63 | if rank not in valid_ranks or suit not in valid_suits: |
| 64 | return False |
| 65 | |
| 66 | return True |
| 67 | |
| 68 | def evaluate_hand(cards): |
| 69 | # Parse cards into ranks and suits |
| 70 | ranks = [] |
| 71 | suits = [] |
| 72 | |
| 73 | for card in cards: |
| 74 | rank_str = card[:-1] |
| 75 | suit = card[-1] |
| 76 | |
| 77 | # Convert rank to number for easier comparison |
| 78 | if rank_str == 'A': |
| 79 | rank = 14 |
| 80 | elif rank_str == 'K': |
| 81 | rank = 13 |
| 82 | elif rank_str == 'Q': |
| 83 | rank = 12 |
| 84 | elif rank_str == 'J': |
| 85 | rank = 11 |
| 86 | else: |
| 87 | rank = int(rank_str) |
| 88 | |
| 89 | ranks.append(rank) |
| 90 | suits.append(suit) |
| 91 | |
| 92 | # Sort ranks in descending order for easier evaluation |
| 93 | ranks.sort(reverse=True) |
| 94 | |
| 95 | # Check for flush (all cards same suit) |
| 96 | is_flush = len(set(suits)) == 1 |
| 97 | |
| 98 | # Check for straight (consecutive ranks) |
| 99 | is_straight = False |
| 100 | sorted_ranks = sorted(ranks) |
| 101 | |
| 102 | # Edge Case: Handle straight with Ace as low (A,2,3,4,5) |
| 103 | if sorted_ranks == [2, 3, 4, 5, 14]: |
| 104 | is_straight = True |
| 105 | # For tie-breaking, treat Ace as 1 in this case |
| 106 | sorted_ranks = [1, 2, 3, 4, 5] |
| 107 | ranks = sorted_ranks[::-1] # Descending order |
| 108 | elif all(sorted_ranks[i] == sorted_ranks[0] + i for i in range(5)): |
| 109 | is_straight = True |
| 110 | |
| 111 | # Count occurrences of each rank |
| 112 | rank_counts = {} |
| 113 | for rank in ranks: |
| 114 | rank_counts[rank] = rank_counts.get(rank, 0) + 1 |
| 115 | |
| 116 | # Get counts in descending order |
| 117 | counts = sorted(rank_counts.values(), reverse=True) |
| 118 | |
| 119 | # Determine hand rank (higher number is better) |
| 120 | if is_straight and is_flush: |
| 121 | # Straight flush |
| 122 | return (8, ranks) |
| 123 | elif counts[0] == 4: |
| 124 | # Four of a kind |
| 125 | # Find the four matching cards and the kicker |
| 126 | four_kind_rank = [rank for rank, count in rank_counts.items() if count == 4][0] |
| 127 | kicker = [rank for rank, count in rank_counts.items() if count == 1][0] |
| 128 | return (7, [four_kind_rank, kicker]) |
| 129 | elif counts[0] == 3 and counts[1] == 2: |
| 130 | # Full house |
| 131 | three_kind_rank = [rank for rank, count in rank_counts.items() if count == 3][0] |
| 132 | pair_rank = [rank for rank, count in rank_counts.items() if count == 2][0] |
| 133 | return (6, [three_kind_rank, pair_rank]) |
| 134 | elif is_flush: |
| 135 | # Flush |
| 136 | return (5, ranks) |
| 137 | elif is_straight: |
| 138 | # Straight |
| 139 | return (4, ranks) |
| 140 | elif counts[0] == 3: |
| 141 | # Three of a kind |
| 142 | three_kind_rank = [rank for rank, count in rank_counts.items() if count == 3][0] |
| 143 | kickers = sorted([rank for rank, count in rank_counts.items() if count == 1], reverse=True) |
| 144 | return (3, [three_kind_rank] + kickers) |
| 145 | elif counts[0] == 2 and counts[1] == 2: |
| 146 | # Two pair |
| 147 | pair_ranks = sorted([rank for rank, count in rank_counts.items() if count == 2], reverse=True) |
| 148 | kicker = [rank for rank, count in rank_counts.items() if count == 1][0] |
| 149 | return (2, pair_ranks + [kicker]) |
| 150 | elif counts[0] == 2: |
| 151 | # One pair |
| 152 | pair_rank = [rank for rank, count in rank_counts.items() if count == 2][0] |
| 153 | kickers = sorted([rank for rank, count in rank_counts.items() if count == 1], reverse=True) |
| 154 | return (1, [pair_rank] + kickers) |
| 155 | else: |
| 156 | # High card |
| 157 | return (0, ranks) |
| 158 | |
| 159 | # Handled Edge Cases: empty hands list, malformed hand strings, invalid card formats, duplicate cards in hand, no valid hands, straight with Ace as low |