Finished
88 * @return {string[]}
99 */
1010export const bestHands = (hands) => {
11 throw new Error('Remove this line and implement the function');
11 if (!hands || hands.length === 0) {
12 // Edge Case: Empty input list
13 return [];
14 }
15
16 const rankedHands = hands.map(hand => {
17 const cards = hand.split(' ');
18
19 // Edge Case: Incomplete hands (less than 5 cards)
20 if (cards.length !== 5) {
21 throw new Error('Invalid hand: must contain exactly 5 cards');
22 }
23
24 const parsedCards = cards.map(card => {
25 // Edge Case: Invalid card formats
26 if (card.length < 2 || card.length > 3) {
27 throw new Error('Invalid card format');
28 }
29
30 const rankStr = card.length === 3 ? card.substring(0, 2) : card[0];
31 const suit = card.length === 3 ? card[2] : card[1];
32
33 // Edge Case: Invalid ranks or suits
34 if (!['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'].includes(rankStr)) {
35 throw new Error('Invalid card rank');
36 }
37
38 if (!['S', 'H', 'D', 'C'].includes(suit)) {
39 throw new Error('Invalid card suit');
40 }
41
42 let rank;
43 if (rankStr === 'J') rank = 11;
44 else if (rankStr === 'Q') rank = 12;
45 else if (rankStr === 'K') rank = 13;
46 else if (rankStr === 'A') rank = 14;
47 else rank = parseInt(rankStr);
48
49 return { rank, suit, rankStr };
50 });
51
52 return {
53 original: hand,
54 cards: parsedCards,
55 rank: evaluateHand(parsedCards)
56 };
57 });
58
59 // Sort hands by rank (highest first)
60 rankedHands.sort((a, b) => {
61 for (let i = 0; i < a.rank.length && i < b.rank.length; i++) {
62 if (a.rank[i] > b.rank[i]) return -1;
63 if (a.rank[i] < b.rank[i]) return 1;
64 }
65 return 0;
66 });
67
68 // Find all hands with the best rank
69 const bestRank = rankedHands[0].rank;
70 const bestHands = rankedHands.filter(hand => {
71 for (let i = 0; i < hand.rank.length && i < bestRank.length; i++) {
72 if (hand.rank[i] > bestRank[i]) return false;
73 if (hand.rank[i] < bestRank[i]) return false;
74 }
75 return true;
76 }).map(hand => hand.original);
77
78 return bestHands;
1279};
80
81function evaluateHand(cards) {
82 const ranks = cards.map(card => card.rank).sort((a, b) => a - b);
83 const suits = cards.map(card => card.suit);
84
85 const rankCounts = {};
86 ranks.forEach(rank => {
87 rankCounts[rank] = (rankCounts[rank] || 0) + 1;
88 });
89
90 const counts = Object.values(rankCounts).sort((a, b) => b - a);
91 const uniqueRanks = Object.keys(rankCounts).map(Number).sort((a, b) => a - b);
92
93 const isFlush = suits.every(suit => suit === suits[0]);
94
95 // Check for straight (including Ace-low straight)
96 let isStraight = false;
97 if (uniqueRanks.length === 5) {
98 if (uniqueRanks[4] - uniqueRanks[0] === 4) {
99 isStraight = true;
100 } else if (uniqueRanks[0] === 2 && uniqueRanks[1] === 3 && uniqueRanks[2] === 4 && uniqueRanks[3] === 5 && uniqueRanks[4] === 14) {
101 // Ace-low straight: A,2,3,4,5
102 isStraight = true;
103 // For comparison purposes, treat Ace as 1 in this case
104 ranks[ranks.indexOf(14)] = 1;
105 ranks.sort((a, b) => a - b);
106 }
107 }
108
109 const isStraightFlush = isStraight && isFlush;
110 const isRoyalFlush = isStraightFlush && ranks[0] === 10 && ranks[4] === 14;
111
112 // Royal Flush
113 if (isRoyalFlush) {
114 return [10];
115 }
116
117 // Straight Flush
118 if (isStraightFlush) {
119 return [9, ranks[4]]; // High card of straight
120 }
121
122 // Four of a Kind
123 if (counts[0] === 4) {
124 const fourKindRank = Object.keys(rankCounts).find(rank => rankCounts[rank] === 4);
125 const otherRank = Object.keys(rankCounts).find(rank => rankCounts[rank] === 1);
126 return [8, parseInt(fourKindRank), parseInt(otherRank)];
127 }
128
129 // Full House
130 if (counts[0] === 3 && counts[1] === 2) {
131 const threeKindRank = Object.keys(rankCounts).find(rank => rankCounts[rank] === 3);
132 const pairRank = Object.keys(rankCounts).find(rank => rankCounts[rank] === 2);
133 return [7, parseInt(threeKindRank), parseInt(pairRank)];
134 }
135
136 // Flush
137 if (isFlush) {
138 return [6, ...[...ranks].reverse()];
139 }
140
141 // Straight
142 if (isStraight) {
143 return [5, ranks[4]]; // High card of straight
144 }
145
146 // Three of a Kind
147 if (counts[0] === 3) {
148 const threeKindRank = Object.keys(rankCounts).find(rank => rankCounts[rank] === 3);
149 const otherRanks = Object.keys(rankCounts)
150 .filter(rank => rankCounts[rank] === 1)
151 .map(Number)
152 .sort((a, b) => b - a);
153 return [4, parseInt(threeKindRank), ...otherRanks];
154 }
155
156 // Two Pair
157 if (counts[0] === 2 && counts[1] === 2) {
158 const pairRanks = Object.keys(rankCounts)
159 .filter(rank => rankCounts[rank] === 2)
160 .map(Number)
161 .sort((a, b) => b - a);
162 const otherRank = Object.keys(rankCounts).find(rank => rankCounts[rank] === 1);
163 return [3, pairRanks[0], pairRanks[1], parseInt(otherRank)];
164 }
165
166 // One Pair
167 if (counts[0] === 2) {
168 const pairRank = Object.keys(rankCounts).find(rank => rankCounts[rank] === 2);
169 const otherRanks = Object.keys(rankCounts)
170 .filter(rank => rankCounts[rank] === 1)
171 .map(Number)
172 .sort((a, b) => b - a);
173 return [2, parseInt(pairRank), ...otherRanks];
174 }
175
176 // High Card
177 return [1, ...[...ranks].reverse()];
178}
179
180// Handled Edge Cases: Empty input list, incomplete hands (less than 5 cards), invalid card formats, invalid ranks or suits, tie-breaking scenarios
Test NameStatus
single hand always wins
Pass
highest card out of all hands wins
Pass
a tie has multiple winners
Pass
multiple hands with the same high cards, tie compares next highest ranked, down to last card
Pass
winning high card hand also has the lowest card
Pass
one pair beats high card
Pass
highest pair wins
Pass
both hands have the same pair, high card wins
Pass
two pairs beats one pair
Pass
both hands have two pairs, highest ranked pair wins
Pass
both hands have two pairs, with the same highest ranked pair, tie goes to low pair
Pass
both hands have two identically ranked pairs, tie goes to remaining card (kicker)
Pass
both hands have two pairs that add to the same value, win goes to highest pair
Pass
two pairs first ranked by largest pair
Pass
three of a kind beats two pair
Pass
both hands have three of a kind, tie goes to highest ranked triplet
Pass
with multiple decks, two players can have same three of a kind, ties go to highest remaining cards
Pass
a straight beats three of a kind
Pass
aces can end a straight (10 J Q K A)
Pass
aces can start a straight (A 2 3 4 5)
Pass
aces cannot be in the middle of a straight (Q K A 2 3)
Pass
both hands with a straight, tie goes to highest ranked card
Pass
even though an ace is usually high, a 5-high straight is the lowest-scoring straight
Pass
flush beats a straight
Pass
both hands have a flush, tie goes to high card, down to the last one if necessary
Pass
full house beats a flush
Pass
both hands have a full house, tie goes to highest-ranked triplet
Pass
with multiple decks, both hands have a full house with the same triplet, tie goes to the pair
Pass
four of a kind beats a full house
Pass
both hands have four of a kind, tie goes to high quad
Pass
with multiple decks, both hands with identical four of a kind, tie determined by kicker
Pass
straight flush beats four of a kind
Pass
aces can end a straight flush (10 J Q K A)
Pass
aces can start a straight flush (A 2 3 4 5)
Pass
aces cannot be in the middle of a straight flush (Q K A 2 3)
Pass
both hands have straight flush, tie goes to highest-ranked card
Pass
even though an ace is usually high, a 5-high straight flush is the lowest-scoring straight flush
Pass

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