@pokertools/evaluator 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Aurelius
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # @pokertools/evaluator
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@pokertools/evaluator.svg)](https://www.npmjs.com/package/@pokertools/evaluator)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A lightning-fast, strongly-typed Poker Hand Evaluator for Node.js and the browser.
7
+
8
+ Capable of evaluating **over 16 million 7-card hands per second** on a standard CPU. This library uses a Perfect Hash algorithm (based on Cactus Kev/Paul Senzee) optimized specifically for the V8 JavaScript engine.
9
+
10
+ ## 🚀 Features
11
+
12
+ - **Extreme Performance:** ~17M evaluations/sec (7-card hands).
13
+ - **Zero Garbage Collection:** Uses static memory buffers to prevent GC overhead during Monte Carlo simulations.
14
+ - **Flexible:** Supports 5, 6, and 7 card hands.
15
+ - **TypeScript:** Written in strict TypeScript with full type definitions.
16
+ - **Lightweight:** Zero runtime dependencies.
17
+
18
+ ## ⚡ Benchmarks
19
+
20
+ Comparison of 7-card hand evaluation speed (Node.js V8):
21
+
22
+ | Library | Input Type | Speed (Hands/Sec) | Relative Speed |
23
+ | :------------------------ | :---------- | :---------------- | :------------- |
24
+ | **@pokertools/evaluator** | **Integer** | **~17,900,000** | **100%** |
25
+ | phe | Integer | ~16,550,000 | 92.5% |
26
+ | poker-evaluator | String | ~1,390,000 | 7.7% |
27
+ | pokersolver | String | ~73,000 | 0.4% |
28
+
29
+ ### Raw Output
30
+
31
+ ```text
32
+ 🃏 Starting Benchmark: 1000 random 7-card hands per cycle
33
+ ----------------------------------------------------------------
34
+ phe (Int) | 16,574,257 hands/sec | ±2.26%
35
+ poker-evaluator (Str) | 1,375,495 hands/sec | ±0.33%
36
+ pokersolver (Str) | 70,980 hands/sec | ±0.70%
37
+ @pokertools (Int) | 17,915,292 hands/sec | ±1.56%
38
+ ----------------------------------------------------------------
39
+ 🚀 WINNER: @pokertools (Int)
40
+ ```
41
+
42
+ _Benchmarks run on an M1 Air. Higher is better._
43
+
44
+ ## 📦 Installation
45
+
46
+ ```bash
47
+ npm install @pokertools/evaluator
48
+ ```
49
+
50
+ ## 📖 Usage
51
+
52
+ ### 1. Basic Usage (Strings)
53
+
54
+ If you are building a UI or simple game logic, string inputs are easiest to work with.
55
+
56
+ ```typescript
57
+ import { evaluateBoard, rankBoard, rankDescription } from "@pokertools/evaluator";
58
+
59
+ // 1. Get a raw strength score (lower is better)
60
+ const score = evaluateBoard("Ah Kh Qh Jh Th 2c 3c");
61
+ console.log(score); // 1 (Royal Flush is the lowest/best number)
62
+
63
+ // 2. Get the Rank Category (Enum)
64
+ const rank = rankBoard("Ah As Ks Kd Qs Qd 2c");
65
+ console.log(rankDescription(rank)); // "Two Pair"
66
+ ```
67
+
68
+ ### 2. High-Performance Usage (Integers)
69
+
70
+ If you are building an Equity Calculator or AI Solver, you should convert cards to integers **once** and pass integers around your system. This creates a 12x performance boost by skipping string parsing.
71
+
72
+ ```typescript
73
+ import { evaluate, getCardCode, rank, HandRank } from "@pokertools/evaluator";
74
+
75
+ // Convert strings to integers once
76
+ const holeCards = [getCardCode("As"), getCardCode("Ah")];
77
+ const board = [getCardCode("Ks"), getCardCode("Kh"), getCardCode("Qs")];
78
+
79
+ // Combine arrays (Spread operator is fast enough for small arrays)
80
+ const hand = [...holeCards, ...board];
81
+
82
+ // Evaluate
83
+ const strength = evaluate(hand);
84
+
85
+ // Check Rank
86
+ if (rank(hand) === HandRank.FullHouse) {
87
+ console.log("We have a boat!");
88
+ }
89
+ ```
90
+
91
+ ## 📚 API Reference
92
+
93
+ ### Core Functions
94
+
95
+ #### `evaluate(codes: number[]): number`
96
+
97
+ The fastest evaluation method. Accepts an array of 5, 6, or 7 integers. Returns a raw score (lower is better).
98
+
99
+ - Royal Flush: 1
100
+ - ...
101
+ - Worst High Card: 7462
102
+
103
+ #### `evaluateStrings(cards: string[]): number`
104
+
105
+ Helper to evaluate an array of strings like `['Ah', 'Td', ...]`.
106
+
107
+ #### `evaluateBoard(board: string): number`
108
+
109
+ Helper to evaluate a space-separated string like `"Ah Td 2c"`.
110
+
111
+ ### Ranking Helpers
112
+
113
+ #### `rank(codes: number[]): HandRank`
114
+
115
+ Returns the `HandRank` enum (0-8) for a set of card integers.
116
+
117
+ #### `HandRank` (Enum)
118
+
119
+ ```typescript
120
+ enum HandRank {
121
+ StraightFlush = 0,
122
+ FourOfAKind = 1,
123
+ FullHouse = 2,
124
+ Flush = 3,
125
+ Straight = 4,
126
+ ThreeOfAKind = 5,
127
+ TwoPair = 6,
128
+ OnePair = 7,
129
+ HighCard = 8,
130
+ }
131
+ ```
132
+
133
+ #### `rankDescription(rank: HandRank): string`
134
+
135
+ Returns human-readable strings like "Full House" or "High Card".
136
+
137
+ ### Card Encoding
138
+
139
+ #### `getCardCode(cardStr: string): number`
140
+
141
+ Converts a card string (e.g., `"Ah"`) into the optimized integer format used by this library.
142
+
143
+ #### `stringifyCardCode(code: number): string`
144
+
145
+ Converts an integer back into a readable string.
146
+
147
+ ## 🧮 Algorithm
148
+
149
+ This library implements the **Perfect Hash** algorithm.
150
+
151
+ 1. It first checks for a Flush using a bitmask OR operation.
152
+ 2. If no flush, it calculates a unique prime-product hash (Quinary) based on the rank counts.
153
+ 3. This hash is used as an index into a pre-computed DAG (Directed Acyclic Graph) lookup table to immediately return the hand strength.
154
+
155
+ This approach avoids expensive sorting or pattern matching operations found in slower libraries.
156
+
157
+ ## License
158
+
159
+ MIT
@@ -0,0 +1,3 @@
1
+ export declare function evaluate5Cards(cards: number[]): number;
2
+ export declare function evaluate6Cards(cards: number[]): number;
3
+ export declare function evaluate7Cards(cards: number[]): number;
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evaluate5Cards = evaluate5Cards;
4
+ exports.evaluate6Cards = evaluate6Cards;
5
+ exports.evaluate7Cards = evaluate7Cards;
6
+ const bit_masks_1 = require("../tables/bit-masks");
7
+ const dp_1 = require("../tables/dp");
8
+ const flush_1 = require("../tables/flush");
9
+ const no_flush_5_1 = require("../tables/no-flush-5");
10
+ const no_flush_6_1 = require("../tables/no-flush-6");
11
+ const no_flush_7_1 = require("../tables/no-flush-7");
12
+ const hash_1 = require("./hash");
13
+ /**
14
+ * Static buffers to prevent garbage collection overhead during hot loop evaluations.
15
+ *
16
+ * @internal
17
+ * @warning NOT THREAD-SAFE / NOT RE-ENTRANT
18
+ *
19
+ * These static arrays are reused across all evaluate() calls within the same
20
+ * JavaScript context to eliminate GC pressure during Monte Carlo simulations.
21
+ *
22
+ * **Thread Safety Implications:**
23
+ * - Safe for standard Node.js/Browser single-threaded execution
24
+ * - Safe for async/await code (each await yields control)
25
+ * - NOT safe if called recursively (don't call evaluate() from within evaluate())
26
+ * - NOT safe with SharedArrayBuffer or true multi-threaded contexts
27
+ * - NOT safe if multiple evaluate() calls are interleaved in the same tick
28
+ *
29
+ * **Performance Trade-off:**
30
+ * Using static buffers provides ~12% speed improvement (17M vs 15M hands/sec)
31
+ * by avoiding array allocations in the hot path. The non-reentrancy is acceptable
32
+ * because poker hand evaluation is a synchronous, non-recursive operation.
33
+ *
34
+ * @example
35
+ * // ✅ SAFE: Sequential evaluation
36
+ * const score1 = evaluate(hand1);
37
+ * const score2 = evaluate(hand2);
38
+ *
39
+ * @example
40
+ * // ✅ SAFE: Async is OK (yields between calls)
41
+ * for (const hand of hands) {
42
+ * const score = evaluate(hand);
43
+ * await saveToDatabase(score);
44
+ * }
45
+ *
46
+ * @example
47
+ * // ❌ UNSAFE: Recursive call
48
+ * function badIdea(cards) {
49
+ * if (cards.length > 7) {
50
+ * return evaluate(cards.slice(0, 7)); // Corrupts static buffers!
51
+ * }
52
+ * return evaluate(cards);
53
+ * }
54
+ */
55
+ const suitBinary = [0, 0, 0, 0];
56
+ const quinary = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
57
+ /**
58
+ * Fast reset of static buffers.
59
+ * Manual index assignment is faster than .fill(0).
60
+ */
61
+ function resetBuffers() {
62
+ suitBinary[0] = 0;
63
+ suitBinary[1] = 0;
64
+ suitBinary[2] = 0;
65
+ suitBinary[3] = 0;
66
+ quinary[0] = 0;
67
+ quinary[1] = 0;
68
+ quinary[2] = 0;
69
+ quinary[3] = 0;
70
+ quinary[4] = 0;
71
+ quinary[5] = 0;
72
+ quinary[6] = 0;
73
+ quinary[7] = 0;
74
+ quinary[8] = 0;
75
+ quinary[9] = 0;
76
+ quinary[10] = 0;
77
+ quinary[11] = 0;
78
+ quinary[12] = 0;
79
+ }
80
+ function evaluate5Cards(cards) {
81
+ const c1 = cards[0], c2 = cards[1], c3 = cards[2], c4 = cards[3], c5 = cards[4];
82
+ resetBuffers();
83
+ // 1. Calculate Suit Hash
84
+ const suitHash = bit_masks_1.SUITBIT_BY_ID[c1] +
85
+ bit_masks_1.SUITBIT_BY_ID[c2] +
86
+ bit_masks_1.SUITBIT_BY_ID[c3] +
87
+ bit_masks_1.SUITBIT_BY_ID[c4] +
88
+ bit_masks_1.SUITBIT_BY_ID[c5];
89
+ // 2. Populate Rank Frequency (Quinary)
90
+ quinary[c1 >> 2]++;
91
+ quinary[c2 >> 2]++;
92
+ quinary[c3 >> 2]++;
93
+ quinary[c4 >> 2]++;
94
+ quinary[c5 >> 2]++;
95
+ // 3. Check for Flush using DP Table
96
+ if (dp_1.SUITS_HASH[suitHash]) {
97
+ suitBinary[c1 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c1];
98
+ suitBinary[c2 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c2];
99
+ suitBinary[c3 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c3];
100
+ suitBinary[c4 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c4];
101
+ suitBinary[c5 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c5];
102
+ return flush_1.FLUSH_LOOKUP[suitBinary[dp_1.SUITS_HASH[suitHash] - 1]];
103
+ }
104
+ // 4. Check Hash Table
105
+ const hash = (0, hash_1.hashQuinary)(quinary, 13, 5);
106
+ return no_flush_5_1.NO_FLUSH_5[hash];
107
+ }
108
+ function evaluate6Cards(cards) {
109
+ const c1 = cards[0], c2 = cards[1], c3 = cards[2], c4 = cards[3], c5 = cards[4], c6 = cards[5];
110
+ resetBuffers();
111
+ const suitHash = bit_masks_1.SUITBIT_BY_ID[c1] +
112
+ bit_masks_1.SUITBIT_BY_ID[c2] +
113
+ bit_masks_1.SUITBIT_BY_ID[c3] +
114
+ bit_masks_1.SUITBIT_BY_ID[c4] +
115
+ bit_masks_1.SUITBIT_BY_ID[c5] +
116
+ bit_masks_1.SUITBIT_BY_ID[c6];
117
+ quinary[c1 >> 2]++;
118
+ quinary[c2 >> 2]++;
119
+ quinary[c3 >> 2]++;
120
+ quinary[c4 >> 2]++;
121
+ quinary[c5 >> 2]++;
122
+ quinary[c6 >> 2]++;
123
+ if (dp_1.SUITS_HASH[suitHash]) {
124
+ suitBinary[c1 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c1];
125
+ suitBinary[c2 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c2];
126
+ suitBinary[c3 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c3];
127
+ suitBinary[c4 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c4];
128
+ suitBinary[c5 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c5];
129
+ suitBinary[c6 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c6];
130
+ return flush_1.FLUSH_LOOKUP[suitBinary[dp_1.SUITS_HASH[suitHash] - 1]];
131
+ }
132
+ const hash = (0, hash_1.hashQuinary)(quinary, 13, 6);
133
+ return no_flush_6_1.NO_FLUSH_6[hash];
134
+ }
135
+ function evaluate7Cards(cards) {
136
+ const c1 = cards[0], c2 = cards[1], c3 = cards[2], c4 = cards[3], c5 = cards[4], c6 = cards[5], c7 = cards[6];
137
+ resetBuffers();
138
+ const suitHash = bit_masks_1.SUITBIT_BY_ID[c1] +
139
+ bit_masks_1.SUITBIT_BY_ID[c2] +
140
+ bit_masks_1.SUITBIT_BY_ID[c3] +
141
+ bit_masks_1.SUITBIT_BY_ID[c4] +
142
+ bit_masks_1.SUITBIT_BY_ID[c5] +
143
+ bit_masks_1.SUITBIT_BY_ID[c6] +
144
+ bit_masks_1.SUITBIT_BY_ID[c7];
145
+ quinary[c1 >> 2]++;
146
+ quinary[c2 >> 2]++;
147
+ quinary[c3 >> 2]++;
148
+ quinary[c4 >> 2]++;
149
+ quinary[c5 >> 2]++;
150
+ quinary[c6 >> 2]++;
151
+ quinary[c7 >> 2]++;
152
+ if (dp_1.SUITS_HASH[suitHash]) {
153
+ suitBinary[c1 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c1];
154
+ suitBinary[c2 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c2];
155
+ suitBinary[c3 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c3];
156
+ suitBinary[c4 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c4];
157
+ suitBinary[c5 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c5];
158
+ suitBinary[c6 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c6];
159
+ suitBinary[c7 & 0x3] |= bit_masks_1.BINARIES_BY_ID[c7];
160
+ return flush_1.FLUSH_LOOKUP[suitBinary[dp_1.SUITS_HASH[suitHash] - 1]];
161
+ }
162
+ const hash = (0, hash_1.hashQuinary)(quinary, 13, 7);
163
+ return no_flush_7_1.NO_FLUSH_7[hash];
164
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Perfect Hash algorithm for quinary sums.
3
+ *
4
+ * @param q - The frequency array of ranks (count of 2s, count of 3s, etc.)
5
+ * @param len - Number of ranks (always 13)
6
+ * @param k - Number of cards (5, 6, or 7)
7
+ */
8
+ export declare function hashQuinary(q: number[], len: number, k: number): number;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hashQuinary = hashQuinary;
4
+ const dp_1 = require("../tables/dp");
5
+ /**
6
+ * Perfect Hash algorithm for quinary sums.
7
+ *
8
+ * @param q - The frequency array of ranks (count of 2s, count of 3s, etc.)
9
+ * @param len - Number of ranks (always 13)
10
+ * @param k - Number of cards (5, 6, or 7)
11
+ */
12
+ function hashQuinary(q, len, k) {
13
+ let sum = 0;
14
+ for (let i = 0; i < len; i++) {
15
+ sum += dp_1.DP_MATRIX[q[i]][len - i - 1][k];
16
+ k -= q[i];
17
+ // Optimization: if we have accounted for all cards (k=0), we can stop early.
18
+ if (k <= 0)
19
+ break;
20
+ }
21
+ return sum;
22
+ }
@@ -0,0 +1,33 @@
1
+ import { HandRank } from "./models/hand-rank";
2
+ export { HandRank, HAND_RANK_DESCRIPTIONS } from "./models/hand-rank";
3
+ export { stringifyCardCode, getCardCode, getCardCodes } from "./utils/card";
4
+ /**
5
+ * Primary entry point.
6
+ * Evaluates 5, 6, or 7 card integer codes.
7
+ * Returns a strength score (lower is better).
8
+ *
9
+ * @param codes Array of card integers (e.g. generated by getCardCode)
10
+ */
11
+ export declare function evaluate(codes: number[]): number;
12
+ /**
13
+ * Evaluates an array of card strings (e.g. ['As', 'Kd', ...]).
14
+ * Slightly slower than `evaluate` due to parsing overhead.
15
+ */
16
+ export declare function evaluateStrings(cards: string[]): number;
17
+ /**
18
+ * Evaluates a board string (e.g. "As Kd Qh ...").
19
+ */
20
+ export declare function evaluateBoard(board: string): number;
21
+ /**
22
+ * Returns the HandRank (enum 0-8) for a given set of card integers.
23
+ * Useful for categorizing hands (e.g. "Full House").
24
+ */
25
+ export declare function rank(codes: number[]): HandRank;
26
+ /**
27
+ * Returns the HandRank (enum 0-8) for a given board string.
28
+ */
29
+ export declare function rankBoard(board: string): HandRank;
30
+ /**
31
+ * Returns the human-readable name of the hand rank (e.g. "Straight Flush").
32
+ */
33
+ export declare function rankDescription(rank: HandRank): string;
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCardCodes = exports.getCardCode = exports.stringifyCardCode = exports.HAND_RANK_DESCRIPTIONS = exports.HandRank = void 0;
4
+ exports.evaluate = evaluate;
5
+ exports.evaluateStrings = evaluateStrings;
6
+ exports.evaluateBoard = evaluateBoard;
7
+ exports.rank = rank;
8
+ exports.rankBoard = rankBoard;
9
+ exports.rankDescription = rankDescription;
10
+ const evaluator_1 = require("./core/evaluator");
11
+ const card_1 = require("./utils/card");
12
+ const hand_rank_1 = require("./models/hand-rank");
13
+ // Export Constants and Enums for consumers
14
+ var hand_rank_2 = require("./models/hand-rank");
15
+ Object.defineProperty(exports, "HandRank", { enumerable: true, get: function () { return hand_rank_2.HandRank; } });
16
+ Object.defineProperty(exports, "HAND_RANK_DESCRIPTIONS", { enumerable: true, get: function () { return hand_rank_2.HAND_RANK_DESCRIPTIONS; } });
17
+ var card_2 = require("./utils/card");
18
+ Object.defineProperty(exports, "stringifyCardCode", { enumerable: true, get: function () { return card_2.stringifyCardCode; } });
19
+ Object.defineProperty(exports, "getCardCode", { enumerable: true, get: function () { return card_2.getCardCode; } });
20
+ Object.defineProperty(exports, "getCardCodes", { enumerable: true, get: function () { return card_2.getCardCodes; } });
21
+ /**
22
+ * Primary entry point.
23
+ * Evaluates 5, 6, or 7 card integer codes.
24
+ * Returns a strength score (lower is better).
25
+ *
26
+ * @param codes Array of card integers (e.g. generated by getCardCode)
27
+ */
28
+ function evaluate(codes) {
29
+ const len = codes.length;
30
+ if (len === 7)
31
+ return (0, evaluator_1.evaluate7Cards)(codes);
32
+ if (len === 6)
33
+ return (0, evaluator_1.evaluate6Cards)(codes);
34
+ if (len === 5)
35
+ return (0, evaluator_1.evaluate5Cards)(codes);
36
+ throw new Error(`Evaluator requires 5, 6, or 7 cards. Received ${len}.`);
37
+ }
38
+ /**
39
+ * Evaluates an array of card strings (e.g. ['As', 'Kd', ...]).
40
+ * Slightly slower than `evaluate` due to parsing overhead.
41
+ */
42
+ function evaluateStrings(cards) {
43
+ return evaluate((0, card_1.getCardCodes)(cards));
44
+ }
45
+ /**
46
+ * Evaluates a board string (e.g. "As Kd Qh ...").
47
+ */
48
+ function evaluateBoard(board) {
49
+ return evaluate((0, card_1.getBoardCodes)(board));
50
+ }
51
+ /**
52
+ * Returns the HandRank (enum 0-8) for a given set of card integers.
53
+ * Useful for categorizing hands (e.g. "Full House").
54
+ */
55
+ function rank(codes) {
56
+ return (0, hand_rank_1.getHandRank)(evaluate(codes));
57
+ }
58
+ /**
59
+ * Returns the HandRank (enum 0-8) for a given board string.
60
+ */
61
+ function rankBoard(board) {
62
+ return (0, hand_rank_1.getHandRank)(evaluateBoard(board));
63
+ }
64
+ /**
65
+ * Returns the human-readable name of the hand rank (e.g. "Straight Flush").
66
+ */
67
+ function rankDescription(rank) {
68
+ return hand_rank_1.HAND_RANK_DESCRIPTIONS[rank];
69
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Standard Poker Hand Ranks.
3
+ * Using const enum for inlining performance.
4
+ */
5
+ export declare const enum HandRank {
6
+ StraightFlush = 0,
7
+ FourOfAKind = 1,
8
+ FullHouse = 2,
9
+ Flush = 3,
10
+ Straight = 4,
11
+ ThreeOfAKind = 5,
12
+ TwoPair = 6,
13
+ OnePair = 7,
14
+ HighCard = 8
15
+ }
16
+ export declare const HAND_RANK_DESCRIPTIONS: Readonly<Record<HandRank, string>>;
17
+ /**
18
+ * Converts a raw evaluator score into a HandRank category.
19
+ * Thresholds are based on the specific hash algorithm used.
20
+ */
21
+ export declare function getHandRank(val: number): HandRank;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HAND_RANK_DESCRIPTIONS = exports.HandRank = void 0;
4
+ exports.getHandRank = getHandRank;
5
+ /**
6
+ * Standard Poker Hand Ranks.
7
+ * Using const enum for inlining performance.
8
+ */
9
+ var HandRank;
10
+ (function (HandRank) {
11
+ HandRank[HandRank["StraightFlush"] = 0] = "StraightFlush";
12
+ HandRank[HandRank["FourOfAKind"] = 1] = "FourOfAKind";
13
+ HandRank[HandRank["FullHouse"] = 2] = "FullHouse";
14
+ HandRank[HandRank["Flush"] = 3] = "Flush";
15
+ HandRank[HandRank["Straight"] = 4] = "Straight";
16
+ HandRank[HandRank["ThreeOfAKind"] = 5] = "ThreeOfAKind";
17
+ HandRank[HandRank["TwoPair"] = 6] = "TwoPair";
18
+ HandRank[HandRank["OnePair"] = 7] = "OnePair";
19
+ HandRank[HandRank["HighCard"] = 8] = "HighCard";
20
+ })(HandRank || (exports.HandRank = HandRank = {}));
21
+ exports.HAND_RANK_DESCRIPTIONS = {
22
+ [0 /* HandRank.StraightFlush */]: "Straight Flush",
23
+ [1 /* HandRank.FourOfAKind */]: "Four of a Kind",
24
+ [2 /* HandRank.FullHouse */]: "Full House",
25
+ [3 /* HandRank.Flush */]: "Flush",
26
+ [4 /* HandRank.Straight */]: "Straight",
27
+ [5 /* HandRank.ThreeOfAKind */]: "Three of a Kind",
28
+ [6 /* HandRank.TwoPair */]: "Two Pair",
29
+ [7 /* HandRank.OnePair */]: "One Pair",
30
+ [8 /* HandRank.HighCard */]: "High Card",
31
+ };
32
+ /**
33
+ * Converts a raw evaluator score into a HandRank category.
34
+ * Thresholds are based on the specific hash algorithm used.
35
+ */
36
+ function getHandRank(val) {
37
+ if (val > 6185)
38
+ return 8 /* HandRank.HighCard */; // 1277 high cards
39
+ if (val > 3325)
40
+ return 7 /* HandRank.OnePair */; // 2860 one pairs
41
+ if (val > 2467)
42
+ return 6 /* HandRank.TwoPair */; // 858 two pairs
43
+ if (val > 1609)
44
+ return 5 /* HandRank.ThreeOfAKind */; // 858 three-kinds
45
+ if (val > 1599)
46
+ return 4 /* HandRank.Straight */; // 10 straights
47
+ if (val > 322)
48
+ return 3 /* HandRank.Flush */; // 1277 flushes
49
+ if (val > 166)
50
+ return 2 /* HandRank.FullHouse */; // 156 full houses
51
+ if (val > 10)
52
+ return 1 /* HandRank.FourOfAKind */; // 156 four-kinds
53
+ return 0 /* HandRank.StraightFlush */; // 10 straight-flushes
54
+ }
@@ -0,0 +1,2 @@
1
+ export declare const BINARIES_BY_ID: readonly number[];
2
+ export declare const SUITBIT_BY_ID: readonly number[];
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SUITBIT_BY_ID = exports.BINARIES_BY_ID = void 0;
4
+ exports.BINARIES_BY_ID = [
5
+ 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0x10, 0x10, 0x10,
6
+ 0x10, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x100, 0x100, 0x100,
7
+ 0x100, 0x200, 0x200, 0x200, 0x200, 0x400, 0x400, 0x400, 0x400, 0x800, 0x800, 0x800, 0x800, 0x1000,
8
+ 0x1000, 0x1000, 0x1000,
9
+ ];
10
+ exports.SUITBIT_BY_ID = [
11
+ 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1,
12
+ 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8,
13
+ 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40, 0x200, 0x1, 0x8, 0x40,
14
+ 0x200,
15
+ ];
@@ -0,0 +1,3 @@
1
+ export declare const CHOOSE_MATRIX: readonly number[][];
2
+ export declare const DP_MATRIX: readonly number[][][];
3
+ export declare const SUITS_HASH: readonly number[];