poker-extval 1.0.1 → 1.1.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/evaluator.js +175 -1
- package/package.json +1 -1
package/evaluator.js
CHANGED
|
@@ -191,9 +191,15 @@ class HandEvaluator {
|
|
|
191
191
|
this.isStraightDraw();
|
|
192
192
|
this.isHighCard();
|
|
193
193
|
|
|
194
|
-
//
|
|
194
|
+
// Add keyCards and kickerCards before restoring original evaluation
|
|
195
|
+
// (these methods need access to this.evaluation)
|
|
195
196
|
const evaluation = this.evaluation;
|
|
197
|
+
evaluation.keyCards = this.#getKeyCards();
|
|
198
|
+
evaluation.kickerCards = this.#getKickerCards();
|
|
199
|
+
|
|
200
|
+
// Restore original evaluation
|
|
196
201
|
this.evaluation = originalEvaluation;
|
|
202
|
+
|
|
197
203
|
return evaluation;
|
|
198
204
|
}
|
|
199
205
|
|
|
@@ -965,6 +971,174 @@ class HandEvaluator {
|
|
|
965
971
|
return this.evaluation.isStraightDraw = this.evaluation.isOpenEndedStraightDraw || this.evaluation.isInsideStraightDraw;
|
|
966
972
|
}
|
|
967
973
|
|
|
974
|
+
// ==================== KEY CARDS AND KICKER CARDS ====================
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Helper method to extract the 5 cards that form a straight.
|
|
978
|
+
* Handles both standard straights and wheel straights.
|
|
979
|
+
*
|
|
980
|
+
* @private
|
|
981
|
+
* @returns {string[]} Array of 5 cards forming the straight, sorted from highest to lowest
|
|
982
|
+
*/
|
|
983
|
+
#getStraightCards() {
|
|
984
|
+
// Try standard straight first
|
|
985
|
+
this.sort();
|
|
986
|
+
const gaps = this.#getGaps();
|
|
987
|
+
if (gaps.every(g => g === 1)) {
|
|
988
|
+
return [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Try wheel straight
|
|
992
|
+
this.sortWheel();
|
|
993
|
+
const gapsWheel = this.#getGapsWheel();
|
|
994
|
+
if (gapsWheel.every(g => g === 1)) {
|
|
995
|
+
// Convert back to standard sorting for return
|
|
996
|
+
return [...this.cards].sort((a, b) => this.#getRankValueWheel(b) - this.#getRankValueWheel(a));
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return [];
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Helper method to extract the 4 cards that form a straight draw.
|
|
1004
|
+
* Handles both standard and wheel straight draws.
|
|
1005
|
+
*
|
|
1006
|
+
* @private
|
|
1007
|
+
* @returns {string[]} Array of 4 cards forming the draw, sorted from highest to lowest
|
|
1008
|
+
*/
|
|
1009
|
+
#getStraightDrawCards() {
|
|
1010
|
+
// Try standard straight draw
|
|
1011
|
+
this.sort();
|
|
1012
|
+
const firstFour = this.cards.slice(0, 4);
|
|
1013
|
+
const lastFour = this.cards.slice(1, 5);
|
|
1014
|
+
|
|
1015
|
+
const firstFourDist = this.getDistance(firstFour);
|
|
1016
|
+
if (firstFourDist === 3 || firstFourDist === 4) {
|
|
1017
|
+
return [...firstFour].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1018
|
+
}
|
|
1019
|
+
const lastFourDist = this.getDistance(lastFour);
|
|
1020
|
+
if (lastFourDist === 3 || lastFourDist === 4) {
|
|
1021
|
+
return [...lastFour].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Try wheel straight draw
|
|
1025
|
+
this.sortWheel();
|
|
1026
|
+
const firstFourWheel = this.cards.slice(0, 4);
|
|
1027
|
+
const lastFourWheel = this.cards.slice(1, 5);
|
|
1028
|
+
|
|
1029
|
+
const firstFourDistWheel = this.getDistanceWheel(firstFourWheel);
|
|
1030
|
+
if (firstFourDistWheel === 3 || firstFourDistWheel === 4) {
|
|
1031
|
+
return [...firstFourWheel].sort((a, b) => this.#getRankValueWheel(b) - this.#getRankValueWheel(a));
|
|
1032
|
+
}
|
|
1033
|
+
const lastFourDistWheel = this.getDistanceWheel(lastFourWheel);
|
|
1034
|
+
if (lastFourDistWheel === 3 || lastFourDistWheel === 4) {
|
|
1035
|
+
return [...lastFourWheel].sort((a, b) => this.#getRankValueWheel(b) - this.#getRankValueWheel(a));
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
return [];
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Determines the primary made hand or draw and extracts the key cards.
|
|
1043
|
+
* Assumes cards are already sorted and evaluation is complete.
|
|
1044
|
+
*
|
|
1045
|
+
* @private
|
|
1046
|
+
* @returns {string[]} Array of key cards, sorted from highest to lowest rank
|
|
1047
|
+
*/
|
|
1048
|
+
#getKeyCards() {
|
|
1049
|
+
// Ensure cards are sorted once (they likely already are from evaluation)
|
|
1050
|
+
const sortedCards = [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1051
|
+
|
|
1052
|
+
// Calculate these once and reuse
|
|
1053
|
+
const rankCounts = this.#getRankCounts();
|
|
1054
|
+
const suits = this.countSuits();
|
|
1055
|
+
|
|
1056
|
+
// Use evaluation results directly - no re-checking
|
|
1057
|
+
// Check made hands in priority order
|
|
1058
|
+
if (this.evaluation.isRoyalFlush || this.evaluation.isStraightFlush ||
|
|
1059
|
+
this.evaluation.isFullHouse) {
|
|
1060
|
+
return sortedCards; // All 5 cards are key cards
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (this.evaluation.isStraight) {
|
|
1064
|
+
return this.#getStraightCards();
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if (this.evaluation.isFourOfAKind) {
|
|
1068
|
+
const fourRank = Object.keys(rankCounts).find(r => rankCounts[r] === 4);
|
|
1069
|
+
return sortedCards.filter(c => c.slice(0, -1) === fourRank)
|
|
1070
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
if (this.evaluation.isFlush) {
|
|
1074
|
+
const flushSuit = Object.keys(suits).find(s => suits[s] === 5);
|
|
1075
|
+
return sortedCards.filter(c => c.slice(-1) === flushSuit)
|
|
1076
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if (this.evaluation.isThreeOfAKind) {
|
|
1080
|
+
const threeRank = Object.keys(rankCounts).find(r => rankCounts[r] === 3);
|
|
1081
|
+
return sortedCards.filter(c => c.slice(0, -1) === threeRank)
|
|
1082
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (this.evaluation.isTwoPair) {
|
|
1086
|
+
const pairRanks = Object.keys(rankCounts).filter(r => rankCounts[r] === 2);
|
|
1087
|
+
return sortedCards.filter(c => pairRanks.includes(c.slice(0, -1)))
|
|
1088
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (this.evaluation.isPair) {
|
|
1092
|
+
const pairRank = Object.keys(rankCounts).find(r => rankCounts[r] === 2);
|
|
1093
|
+
return sortedCards.filter(c => c.slice(0, -1) === pairRank)
|
|
1094
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// Check draws (only if no made hand)
|
|
1098
|
+
if (this.evaluation.isFlushDraw) {
|
|
1099
|
+
const flushSuit = Object.keys(suits).find(s => suits[s] === 4);
|
|
1100
|
+
return sortedCards.filter(c => c.slice(-1) === flushSuit)
|
|
1101
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
if (this.evaluation.isBackdoorFlushDraw) {
|
|
1105
|
+
const flushSuit = Object.keys(suits).find(s => suits[s] === 3);
|
|
1106
|
+
return sortedCards.filter(c => c.slice(-1) === flushSuit)
|
|
1107
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
if (this.evaluation.isOpenEndedStraightDraw || this.evaluation.isInsideStraightDraw) {
|
|
1111
|
+
const drawCards = this.#getStraightDrawCards();
|
|
1112
|
+
// Only return if we actually found draw cards (safety check)
|
|
1113
|
+
if (drawCards.length === 4) {
|
|
1114
|
+
return drawCards;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// High card or no hand/draw
|
|
1119
|
+
return [];
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Gets the kicker cards (cards not in the made hand or draw).
|
|
1124
|
+
*
|
|
1125
|
+
* @private
|
|
1126
|
+
* @returns {string[]} Array of kicker cards, sorted from highest to lowest rank
|
|
1127
|
+
*/
|
|
1128
|
+
#getKickerCards() {
|
|
1129
|
+
const keyCards = this.#getKeyCards();
|
|
1130
|
+
if (keyCards.length === 0) {
|
|
1131
|
+
// All cards are kickers (high card)
|
|
1132
|
+
return [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// Use Set for O(1) lookup
|
|
1136
|
+
const keyCardsSet = new Set(keyCards);
|
|
1137
|
+
return this.cards
|
|
1138
|
+
.filter(c => !keyCardsSet.has(c))
|
|
1139
|
+
.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1140
|
+
}
|
|
1141
|
+
|
|
968
1142
|
// ==================== HIGH CARD ====================
|
|
969
1143
|
|
|
970
1144
|
/**
|