poker-extval 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +84 -4
  2. package/evaluator.js +386 -103
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -128,8 +128,10 @@ The `evaluateHand()` function returns an object where:
128
128
  - `isInsideStraightDrawWheel` - Inside draw using wheel ranking
129
129
 
130
130
  #### Card Arrays
131
- - `keyCards` - Array of cards (sorted) that constitute the made hand or draw
132
- - `kickerCards` - Array of cards (sorted) that don't constitute the made hand or draw
131
+ - `keyCards` - Object with keys for each hand type/draw, values are arrays of cards (sorted) that constitute that specific hand type or draw. False hand types have empty arrays `[]`.
132
+ - `kickerCards` - Object with keys for each hand type/draw, values are arrays of cards (sorted) that don't constitute that specific hand type or draw. False hand types have empty arrays `[]`, except `isHighCard` which contains all 5 cards when true.
133
+
134
+ **Note**: Each hand type has its own `keyCards` and `kickerCards` entries. For example, a hand with both a pair and a flush draw will have separate entries in `keyCards.isPair`, `keyCards.isFlushDraw`, `kickerCards.isPair`, and `kickerCards.isFlushDraw`.
133
135
 
134
136
  ### Example Response
135
137
 
@@ -160,8 +162,86 @@ The `evaluateHand()` function returns an object where:
160
162
  "isInsideStraightDraw": false,
161
163
  "isStraightDraw": false,
162
164
  "isHighCard": false,
163
- "keyCards": ["As", "Ks", "Qs", "Js", "Ts"],
164
- "kickerCards": []
165
+ "keyCards": {
166
+ "isRoyalFlush": ["As", "Ks", "Qs", "Js", "Ts"],
167
+ "isStraightFlush": [],
168
+ "isFourOfAKind": [],
169
+ "isFullHouse": [],
170
+ "isFlush": ["As", "Ks", "Qs", "Js", "Ts"],
171
+ "isStraight": ["As", "Ks", "Qs", "Js", "Ts"],
172
+ "isThreeOfAKind": [],
173
+ "isTwoPair": [],
174
+ "isPair": [],
175
+ "isTopPair": [],
176
+ "isMiddlePair": [],
177
+ "isBottomPair": [],
178
+ "isTopAndMiddlePair": [],
179
+ "isTopAndBottomPair": [],
180
+ "isMiddleAndBottomPair": [],
181
+ "isTopThreeOfAKind": [],
182
+ "isMiddleThreeOfAKind": [],
183
+ "isBottomThreeOfAKind": [],
184
+ "isFlushDraw": [],
185
+ "isBackdoorFlushDraw": [],
186
+ "isOpenEndedStraightDraw": [],
187
+ "isInsideStraightDraw": [],
188
+ "isStraightDraw": [],
189
+ "isStraightWheel": [],
190
+ "isOpenEndedStraightDrawWheel": [],
191
+ "isInsideStraightDrawWheel": [],
192
+ "isHighCard": []
193
+ },
194
+ "kickerCards": {
195
+ "isRoyalFlush": [],
196
+ "isStraightFlush": [],
197
+ "isFourOfAKind": [],
198
+ "isFullHouse": [],
199
+ "isFlush": [],
200
+ "isStraight": [],
201
+ "isThreeOfAKind": [],
202
+ "isTwoPair": [],
203
+ "isPair": [],
204
+ "isTopPair": [],
205
+ "isMiddlePair": [],
206
+ "isBottomPair": [],
207
+ "isTopAndMiddlePair": [],
208
+ "isTopAndBottomPair": [],
209
+ "isMiddleAndBottomPair": [],
210
+ "isTopThreeOfAKind": [],
211
+ "isMiddleThreeOfAKind": [],
212
+ "isBottomThreeOfAKind": [],
213
+ "isFlushDraw": [],
214
+ "isBackdoorFlushDraw": [],
215
+ "isOpenEndedStraightDraw": [],
216
+ "isInsideStraightDraw": [],
217
+ "isStraightDraw": [],
218
+ "isStraightWheel": [],
219
+ "isOpenEndedStraightDrawWheel": [],
220
+ "isInsideStraightDrawWheel": [],
221
+ "isHighCard": []
222
+ }
223
+ }
224
+ }
225
+ ```
226
+
227
+ **Example 2: Pair with Flush Draw**
228
+
229
+ ```javascript
230
+ {
231
+ "As, Ah, Ks, Qs, Js": {
232
+ "isPair": true,
233
+ "isFlushDraw": true,
234
+ // ... other boolean properties ...
235
+ "keyCards": {
236
+ "isPair": ["As", "Ah"],
237
+ "isFlushDraw": ["As", "Ks", "Qs", "Js"],
238
+ // ... other hand types with empty arrays ...
239
+ },
240
+ "kickerCards": {
241
+ "isPair": ["Ks", "Qs", "Js"],
242
+ "isFlushDraw": ["Ah"],
243
+ // ... other hand types with empty arrays ...
244
+ }
165
245
  }
166
246
  }
