@xpressit/winning-poker-hand-rank
Version:
Determine winning poker hand rank
277 lines (276 loc) • 8.75 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.rankOmahaHand = exports.rankEightOrBetter = exports.rankAceFiveLow = exports.getAceRank = exports.rankShortDeckHand = exports.convertToSixPlusHandRank = exports.rankTexasHand = exports.toFixedSixPlusRank = exports.toFixedTexasRank = exports.EIGHT_OR_BETTER_MAX = exports.SIXPLUS_FULL_HOUSE = exports.SIXPLUS_FLUSH = exports.INVALID = exports.HIGH_CARD = exports.NOTHING = exports.PAIR = exports.TWO_PAIR = exports.THREE_OF_A_KIND = exports.STRAIGHT = exports.FLUSH = exports.FULL_HOUSE = exports.FOUR_OF_A_KIND = exports.STRAIGHT_FLUSH = exports.ROYAL_FLUSH = void 0;
const cactusFastRankHand_1 = require("./cactusFastRankHand");
exports.ROYAL_FLUSH = 1;
exports.STRAIGHT_FLUSH = 10;
exports.FOUR_OF_A_KIND = 166;
exports.FULL_HOUSE = 322;
exports.FLUSH = 1599;
exports.STRAIGHT = 1609;
exports.THREE_OF_A_KIND = 2467;
exports.TWO_PAIR = 3325;
exports.PAIR = 6185;
exports.NOTHING = 7462;
exports.HIGH_CARD = exports.NOTHING;
exports.INVALID = 65535;
exports.SIXPLUS_FLUSH = 166 + 1277;
exports.SIXPLUS_FULL_HOUSE = exports.SIXPLUS_FLUSH + 156;
exports.EIGHT_OR_BETTER_MAX = 512;
const t7c5 = [
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 5, 4, 6],
[0, 1, 2, 3, 6, 4, 5],
[0, 1, 2, 4, 5, 3, 6],
[0, 1, 2, 4, 6, 3, 5],
[0, 1, 2, 5, 6, 3, 4],
[0, 1, 3, 4, 5, 2, 6],
[0, 1, 3, 4, 6, 2, 5],
[0, 1, 3, 5, 6, 2, 4],
[0, 1, 4, 5, 6, 2, 3],
[0, 2, 3, 4, 5, 1, 6],
[0, 2, 3, 4, 6, 1, 5],
[0, 2, 3, 5, 6, 1, 4],
[0, 2, 4, 5, 6, 1, 3],
[0, 3, 4, 5, 6, 1, 2],
[1, 2, 3, 4, 5, 0, 6],
[1, 2, 3, 4, 6, 0, 5],
[1, 2, 3, 5, 6, 0, 4],
[1, 2, 4, 5, 6, 0, 3],
[1, 3, 4, 5, 6, 0, 2],
[2, 3, 4, 5, 6, 0, 1],
];
const rank567cardHand = (hand, f = cactusFastRankHand_1.cactusFastRankHand) => {
if (hand.length === 5) {
const rank = f([hand[0], hand[1], hand[2], hand[3], hand[4]]);
return {
rank,
madeHand: [hand[0], hand[1], hand[2], hand[3], hand[4]],
};
}
if (hand.length === 6) {
const possibleHands = [
[hand[0], hand[1], hand[2], hand[3], hand[4]],
[hand[0], hand[1], hand[2], hand[3], hand[5]],
[hand[0], hand[1], hand[2], hand[4], hand[5]],
[hand[0], hand[1], hand[3], hand[4], hand[5]],
[hand[0], hand[2], hand[3], hand[4], hand[5]],
[hand[1], hand[2], hand[3], hand[4], hand[5]],
];
const sortedHands = possibleHands
.map((h) => ({
rank: f(h),
madeHand: h,
}))
.sort((a, b) => a.rank - b.rank);
return sortedHands[0];
}
if (hand.length === 7) {
let r = 0;
let rank = 9999;
let bestHand = [hand[0], hand[1], hand[2], hand[3], hand[4]];
for (let i = 0; i < 21; i++) {
const inputHand = [
hand[t7c5[i][0]],
hand[t7c5[i][1]],
hand[t7c5[i][2]],
hand[t7c5[i][3]],
hand[t7c5[i][4]],
];
r = f(inputHand);
if (r < rank) {
rank = r;
bestHand = inputHand;
}
}
return {
rank,
madeHand: bestHand,
};
}
throw new Error(`Hand ranker doesn't support ${hand.length} cards`);
};
const toFixedTexasRank = (r) => {
if (r === exports.ROYAL_FLUSH) {
return exports.ROYAL_FLUSH;
}
if (r <= exports.STRAIGHT_FLUSH) {
return exports.STRAIGHT_FLUSH;
}
if (r <= exports.FOUR_OF_A_KIND) {
return exports.FOUR_OF_A_KIND;
}
if (r <= exports.FULL_HOUSE) {
return exports.FULL_HOUSE;
}
if (r <= exports.FLUSH) {
return exports.FLUSH;
}
if (r <= exports.STRAIGHT) {
return exports.STRAIGHT;
}
if (r <= exports.THREE_OF_A_KIND) {
return exports.THREE_OF_A_KIND;
}
if (r <= exports.TWO_PAIR) {
return exports.TWO_PAIR;
}
if (r <= exports.PAIR) {
return exports.PAIR;
}
if (r != exports.INVALID) {
return exports.NOTHING;
}
return exports.INVALID;
};
exports.toFixedTexasRank = toFixedTexasRank;
const toFixedSixPlusRank = (r) => {
if (r === exports.ROYAL_FLUSH) {
return exports.ROYAL_FLUSH;
}
if (r <= exports.STRAIGHT_FLUSH) {
return exports.STRAIGHT_FLUSH;
}
if (r <= exports.FOUR_OF_A_KIND) {
return exports.FOUR_OF_A_KIND;
}
if (r <= exports.SIXPLUS_FLUSH) {
return exports.SIXPLUS_FLUSH;
}
if (r <= exports.SIXPLUS_FULL_HOUSE) {
return exports.SIXPLUS_FULL_HOUSE;
}
if (r <= exports.STRAIGHT) {
return exports.STRAIGHT;
}
if (r <= exports.THREE_OF_A_KIND) {
return exports.THREE_OF_A_KIND;
}
if (r <= exports.TWO_PAIR) {
return exports.TWO_PAIR;
}
if (r <= exports.PAIR) {
return exports.PAIR;
}
if (r != exports.INVALID) {
return exports.NOTHING;
}
return exports.INVALID;
};
exports.toFixedSixPlusRank = toFixedSixPlusRank;
const rankTexasHand = (pocket, board) => {
return rank567cardHand([...pocket, ...board], cactusFastRankHand_1.cactusFastRankHand);
};
exports.rankTexasHand = rankTexasHand;
const convertToSixPlusHandRank = (handRank) => {
if (handRank === 747) {
handRank = 6;
}
else if (handRank === 6610) {
handRank = 1605;
}
// Swap full_house with flush
const fixedRank = (0, exports.toFixedTexasRank)(handRank);
if (fixedRank === exports.FULL_HOUSE) {
handRank = handRank - exports.FOUR_OF_A_KIND + 166 + 1277;
}
else if (fixedRank === exports.FLUSH) {
handRank = handRank - exports.FULL_HOUSE + 166;
}
return handRank;
};
exports.convertToSixPlusHandRank = convertToSixPlusHandRank;
const rankShortDeckHand = (pocket, board) => {
const f = (hand) => {
const handRank = (0, cactusFastRankHand_1.cactusFastRankHand)(hand);
return (0, exports.convertToSixPlusHandRank)(handRank);
};
return rank567cardHand([...pocket, ...board], f);
};
exports.rankShortDeckHand = rankShortDeckHand;
const t4c2 = [
[0, 1, 2, 3],
[0, 2, 1, 3],
[0, 3, 1, 2],
[1, 2, 0, 3],
[1, 3, 0, 2],
[2, 3, 0, 1],
];
const t5c3 = [
[0, 1, 2, 3, 4],
[0, 1, 3, 2, 4],
[0, 1, 4, 2, 3],
[0, 2, 3, 1, 4],
[0, 2, 4, 1, 3],
[0, 3, 4, 1, 2],
[1, 2, 3, 0, 4],
[1, 2, 4, 0, 3],
[1, 3, 4, 0, 2],
[2, 3, 4, 0, 1],
];
// AceRank returns the card [Ace]-low index.
const getAceRank = (card) => {
// int(c>>8&0xf+1) % 13
return (((card >> 8) & 0xf) + 1) % 13;
};
exports.getAceRank = getAceRank;
const rankAceFiveLow = (mask, hand) => {
let rank = 0;
for (const card of hand) {
const n = (0, exports.getAceRank)(card);
rank |= (1 << n) | ((((mask & (1 << n)) >>> n) & 1) * 0x8000);
mask |= 1 << n;
}
return rank;
};
exports.rankAceFiveLow = rankAceFiveLow;
// RankEightOrBetter is a 8-or-better low rank eval func. [Ace]'s are low,
// [Straight]'s and [Flush]'s do not count.
const rankEightOrBetter = (hand) => {
return (0, exports.rankAceFiveLow)(0xff00, hand);
};
exports.rankEightOrBetter = rankEightOrBetter;
const rankOmahaHand = (pocket, board, low = false) => {
let bestHand = [pocket[0], pocket[1], pocket[2], pocket[3], board[0]];
let bestRank = exports.INVALID;
let lowBestHand = [pocket[0], pocket[1], pocket[2], pocket[3], board[0]];
let lowBestRank = exports.INVALID;
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 10; j++) {
const hand = [
pocket[t4c2[i][0]],
pocket[t4c2[i][1]],
board[t5c3[j][0]],
board[t5c3[j][1]],
board[t5c3[j][2]],
];
const rank = (0, cactusFastRankHand_1.cactusFastRankHand)(hand);
if (rank < bestRank) {
bestHand = hand;
bestRank = rank;
}
if (low) {
const lowRank = (0, exports.rankEightOrBetter)(hand);
if (lowRank < lowBestRank) {
lowBestHand = hand;
lowBestRank = lowRank;
}
}
}
}
if (low && lowBestRank < exports.EIGHT_OR_BETTER_MAX) {
return {
rank: bestRank,
madeHand: bestHand,
low: {
rank: lowBestRank,
madeHand: lowBestHand,
},
};
}
return {
rank: bestRank,
madeHand: bestHand,
};
};
exports.rankOmahaHand = rankOmahaHand;