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.
- package/README.md +84 -4
- package/evaluator.js +386 -103
- 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` -
|
|
132
|
-
- `kickerCards` -
|
|
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":
|
|
164
|
-
|
|
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.#
|
|
198
|
-
evaluation.kickerCards = this.#
|
|
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
|
/**
|