167
247
  ```
package/evaluator.js CHANGED
@@ -194,8 +194,8 @@ class HandEvaluator {
194
194
  // Add keyCards and kickerCards before restoring original evaluation
195
195
  // (these methods need access to this.evaluation)
196
196
  const evaluation = this.evaluation;
197
- evaluation.keyCards = this.#getKeyCards();
198
- evaluation.kickerCards = this.#getKickerCards();
197
+ evaluation.keyCards = this.#buildKeyCardsObject();
198
+ evaluation.kickerCards = this.#buildKickerCardsObject(evaluation.keyCards);
199
199
 
200
200
  // Restore original evaluation
201
201
  this.evaluation = originalEvaluation;
@@ -971,6 +971,390 @@ class HandEvaluator {
971
971
  return [];
972
972
  }
973
973
 
974
+ // ==================== KEY CARDS EXTRACTION BY HAND TYPE ====================
975
+
976
+ /**
977
+ * Gets key cards for royal flush (all 5 cards).
978
+ *
979
+ * @private
980
+ * @returns {string[]} Array of 5 cards if royal flush, empty array otherwise
981
+ */
982
+ #getKeyCardsForRoyalFlush() {
983
+ if (!this.evaluation.isRoyalFlush) return [];
984
+ return [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
985
+ }
986
+
987
+ /**
988
+ * Gets key cards for straight flush (all 5 cards).
989
+ *
990
+ * @private
991
+ * @returns {string[]} Array of 5 cards if straight flush, empty array otherwise
992
+ */
993
+ #getKeyCardsForStraightFlush() {
994
+ if (!this.evaluation.isStraightFlush) return [];
995
+ return [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
996
+ }
997
+
998
+ /**
999
+ * Gets key cards for four of a kind (4 cards of same rank).
1000
+ *
1001
+ * @private
1002
+ * @returns {string[]} Array of 4 cards if four of a kind, empty array otherwise
1003
+ */
1004
+ #getKeyCardsForFourOfAKind() {
1005
+ if (!this.evaluation.isFourOfAKind) return [];
1006
+ const rankCounts = this.#getRankCounts();
1007
+ const fourRank = Object.keys(rankCounts).find(r => rankCounts[r] === 4);
1008
+ return this.cards.filter(c => c.slice(0, -1) === fourRank)
1009
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1010
+ }
1011
+
1012
+ /**
1013
+ * Gets key cards for full house (all 5 cards).
1014
+ *
1015
+ * @private
1016
+ * @returns {string[]} Array of 5 cards if full house, empty array otherwise
1017
+ */
1018
+ #getKeyCardsForFullHouse() {
1019
+ if (!this.evaluation.isFullHouse) return [];
1020
+ return [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1021
+ }
1022
+
1023
+ /**
1024
+ * Gets key cards for flush (all 5 cards of same suit).
1025
+ *
1026
+ * @private
1027
+ * @returns {string[]} Array of 5 cards if flush, empty array otherwise
1028
+ */
1029
+ #getKeyCardsForFlush() {
1030
+ if (!this.evaluation.isFlush) return [];
1031
+ const suits = this.#countSuits();
1032
+ const flushSuit = Object.keys(suits).find(s => suits[s] === 5);
1033
+ return this.cards.filter(c => c.slice(-1) === flushSuit)
1034
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1035
+ }
1036
+
1037
+ /**
1038
+ * Gets key cards for straight (5 cards forming straight).
1039
+ *
1040
+ * @private
1041
+ * @returns {string[]} Array of 5 cards if straight, empty array otherwise
1042
+ */
1043
+ #getKeyCardsForStraight() {
1044
+ if (!this.evaluation.isStraight) return [];
1045
+ return this.#getStraightCards();
1046
+ }
1047
+
1048
+ /**
1049
+ * Gets key cards for three of a kind (3 cards of same rank).
1050
+ *
1051
+ * @private
1052
+ * @returns {string[]} Array of 3 cards if three of a kind, empty array otherwise
1053
+ */
1054
+ #getKeyCardsForThreeOfAKind() {
1055
+ if (!this.evaluation.isThreeOfAKind) return [];
1056
+ const rankCounts = this.#getRankCounts();
1057
+ const threeRank = Object.keys(rankCounts).find(r => rankCounts[r] === 3);
1058
+ return this.cards.filter(c => c.slice(0, -1) === threeRank)
1059
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1060
+ }
1061
+
1062
+ /**
1063
+ * Gets key cards for two pair (4 cards forming 2 pairs).
1064
+ *
1065
+ * @private
1066
+ * @returns {string[]} Array of 4 cards if two pair, empty array otherwise
1067
+ */
1068
+ #getKeyCardsForTwoPair() {
1069
+ if (!this.evaluation.isTwoPair) return [];
1070
+ const rankCounts = this.#getRankCounts();
1071
+ const pairRanks = Object.keys(rankCounts).filter(r => rankCounts[r] === 2);
1072
+ return this.cards.filter(c => pairRanks.includes(c.slice(0, -1)))
1073
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1074
+ }
1075
+
1076
+ /**
1077
+ * Gets key cards for pair (2 cards of same rank).
1078
+ *
1079
+ * @private
1080
+ * @returns {string[]} Array of 2 cards if pair, empty array otherwise
1081
+ */
1082
+ #getKeyCardsForPair() {
1083
+ if (!this.evaluation.isPair) return [];
1084
+ const rankCounts = this.#getRankCounts();
1085
+ const pairRank = Object.keys(rankCounts).find(r => rankCounts[r] === 2);
1086
+ return this.cards.filter(c => c.slice(0, -1) === pairRank)
1087
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1088
+ }
1089
+
1090
+ /**
1091
+ * Gets key cards for top pair (2 cards if top pair).
1092
+ *
1093
+ * @private
1094
+ * @returns {string[]} Array of 2 cards if top pair, empty array otherwise
1095
+ */
1096
+ #getKeyCardsForTopPair() {
1097
+ if (!this.evaluation.isTopPair) return [];
1098
+ return this.#getKeyCardsForPair();
1099
+ }
1100
+
1101
+ /**
1102
+ * Gets key cards for middle pair (2 cards if middle pair).
1103
+ *
1104
+ * @private
1105
+ * @returns {string[]} Array of 2 cards if middle pair, empty array otherwise
1106
+ */
1107
+ #getKeyCardsForMiddlePair() {
1108
+ if (!this.evaluation.isMiddlePair) return [];
1109
+ return this.#getKeyCardsForPair();
1110
+ }
1111
+
1112
+ /**
1113
+ * Gets key cards for bottom pair (2 cards if bottom pair).
1114
+ *
1115
+ * @private
1116
+ * @returns {string[]} Array of 2 cards if bottom pair, empty array otherwise
1117
+ */
1118
+ #getKeyCardsForBottomPair() {
1119
+ if (!this.evaluation.isBottomPair) return [];
1120
+ return this.#getKeyCardsForPair();
1121
+ }
1122
+
1123
+ /**
1124
+ * Gets key cards for top and middle pair (4 cards if top and middle pair).
1125
+ *
1126
+ * @private
1127
+ * @returns {string[]} Array of 4 cards if top and middle pair, empty array otherwise
1128
+ */
1129
+ #getKeyCardsForTopAndMiddlePair() {
1130
+ if (!this.evaluation.isTopAndMiddlePair) return [];
1131
+ return this.#getKeyCardsForTwoPair();
1132
+ }
1133
+
1134
+ /**
1135
+ * Gets key cards for top and bottom pair (4 cards if top and bottom pair).
1136
+ *
1137
+ * @private
1138
+ * @returns {string[]} Array of 4 cards if top and bottom pair, empty array otherwise
1139
+ */
1140
+ #getKeyCardsForTopAndBottomPair() {
1141
+ if (!this.evaluation.isTopAndBottomPair) return [];
1142
+ return this.#getKeyCardsForTwoPair();
1143
+ }
1144
+
1145
+ /**
1146
+ * Gets key cards for middle and bottom pair (4 cards if middle and bottom pair).
1147
+ *
1148
+ * @private
1149
+ * @returns {string[]} Array of 4 cards if middle and bottom pair, empty array otherwise
1150
+ */
1151
+ #getKeyCardsForMiddleAndBottomPair() {
1152
+ if (!this.evaluation.isMiddleAndBottomPair) return [];
1153
+ return this.#getKeyCardsForTwoPair();
1154
+ }
1155
+
1156
+ /**
1157
+ * Gets key cards for top three of a kind (3 cards if top three).
1158
+ *
1159
+ * @private
1160
+ * @returns {string[]} Array of 3 cards if top three of a kind, empty array otherwise
1161
+ */
1162
+ #getKeyCardsForTopThreeOfAKind() {
1163
+ if (!this.evaluation.isTopThreeOfAKind) return [];
1164
+ return this.#getKeyCardsForThreeOfAKind();
1165
+ }
1166
+
1167
+ /**
1168
+ * Gets key cards for middle three of a kind (3 cards if middle three).
1169
+ *
1170
+ * @private
1171
+ * @returns {string[]} Array of 3 cards if middle three of a kind, empty array otherwise
1172
+ */
1173
+ #getKeyCardsForMiddleThreeOfAKind() {
1174
+ if (!this.evaluation.isMiddleThreeOfAKind) return [];
1175
+ return this.#getKeyCardsForThreeOfAKind();
1176
+ }
1177
+
1178
+ /**
1179
+ * Gets key cards for bottom three of a kind (3 cards if bottom three).
1180
+ *
1181
+ * @private
1182
+ * @returns {string[]} Array of 3 cards if bottom three of a kind, empty array otherwise
1183
+ */
1184
+ #getKeyCardsForBottomThreeOfAKind() {
1185
+ if (!this.evaluation.isBottomThreeOfAKind) return [];
1186
+ return this.#getKeyCardsForThreeOfAKind();
1187
+ }
1188
+
1189
+ /**
1190
+ * Gets key cards for flush draw (4 cards of same suit).
1191
+ *
1192
+ * @private
1193
+ * @returns {string[]} Array of 4 cards if flush draw, empty array otherwise
1194
+ */
1195
+ #getKeyCardsForFlushDraw() {
1196
+ if (!this.evaluation.isFlushDraw) return [];
1197
+ const suits = this.#countSuits();
1198
+ const flushSuit = Object.keys(suits).find(s => suits[s] === 4);
1199
+ return this.cards.filter(c => c.slice(-1) === flushSuit)
1200
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1201
+ }
1202
+
1203
+ /**
1204
+ * Gets key cards for backdoor flush draw (3 cards of same suit).
1205
+ *
1206
+ * @private
1207
+ * @returns {string[]} Array of 3 cards if backdoor flush draw, empty array otherwise
1208
+ */
1209
+ #getKeyCardsForBackdoorFlushDraw() {
1210
+ if (!this.evaluation.isBackdoorFlushDraw) return [];
1211
+ const suits = this.#countSuits();
1212
+ const flushSuit = Object.keys(suits).find(s => suits[s] === 3);
1213
+ return this.cards.filter(c => c.slice(-1) === flushSuit)
1214
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1215
+ }
1216
+
1217
+ /**
1218
+ * Gets key cards for open-ended straight draw (4 cards forming draw).
1219
+ *
1220
+ * @private
1221
+ * @returns {string[]} Array of 4 cards if open-ended straight draw, empty array otherwise
1222
+ */
1223
+ #getKeyCardsForOpenEndedStraightDraw() {
1224
+ if (!this.evaluation.isOpenEndedStraightDraw) return [];
1225
+ return this.#getStraightDrawCards();
1226
+ }
1227
+
1228
+ /**
1229
+ * Gets key cards for inside straight draw (4 cards forming draw).
1230
+ *
1231
+ * @private
1232
+ * @returns {string[]} Array of 4 cards if inside straight draw, empty array otherwise
1233
+ */
1234
+ #getKeyCardsForInsideStraightDraw() {
1235
+ if (!this.evaluation.isInsideStraightDraw) return [];
1236
+ return this.#getStraightDrawCards();
1237
+ }
1238
+
1239
+ /**
1240
+ * Gets key cards for straight draw (4 cards forming any straight draw).
1241
+ *
1242
+ * @private
1243
+ * @returns {string[]} Array of 4 cards if straight draw, empty array otherwise
1244
+ */
1245
+ #getKeyCardsForStraightDraw() {
1246
+ if (!this.evaluation.isStraightDraw) return [];
1247
+ return this.#getStraightDrawCards();
1248
+ }
1249
+
1250
+ /**
1251
+ * Gets key cards for wheel straight (5 cards forming wheel).
1252
+ *
1253
+ * @private
1254
+ * @returns {string[]} Array of 5 cards if wheel straight, empty array otherwise
1255
+ */
1256
+ #getKeyCardsForStraightWheel() {
1257
+ if (!this.evaluation.isStraightWheel) return [];
1258
+ return this.#getStraightCards();
1259
+ }
1260
+
1261
+ /**
1262
+ * Gets key cards for open-ended straight draw wheel (4 cards forming wheel draw).
1263
+ *
1264
+ * @private
1265
+ * @returns {string[]} Array of 4 cards if wheel open-ended draw, empty array otherwise
1266
+ */
1267
+ #getKeyCardsForOpenEndedStraightDrawWheel() {
1268
+ if (!this.evaluation.isOpenEndedStraightDrawWheel) return [];
1269
+ return this.#getStraightDrawCards();
1270
+ }
1271
+
1272
+ /**
1273
+ * Gets key cards for inside straight draw wheel (4 cards forming wheel draw).
1274
+ *
1275
+ * @private
1276
+ * @returns {string[]} Array of 4 cards if wheel inside draw, empty array otherwise
1277
+ */
1278
+ #getKeyCardsForInsideStraightDrawWheel() {
1279
+ if (!this.evaluation.isInsideStraightDrawWheel) return [];
1280
+ return this.#getStraightDrawCards();
1281
+ }
1282
+
1283
+ /**
1284
+ * Gets key cards for high card (empty array).
1285
+ *
1286
+ * @private
1287
+ * @returns {string[]} Always returns empty array
1288
+ */
1289
+ #getKeyCardsForHighCard() {
1290
+ return [];
1291
+ }
1292
+
1293
+ /**
1294
+ * Builds the keyCards object with entries for all hand types.
1295
+ *
1296
+ * @private
1297
+ * @returns {Object} Object with keys for all hand types, values are arrays of key cards
1298
+ */
1299
+ #buildKeyCardsObject() {
1300
+ return {
1301
+ isRoyalFlush: this.#getKeyCardsForRoyalFlush(),
1302
+ isStraightFlush: this.#getKeyCardsForStraightFlush(),
1303
+ isFourOfAKind: this.#getKeyCardsForFourOfAKind(),
1304
+ isFullHouse: this.#getKeyCardsForFullHouse(),
1305
+ isFlush: this.#getKeyCardsForFlush(),
1306
+ isStraight: this.#getKeyCardsForStraight(),
1307
+ isThreeOfAKind: this.#getKeyCardsForThreeOfAKind(),
1308
+ isTwoPair: this.#getKeyCardsForTwoPair(),
1309
+ isPair: this.#getKeyCardsForPair(),
1310
+ isTopPair: this.#getKeyCardsForTopPair(),
1311
+ isMiddlePair: this.#getKeyCardsForMiddlePair(),
1312
+ isBottomPair: this.#getKeyCardsForBottomPair(),
1313
+ isTopAndMiddlePair: this.#getKeyCardsForTopAndMiddlePair(),
1314
+ isTopAndBottomPair: this.#getKeyCardsForTopAndBottomPair(),
1315
+ isMiddleAndBottomPair: this.#getKeyCardsForMiddleAndBottomPair(),
1316
+ isTopThreeOfAKind: this.#getKeyCardsForTopThreeOfAKind(),
1317
+ isMiddleThreeOfAKind: this.#getKeyCardsForMiddleThreeOfAKind(),
1318
+ isBottomThreeOfAKind: this.#getKeyCardsForBottomThreeOfAKind(),
1319
+ isFlushDraw: this.#getKeyCardsForFlushDraw(),
1320
+ isBackdoorFlushDraw: this.#getKeyCardsForBackdoorFlushDraw(),
1321
+ isOpenEndedStraightDraw: this.#getKeyCardsForOpenEndedStraightDraw(),
1322
+ isInsideStraightDraw: this.#getKeyCardsForInsideStraightDraw(),
1323
+ isStraightDraw: this.#getKeyCardsForStraightDraw(),
1324
+ isStraightWheel: this.#getKeyCardsForStraightWheel(),
1325
+ isOpenEndedStraightDrawWheel: this.#getKeyCardsForOpenEndedStraightDrawWheel(),
1326
+ isInsideStraightDrawWheel: this.#getKeyCardsForInsideStraightDrawWheel(),
1327
+ isHighCard: this.#getKeyCardsForHighCard()
1328
+ };
1329
+ }
1330
+
1331
+ /**
1332
+ * Builds the kickerCards object with entries for all hand types.
1333
+ *
1334
+ * @private
1335
+ * @param {Object} keyCardsObj - The keyCards object
1336
+ * @returns {Object} Object with keys for all hand types, values are arrays of kicker cards
1337
+ */
1338
+ #buildKickerCardsObject(keyCardsObj) {
1339
+ const sortedCards = [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1340
+ const result = {};
1341
+
1342
+ for (const [handType, keyCards] of Object.entries(keyCardsObj)) {
1343
+ if (handType === 'isHighCard') {
1344
+ result[handType] = sortedCards;
1345
+ } else if (keyCards.length === 0) {
1346
+ result[handType] = [];
1347
+ } else {
1348
+ const keyCardsSet = new Set(keyCards);
1349
+ result[handType] = this.cards
1350
+ .filter(c => !keyCardsSet.has(c))
1351
+ .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1352
+ }
1353
+ }
1354
+
1355
+ return result;
1356
+ }
1357
+
974
1358
  /**
975
1359
  * Helper method to extract the 4 cards that form a straight draw.
976
1360
  * Handles both standard and wheel straight draws.
@@ -1010,107 +1394,6 @@ class HandEvaluator {
1010
1394
  return [];
1011
1395
  }
1012
1396
 
1013
- /**
1014
- * Determines the primary made hand or draw and extracts the key cards.
1015
- * Assumes cards are already sorted and evaluation is complete.
1016
- *
1017
- * @private
1018
- * @returns {string[]} Array of key cards, sorted from highest to lowest rank
1019
- */
1020
- #getKeyCards() {
1021
- // Ensure cards are sorted once (they likely already are from evaluation)
1022
- const sortedCards = [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1023
-
1024
- // Calculate these once and reuse
1025
- const rankCounts = this.#getRankCounts();
1026
- const suits = this.#countSuits();
1027
-
1028
- // Use evaluation results directly - no re-checking
1029
- // Check made hands in priority order
1030
- if (this.evaluation.isRoyalFlush || this.evaluation.isStraightFlush ||
1031
- this.evaluation.isFullHouse) {
1032
- return sortedCards; // All 5 cards are key cards
1033
- }
1034
-
1035
- if (this.evaluation.isStraight) {
1036
- return this.#getStraightCards();
1037
- }
1038
-
1039
- if (this.evaluation.isFourOfAKind) {
1040
- const fourRank = Object.keys(rankCounts).find(r => rankCounts[r] === 4);
1041
- return sortedCards.filter(c => c.slice(0, -1) === fourRank)
1042
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1043
- }
1044
-
1045
- if (this.evaluation.isFlush) {
1046
- const flushSuit = Object.keys(suits).find(s => suits[s] === 5);
1047
- return sortedCards.filter(c => c.slice(-1) === flushSuit)
1048
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1049
- }
1050
-
1051
- if (this.evaluation.isThreeOfAKind) {
1052
- const threeRank = Object.keys(rankCounts).find(r => rankCounts[r] === 3);
1053
- return sortedCards.filter(c => c.slice(0, -1) === threeRank)
1054
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1055
- }
1056
-
1057
- if (this.evaluation.isTwoPair) {
1058
- const pairRanks = Object.keys(rankCounts).filter(r => rankCounts[r] === 2);
1059
- return sortedCards.filter(c => pairRanks.includes(c.slice(0, -1)))
1060
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1061
- }
1062
-
1063
- if (this.evaluation.isPair) {
1064
- const pairRank = Object.keys(rankCounts).find(r => rankCounts[r] === 2);
1065
- return sortedCards.filter(c => c.slice(0, -1) === pairRank)
1066
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1067
- }
1068
-
1069
- // Check draws (only if no made hand)
1070
- if (this.evaluation.isFlushDraw) {
1071
- const flushSuit = Object.keys(suits).find(s => suits[s] === 4);
1072
- return sortedCards.filter(c => c.slice(-1) === flushSuit)
1073
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1074
- }
1075
-
1076
- if (this.evaluation.isBackdoorFlushDraw) {
1077
- const flushSuit = Object.keys(suits).find(s => suits[s] === 3);
1078
- return sortedCards.filter(c => c.slice(-1) === flushSuit)
1079
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1080
- }
1081
-
1082
- if (this.evaluation.isOpenEndedStraightDraw || this.evaluation.isInsideStraightDraw) {
1083
- const drawCards = this.#getStraightDrawCards();
1084
- // Only return if we actually found draw cards (safety check)
1085
- if (drawCards.length === 4) {
1086
- return drawCards;
1087
- }
1088
- }
1089
-
1090
- // High card or no hand/draw
1091
- return [];
1092
- }
1093
-
1094
- /**
1095
- * Gets the kicker cards (cards not in the made hand or draw).
1096
- *
1097
- * @private
1098
- * @returns {string[]} Array of kicker cards, sorted from highest to lowest rank
1099
- */
1100
- #getKickerCards() {
1101
- const keyCards = this.#getKeyCards();
1102
- if (keyCards.length === 0) {
1103
- // All cards are kickers (high card)
1104
- return [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1105
- }
1106
-
1107
- // Use Set for O(1) lookup
1108
- const keyCardsSet = new Set(keyCards);
1109
- return this.cards
1110
- .filter(c => !keyCardsSet.has(c))
1111
- .sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
1112
- }
1113
-
1114
1397
  // ==================== HIGH CARD ====================
1115
1398
 
1116
1399
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poker-extval",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Poker hand evaluator with comprehensive test suite",
5
5
  "main": "evaluator.js",
6
6
  "files": [