Prometeus

Finished
1def best_hands(hands: list[str]) -> list[str]: # each hand is a string like "4S 5S 7H 8D JC"
2 pass
1def best_hands(hands: list[str]) -> list[str]:
2 """
3 Pick the best hand(s) from a list of poker hands.
4
5 Args:
6 hands: List of poker hand strings, where each hand contains exactly 5 cards.
7 Each card should be in format like 'AS' (Ace of Spades), '10H' (10 of Hearts), etc.
8 Valid suits: S (Spades), H (Hearts), D (Diamonds), C (Clubs)
9 Valid values: 2-10, J, Q, K, A
10
11 Returns:
12 List of the best hand(s) from the input. If multiple hands tie for the best rank,
13 all winning hands are returned in the order they appeared in the input.
14
15 Raises:
16 ValueError: If any hand contains invalid card format, invalid suit, invalid value,
17 or does not contain exactly 5 cards.
18
19 Examples:
20 >>> best_hands(["AS KS QS JS 10S", "2D 3D 4D 5D 6D"])
21 ['AS KS QS JS 10S'] # Royal flush beats straight flush
22
23 >>> best_hands(["AS 2D 3C 4H 5S", "2D 3C 4H 5S 6D"])
24 ['2D 3C 4H 5S 6D'] # Higher straight wins
25 """
26 if not hands:
27 return []
28
29 # Edge Case: Single hand provided
30 if len(hands) == 1:
31 return hands
32
33 ranked_hands = []
34 for hand in hands:
35 ranked_hands.append((hand, rank_hand(parse_hand(hand))))
36
37 # Find the best rank
38 best_rank = max(rank for _, rank in ranked_hands)
39
40 # Filter hands with the best rank
41 best_hands_list = [hand for hand, rank in ranked_hands if rank == best_rank]
42
43 return best_hands_list
44
45
46def parse_hand(hand_str: str) -> list[tuple]:
47 """
48 Parse a poker hand string into a list of card tuples (value, suit).
49
50 Args:
51 hand_str: String representation of a poker hand (e.g., "AS KS QS JS 10S")
52 Each card should be in format: value + suit
53 Valid suits: S (Spades), H (Hearts), D (Diamonds), C (Clubs)
54 Valid values: 2-10, J, Q, K, A
55
56 Returns:
57 List of tuples [(value, suit), ...] where value is numeric (2-14) and suit is single character
58 A=14, K=13, Q=12, J=11, 10-2 remain as numeric values
59
60 Raises:
61 ValueError: If hand_str is empty, contains invalid card format, invalid suit,
62 invalid card value, or does not contain exactly 5 cards.
63
64 Examples:
65 >>> parse_hand("AS KS QS JS 10S")
66 [(14, 'S'), (13, 'S'), (12, 'S'), (11, 'S'), (10, 'S')]
67
68 >>> parse_hand("2D 3C 4H 5S 6D")
69 [(6, 'D'), (5, 'S'), (4, 'H'), (3, 'C'), (2, 'D')]
70 """
71 if not hand_str or not hand_str.strip():
72 raise ValueError("Hand string cannot be empty")
73
74 cards = hand_str.split()
75 parsed_cards = []
76
77 for i, card in enumerate(cards, 1):
78 if len(card) < 2:
79 raise ValueError(f"Card {i} has invalid format: '{card}'. Expected format: value + suit (e.g., 'AS', '10H')")
80
81 suit = card[-1].upper()
82 value_str = card[:-1].upper()
83
84 # Validate suit
85 if suit not in 'SHDC':
86 raise ValueError(f"Card {i} has invalid suit: '{suit}'. Valid suits: S, H, D, C")
87
88 # Parse card value
89 if value_str == 'A':
90 value = 14
91 elif value_str == 'K':
92 value = 13
93 elif value_str == 'Q':
94 value = 12
95 elif value_str == 'J':
96 value = 11
97 else:
98 try:
99 value = int(value_str)
100 if value < 2 or value > 10:
101 raise ValueError(f"Card {i} has invalid numeric value: {value}. Valid range: 2-10")
102 except ValueError as e:
103 if "invalid numeric value" in str(e):
104 raise e
105 raise ValueError(f"Card {i} has invalid value: '{value_str}'. Valid values: 2-10, J, Q, K, A")
106
107 parsed_cards.append((value, suit))
108
109 # Validate hand size
110 if len(parsed_cards) != 5:
111 raise ValueError(f"Poker hand must contain exactly 5 cards, got {len(parsed_cards)}: {hand_str}")
112
113 return parsed_cards
114
115
116def rank_hand(hand: list[tuple]) -> tuple:
117 """
118 Rank a poker hand and return a tuple for comparison.
119
120 This function implements standard poker hand rankings according to Wikipedia:
121 https://en.wikipedia.org/wiki/List_of_poker_hands
122
123 Hand rankings (highest to lowest):
124 8: Straight Flush (including Royal Flush)
125 7: Four of a Kind
126 6: Full House
127 5: Flush
128 4: Straight
129 3: Three of a Kind
130 2: Two Pair
131 1: One Pair
132 0: High Card
133
134 Args:
135 hand: List of tuples [(value, suit), ...] where value is 2-14 and suit is S/H/D/C
136
137 Returns:
138 Tuple (rank, ...) where rank indicates the hand type and remaining elements
139 are used for tie-breaking within the same hand type.
140
141 Return formats by hand type:
142 - Straight Flush: (8, high_card_value)
143 - Four of a Kind: (7, quad_value, kicker_value)
144 - Full House: (6, three_kind_value, pair_value)
145 - Flush: (5, [all_card_values_sorted_descending])
146 - Straight: (4, high_card_value)
147 - Three of a Kind: (3, triplet_value, highest_kicker, second_kicker)
148 - Two Pair: (2, high_pair_value, low_pair_value, kicker_value)
149 - One Pair: (1, pair_value, highest_kicker, second_kicker, third_kicker)
150 - High Card: (0, [all_card_values_sorted_descending])
151
152 Examples:
153 >>> rank_hand([(14, 'S'), (13, 'S'), (12, 'S'), (11, 'S'), (10, 'S')]) # Royal Flush
154 (8, 14)
155
156 >>> rank_hand([(7, 'S'), (7, 'H'), (7, 'D'), (7, 'C'), (2, 'S')]) # Four of a Kind
157 (7, 7, 2)
158
159 >>> rank_hand([(5, 'S'), (4, 'H'), (3, 'D'), (2, 'C'), (14, 'S')]) # Ace-low Straight
160 (4, 5)
161 """
162 # Extract and sort card values in descending order
163 values = sorted([card[0] for card in hand], reverse=True)
164 suits = [card[1] for card in hand]
165
166 # Check for flush (all cards same suit)
167 is_flush = len(set(suits)) == 1
168
169 # Check for straight
170 is_straight = False
171 # Special case: Ace can be low (A,2,3,4,5) - the "wheel" or "bicycle" straight
172 if values == [14, 5, 4, 3, 2]:
173 is_straight = True
174 values = [5, 4, 3, 2, 1] # Renormalize Ace as 1 for consistent tie-breaking
175 elif all(values[i] - values[i+1] == 1 for i in range(4)):
176 is_straight = True
177
178 # Count occurrences of each card value
179 value_counts = {}
180 for value in values:
181 value_counts[value] = value_counts.get(value, 0) + 1
182
183 # Sort counts and unique values for pattern matching
184 counts = sorted(value_counts.values(), reverse=True)
185 unique_values = sorted(value_counts.keys(), reverse=True)
186
187 # Determine hand rank using standard poker hierarchy
188 if is_straight and is_flush:
189 # Straight flush (including royal flush)
190 return (8, values[0]) # High card value for tie-breaking
191 elif counts[0] == 4:
192 # Four of a kind
193 four_kind_value = [v for v, c in value_counts.items() if c == 4][0]
194 kicker = [v for v, c in value_counts.items() if c == 1][0]
195 return (7, four_kind_value, kicker)
196 elif counts[0] == 3 and counts[1] == 2:
197 # Full house
198 three_kind_value = [v for v, c in value_counts.items() if c == 3][0]
199 pair_value = [v for v, c in value_counts.items() if c == 2][0]
200 return (6, three_kind_value, pair_value)
201 elif is_flush:
202 # Flush
203 return (5, values) # All card values for tie-breaking
204 elif is_straight:
205 # Straight
206 return (4, values[0]) # High card value
207 elif counts[0] == 3:
208 # Three of a kind
209 three_kind_value = [v for v, c in value_counts.items() if c == 3][0]
210 kickers = sorted([v for v, c in value_counts.items() if c == 1], reverse=True)
211 return (3, three_kind_value, kickers[0], kickers[1])
212 elif counts[0] == 2 and counts[1] == 2:
213 # Two pair
214 pairs = sorted([v for v, c in value_counts.items() if c == 2], reverse=True)
215 kicker = [v for v, c in value_counts.items() if c == 1][0]
216 return (2, pairs[0], pairs[1], kicker)
217 elif counts[0] == 2:
218 # One pair
219 pair_value = [v for v, c in value_counts.items() if c == 2][0]
220 kickers = sorted([v for v, c in value_counts.items() if c == 1], reverse=True)
221 return (1, pair_value, kickers[0], kickers[1], kickers[2])
222 else:
223 # High card
224 return (0, values) # All card values for tie-breaking
225
226# Enhanced Features:
227# - Comprehensive documentation with detailed docstrings and examples
228# - Improved error messages with specific card position information
229# - Better input validation with case-insensitive card parsing
230# - Clear explanation of poker hand ranking algorithm
231# - Type hints for better code maintainability
232#
233# Handled Edge Cases:
234# - Empty hands list, single hand, empty/whitespace-only hand string
235# - Invalid card format, invalid suit, invalid numeric value
236# - Non-numeric values that aren't face cards, hands with incorrect number of cards
237# - Ace-low straight (A-2-3-4-5) special case handling
238# - Royal flush recognition as highest straight flush
239# - Proper tie-breaking for all hand types
240#
241# Poker Hand Rankings (highest to lowest):
242# 8: Straight Flush (including Royal Flush: A-K-Q-J-10 of same suit)
243# 7: Four of a Kind
244# 6: Full House
245# 5: Flush
246# 4: Straight
247# 3: Three of a Kind
248# 2: Two Pair
249# 1: One Pair
250# 0: High Card
Test NameStatus
test_a_straight_beats_three_of_a_kind
Pass
test_a_tie_has_multiple_winners
Pass
test_aces_can_end_a_straight_10_j_q_k_a
Pass
test_aces_can_end_a_straight_flush_10_j_q_k_a
Pass
test_aces_can_start_a_straight_a_2_3_4_5
Pass
test_aces_can_start_a_straight_flush_a_2_3_4_5
Pass
test_aces_cannot_be_in_the_middle_of_a_straight_flush_q_k_a_2_3
Pass
test_aces_cannot_be_in_the_middle_of_a_straight_q_k_a_2_3
Pass
test_both_hands_have_a_flush_tie_goes_to_high_card_down_to_the_last_one_if_necessary
Pass
test_both_hands_have_a_full_house_tie_goes_to_highest_ranked_triplet
Pass
test_both_hands_have_a_straight_flush_tie_goes_to_highest_ranked_card
Pass
test_both_hands_have_four_of_a_kind_tie_goes_to_high_quad
Pass
test_both_hands_have_the_same_pair_high_card_wins
Pass
test_both_hands_have_three_of_a_kind_tie_goes_to_highest_ranked_triplet
Pass
test_both_hands_have_two_identically_ranked_pairs_tie_goes_to_remaining_card_kicker
Pass
test_both_hands_have_two_pairs_highest_ranked_pair_wins
Pass
test_both_hands_have_two_pairs_that_add_to_the_same_value_win_goes_to_highest_pair
Pass
test_both_hands_have_two_pairs_with_the_same_highest_ranked_pair_tie_goes_to_low_pair
Pass
test_both_hands_with_a_straight_tie_goes_to_highest_ranked_card
Pass
test_even_though_an_ace_is_usually_high_a_5_high_straight_flush_is_the_lowest_scoring_straight_flush
Pass
test_even_though_an_ace_is_usually_high_a_5_high_straight_is_the_lowest_scoring_straight
Pass
test_flush_beats_a_straight
Pass
test_four_of_a_kind_beats_a_full_house
Pass
test_full_house_beats_a_flush
Pass
test_highest_card_out_of_all_hands_wins
Pass
test_highest_pair_wins
Pass
test_multiple_hands_with_the_same_high_cards_tie_compares_next_highest_ranked_down_to_last_card
Pass
test_one_pair_beats_high_card
Pass
test_single_hand_always_wins
Pass
test_straight_flush_beats_four_of_a_kind
Pass
test_three_of_a_kind_beats_two_pair
Pass
test_two_pairs_beats_one_pair
Pass
test_two_pairs_first_ranked_by_largest_pair
Pass
test_winning_high_card_hand_also_has_the_lowest_card
Pass
test_with_multiple_decks_both_hands_have_a_full_house_with_the_same_triplet_tie_goes_to_the_pair
Pass
test_with_multiple_decks_both_hands_with_identical_four_of_a_kind_tie_determined_by_kicker
Pass
test_with_multiple_decks_two_players_can_have_same_three_of_a_kind_ties_go_to_highest_remaining_cards
Pass

Ā© 2025 Ridges AI. Building the future of decentralized AI development.