poker-extval 1.1.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 +95 -15
- package/evaluator.js +549 -294
- package/package.json +1 -1
package/evaluator.js
CHANGED
|
@@ -166,36 +166,36 @@ class HandEvaluator {
|
|
|
166
166
|
this.evaluation = {};
|
|
167
167
|
|
|
168
168
|
// Call all evaluation methods - they will populate this.evaluation
|
|
169
|
-
this
|
|
170
|
-
this
|
|
171
|
-
this
|
|
172
|
-
this
|
|
173
|
-
this
|
|
174
|
-
this
|
|
175
|
-
this
|
|
176
|
-
this
|
|
177
|
-
this
|
|
178
|
-
this
|
|
179
|
-
this
|
|
180
|
-
this
|
|
181
|
-
this
|
|
182
|
-
this
|
|
183
|
-
this
|
|
184
|
-
this
|
|
185
|
-
this
|
|
186
|
-
this
|
|
187
|
-
this
|
|
188
|
-
this
|
|
189
|
-
this
|
|
190
|
-
this
|
|
191
|
-
this
|
|
192
|
-
this
|
|
169
|
+
this.#isFourOfAKind();
|
|
170
|
+
this.#isFullHouse();
|
|
171
|
+
this.#isThreeOfAKind();
|
|
172
|
+
this.#isTwoPair();
|
|
173
|
+
this.#isPair();
|
|
174
|
+
this.#isFlush();
|
|
175
|
+
this.#isTopPair();
|
|
176
|
+
this.#isMiddlePair();
|
|
177
|
+
this.#isBottomPair();
|
|
178
|
+
this.#isTopAndMiddlePair();
|
|
179
|
+
this.#isTopAndBottomPair();
|
|
180
|
+
this.#isMiddleAndBottomPair();
|
|
181
|
+
this.#isTopThreeOfAKind();
|
|
182
|
+
this.#isMiddleThreeOfAKind();
|
|
183
|
+
this.#isBottomThreeOfAKind();
|
|
184
|
+
this.#isFlushDraw();
|
|
185
|
+
this.#isBackdoorFlushDraw();
|
|
186
|
+
this.#isStraight();
|
|
187
|
+
this.#isRoyalFlush();
|
|
188
|
+
this.#isStraightFlush();
|
|
189
|
+
this.#isOpenEndedStraightDraw();
|
|
190
|
+
this.#isInsideStraightDraw();
|
|
191
|
+
this.#isStraightDraw();
|
|
192
|
+
this.#isHighCard();
|
|
193
193
|
|
|
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;
|
|
@@ -281,18 +281,12 @@ class HandEvaluator {
|
|
|
281
281
|
* Sorts cards from highest to lowest rank using standard poker ranking (Ace high).
|
|
282
282
|
* Modifies the array in place. If no cards parameter is provided, sorts this.cards.
|
|
283
283
|
*
|
|
284
|
+
* @private
|
|
284
285
|
* @param {string[]} [cards=null] - Optional array of card strings to sort.
|
|
285
286
|
* If null or undefined, sorts this.cards instead.
|
|
286
287
|
* @returns {void}
|
|
287
|
-
* @example
|
|
288
|
-
* const evaluator = new HandEvaluator(['2s', 'As', 'Ks', '5s', 'Ts']);
|
|
289
|
-
* evaluator.sort(); // Modifies evaluator.cards to ['As', 'Ks', 'Ts', '5s', '2s']
|
|
290
|
-
*
|
|
291
|
-
* @example
|
|
292
|
-
* const cards = ['2s', 'As', 'Ks'];
|
|
293
|
-
* evaluator.sort(cards); // Modifies cards array to ['As', 'Ks', '2s']
|
|
294
288
|
*/
|
|
295
|
-
sort(cards = null) {
|
|
289
|
+
#sort(cards = null) {
|
|
296
290
|
const cardsToSort = cards || this.cards;
|
|
297
291
|
cardsToSort.sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
298
292
|
}
|
|
@@ -302,14 +296,12 @@ class HandEvaluator {
|
|
|
302
296
|
* Modifies the array in place. Used for evaluating wheel straights (A-2-3-4-5).
|
|
303
297
|
* If no cards parameter is provided, sorts this.cards.
|
|
304
298
|
*
|
|
299
|
+
* @private
|
|
305
300
|
* @param {string[]} [cards=null] - Optional array of card strings to sort.
|
|
306
301
|
* If null or undefined, sorts this.cards instead.
|
|
307
302
|
* @returns {void}
|
|
308
|
-
* @example
|
|
309
|
-
* const evaluator = new HandEvaluator(['As', '2s', '3s', '4s', '5s']);
|
|
310
|
-
* evaluator.sortWheel(); // Modifies evaluator.cards to ['5s', '4s', '3s', '2s', 'As']
|
|
311
303
|
*/
|
|
312
|
-
sortWheel(cards = null) {
|
|
304
|
+
#sortWheel(cards = null) {
|
|
313
305
|
const cardsToSort = cards || this.cards;
|
|
314
306
|
cardsToSort.sort((a, b) => this.#getRankValueWheel(b) - this.#getRankValueWheel(a));
|
|
315
307
|
}
|
|
@@ -318,18 +310,12 @@ class HandEvaluator {
|
|
|
318
310
|
* Calculates the distance (rank difference) between the highest and lowest card.
|
|
319
311
|
* Uses standard poker ranking (Ace high). Distance of 4 indicates a possible straight.
|
|
320
312
|
*
|
|
313
|
+
* @private
|
|
321
314
|
* @param {string[]} [cards=null] - Optional array of card strings to evaluate.
|
|
322
315
|
* If null or undefined, uses this.cards instead.
|
|
323
316
|
* @returns {number} Distance between highest and lowest card (0-12)
|
|
324
|
-
* @example
|
|
325
|
-
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', 'Ts']);
|
|
326
|
-
* evaluator.getDistance(); // Returns 4 (A=13, T=9, 13-9=4)
|
|
327
|
-
*
|
|
328
|
-
* @example
|
|
329
|
-
* evaluator.getDistance(['As', '2s', '3s', '4s', '5s']);
|
|
330
|
-
* // Returns 12 (A=13, 2=1, 13-1=12)
|
|
331
317
|
*/
|
|
332
|
-
getDistance(cards = null) {
|
|
318
|
+
#getDistance(cards = null) {
|
|
333
319
|
if (cards) {
|
|
334
320
|
// If cards array provided, sort a copy and calculate distance
|
|
335
321
|
if (cards.length === 0) return 0;
|
|
@@ -338,7 +324,7 @@ class HandEvaluator {
|
|
|
338
324
|
}
|
|
339
325
|
// If no cards provided, use instance cards property
|
|
340
326
|
if (this.cards.length === 0) return 0;
|
|
341
|
-
this
|
|
327
|
+
this.#sort();
|
|
342
328
|
return this.#getRankValue(this.cards[0]) - this.#getRankValue(this.cards[this.cards.length - 1]);
|
|
343
329
|
}
|
|
344
330
|
|
|
@@ -346,14 +332,12 @@ class HandEvaluator {
|
|
|
346
332
|
* Calculates the distance (rank difference) between the highest and lowest card using wheel ranking.
|
|
347
333
|
* Uses wheel ranking (Ace low). Distance of 4 indicates a possible wheel straight (A-2-3-4-5).
|
|
348
334
|
*
|
|
335
|
+
* @private
|
|
349
336
|
* @param {string[]} [cards=null] - Optional array of card strings to evaluate.
|
|
350
337
|
* If null or undefined, uses this.cards instead.
|
|
351
338
|
* @returns {number} Distance between highest and lowest card using wheel ranking (0-12)
|
|
352
|
-
* @example
|
|
353
|
-
* const evaluator = new HandEvaluator(['As', '2s', '3s', '4s', '5s']);
|
|
354
|
-
* evaluator.getDistanceWheel(); // Returns 4 (5=5, A=1, 5-1=4) - wheel straight
|
|
355
339
|
*/
|
|
356
|
-
getDistanceWheel(cards = null) {
|
|
340
|
+
#getDistanceWheel(cards = null) {
|
|
357
341
|
if (cards) {
|
|
358
342
|
// If cards array provided, sort a copy using wheel values and calculate distance
|
|
359
343
|
if (cards.length === 0) return 0;
|
|
@@ -362,24 +346,18 @@ class HandEvaluator {
|
|
|
362
346
|
}
|
|
363
347
|
// If no cards provided, use instance cards property
|
|
364
348
|
if (this.cards.length === 0) return 0;
|
|
365
|
-
this
|
|
349
|
+
this.#sortWheel();
|
|
366
350
|
return this.#getRankValueWheel(this.cards[0]) - this.#getRankValueWheel(this.cards[this.cards.length - 1]);
|
|
367
351
|
}
|
|
368
352
|
|
|
369
353
|
/**
|
|
370
354
|
* Counts the occurrences of each suit in the hand.
|
|
371
355
|
*
|
|
356
|
+
* @private
|
|
372
357
|
* @returns {Object<string, number>} Object mapping suit to count:
|
|
373
358
|
* { s: count, h: count, d: count, c: count }
|
|
374
|
-
* @example
|
|
375
|
-
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', 'Ts']);
|
|
376
|
-
* evaluator.countSuits(); // Returns { s: 5 }
|
|
377
|
-
*
|
|
378
|
-
* @example
|
|
379
|
-
* const evaluator = new HandEvaluator(['As', 'Kh', 'Qd', 'Jc', 'Ts']);
|
|
380
|
-
* evaluator.countSuits(); // Returns { s: 2, h: 1, d: 1, c: 1 }
|
|
381
359
|
*/
|
|
382
|
-
countSuits() {
|
|
360
|
+
#countSuits() {
|
|
383
361
|
return this.cards.reduce((acc, card) => ({ ...acc, [card.slice(-1)]: (acc[card.slice(-1)] || 0) + 1 }), {});
|
|
384
362
|
}
|
|
385
363
|
|
|
@@ -387,17 +365,11 @@ class HandEvaluator {
|
|
|
387
365
|
* Counts the occurrences of each rank and returns sorted counts (descending).
|
|
388
366
|
* Used to identify pairs, three of a kind, four of a kind, etc.
|
|
389
367
|
*
|
|
368
|
+
* @private
|
|
390
369
|
* @returns {number[]} Array of rank counts sorted in descending order
|
|
391
|
-
* @example
|
|
392
|
-
* const evaluator = new HandEvaluator(['As', 'Ah', 'Ad', 'Kc', 'Ks']);
|
|
393
|
-
* evaluator.countRanks(); // Returns [3, 2] (three aces, two kings)
|
|
394
|
-
*
|
|
395
|
-
* @example
|
|
396
|
-
* const evaluator = new HandEvaluator(['As', 'Ah', 'Kd', 'Kc', 'Qs']);
|
|
397
|
-
* evaluator.countRanks(); // Returns [2, 2, 1] (two aces, two kings, one queen)
|
|
398
370
|
*/
|
|
399
|
-
countRanks() {
|
|
400
|
-
this
|
|
371
|
+
#countRanks() {
|
|
372
|
+
this.#sort();
|
|
401
373
|
const rankCounts = this.cards.reduce((acc, card) => (acc[card.slice(0, -1)] = (acc[card.slice(0, -1)] || 0) + 1, acc), {});
|
|
402
374
|
return Object.values(rankCounts).sort((a, b) => b - a);
|
|
403
375
|
}
|
|
@@ -412,7 +384,7 @@ class HandEvaluator {
|
|
|
412
384
|
* // Returns { A: 3, K: 2 }
|
|
413
385
|
*/
|
|
414
386
|
#getRankCounts() {
|
|
415
|
-
this
|
|
387
|
+
this.#sort();
|
|
416
388
|
return this.cards.reduce((acc, card) => (acc[card.slice(0, -1)] = (acc[card.slice(0, -1)] || 0) + 1, acc), {});
|
|
417
389
|
}
|
|
418
390
|
|
|
@@ -446,7 +418,7 @@ class HandEvaluator {
|
|
|
446
418
|
* // Returns [1, 1, 1, 2] (gap of 2 between J and 9)
|
|
447
419
|
*/
|
|
448
420
|
#getGaps() {
|
|
449
|
-
this
|
|
421
|
+
this.#sort();
|
|
450
422
|
const gaps = [];
|
|
451
423
|
for (let i = 0; i < 4; i++) {
|
|
452
424
|
gaps.push(this.#getRankValue(this.cards[i]) - this.#getRankValue(this.cards[i + 1]));
|
|
@@ -466,7 +438,7 @@ class HandEvaluator {
|
|
|
466
438
|
* // Returns [1, 1, 1, 1] (all gaps are 1)
|
|
467
439
|
*/
|
|
468
440
|
#getGapsWheel() {
|
|
469
|
-
this
|
|
441
|
+
this.#sortWheel();
|
|
470
442
|
const gaps = [];
|
|
471
443
|
for (let i = 0; i < 4; i++) {
|
|
472
444
|
gaps.push(this.#getRankValueWheel(this.cards[i]) - this.#getRankValueWheel(this.cards[i + 1]));
|
|
@@ -484,9 +456,9 @@ class HandEvaluator {
|
|
|
484
456
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Ad', 'Ac', 'Ks']);
|
|
485
457
|
* evaluator.isFourOfAKind(); // Returns true
|
|
486
458
|
*/
|
|
487
|
-
isFourOfAKind() {
|
|
459
|
+
#isFourOfAKind() {
|
|
488
460
|
if (this.evaluation.isFourOfAKind !== undefined) return this.evaluation.isFourOfAKind;
|
|
489
|
-
const ranks = this
|
|
461
|
+
const ranks = this.#countRanks();
|
|
490
462
|
return this.evaluation.isFourOfAKind = ranks[0] === 4 && ranks[1] === 1;
|
|
491
463
|
}
|
|
492
464
|
|
|
@@ -498,9 +470,9 @@ class HandEvaluator {
|
|
|
498
470
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Ad', 'Kc', 'Ks']);
|
|
499
471
|
* evaluator.isFullHouse(); // Returns true
|
|
500
472
|
*/
|
|
501
|
-
isFullHouse() {
|
|
473
|
+
#isFullHouse() {
|
|
502
474
|
if (this.evaluation.isFullHouse !== undefined) return this.evaluation.isFullHouse;
|
|
503
|
-
const ranks = this
|
|
475
|
+
const ranks = this.#countRanks();
|
|
504
476
|
return this.evaluation.isFullHouse = ranks[0] === 3 && ranks[1] === 2;
|
|
505
477
|
}
|
|
506
478
|
|
|
@@ -512,9 +484,9 @@ class HandEvaluator {
|
|
|
512
484
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Ad', 'Kc', 'Qs']);
|
|
513
485
|
* evaluator.isThreeOfAKind(); // Returns true
|
|
514
486
|
*/
|
|
515
|
-
isThreeOfAKind() {
|
|
487
|
+
#isThreeOfAKind() {
|
|
516
488
|
if (this.evaluation.isThreeOfAKind !== undefined) return this.evaluation.isThreeOfAKind;
|
|
517
|
-
const ranks = this
|
|
489
|
+
const ranks = this.#countRanks();
|
|
518
490
|
return this.evaluation.isThreeOfAKind = ranks[0] === 3 && ranks[1] === 1 && ranks[2] === 1;
|
|
519
491
|
}
|
|
520
492
|
|
|
@@ -526,9 +498,9 @@ class HandEvaluator {
|
|
|
526
498
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Kd', 'Kc', 'Qs']);
|
|
527
499
|
* evaluator.isTwoPair(); // Returns true
|
|
528
500
|
*/
|
|
529
|
-
isTwoPair() {
|
|
501
|
+
#isTwoPair() {
|
|
530
502
|
if (this.evaluation.isTwoPair !== undefined) return this.evaluation.isTwoPair;
|
|
531
|
-
const ranks = this
|
|
503
|
+
const ranks = this.#countRanks();
|
|
532
504
|
return this.evaluation.isTwoPair = ranks[0] === 2 && ranks[1] === 2 && ranks[2] === 1;
|
|
533
505
|
}
|
|
534
506
|
|
|
@@ -540,9 +512,9 @@ class HandEvaluator {
|
|
|
540
512
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Kd', 'Qc', 'Js']);
|
|
541
513
|
* evaluator.isPair(); // Returns true
|
|
542
514
|
*/
|
|
543
|
-
isPair() {
|
|
515
|
+
#isPair() {
|
|
544
516
|
if (this.evaluation.isPair !== undefined) return this.evaluation.isPair;
|
|
545
|
-
const ranks = this
|
|
517
|
+
const ranks = this.#countRanks();
|
|
546
518
|
return this.evaluation.isPair = ranks[0] === 2 && ranks[1] === 1 && ranks[2] === 1 && ranks[3] === 1;
|
|
547
519
|
}
|
|
548
520
|
|
|
@@ -557,9 +529,9 @@ class HandEvaluator {
|
|
|
557
529
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Kd', 'Qc', 'Js']);
|
|
558
530
|
* evaluator.isTopPair(); // Returns true (pair of aces, highest card)
|
|
559
531
|
*/
|
|
560
|
-
isTopPair() {
|
|
532
|
+
#isTopPair() {
|
|
561
533
|
if (this.evaluation.isTopPair !== undefined) return this.evaluation.isTopPair;
|
|
562
|
-
if (this.evaluation.isPair === undefined) this
|
|
534
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
563
535
|
if (!this.evaluation.isPair) return this.evaluation.isTopPair = false;
|
|
564
536
|
const rankCounts = this.#getRankCounts();
|
|
565
537
|
return this.evaluation.isTopPair = rankCounts[this.cards[0].slice(0, -1)] === 2;
|
|
@@ -574,10 +546,10 @@ class HandEvaluator {
|
|
|
574
546
|
* const evaluator = new HandEvaluator(['As', 'Kh', 'Kd', 'Qc', 'Js']);
|
|
575
547
|
* evaluator.isMiddlePair(); // Returns true (pair of kings, not highest or lowest)
|
|
576
548
|
*/
|
|
577
|
-
isMiddlePair() {
|
|
549
|
+
#isMiddlePair() {
|
|
578
550
|
if (this.evaluation.isMiddlePair !== undefined) return this.evaluation.isMiddlePair;
|
|
579
|
-
if (this.evaluation.isPair === undefined) this
|
|
580
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
551
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
552
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
581
553
|
if (!this.evaluation.isPair || this.evaluation.isTwoPair) return this.evaluation.isMiddlePair = false;
|
|
582
554
|
const ranks = this.#getRankCountsSorted();
|
|
583
555
|
return this.evaluation.isMiddlePair = ranks.length === 4 && (ranks[1].count === 2 || ranks[2].count === 2);
|
|
@@ -592,9 +564,9 @@ class HandEvaluator {
|
|
|
592
564
|
* const evaluator = new HandEvaluator(['As', 'Kh', 'Qd', '2c', '2s']);
|
|
593
565
|
* evaluator.isBottomPair(); // Returns true (pair of deuces, lowest card)
|
|
594
566
|
*/
|
|
595
|
-
isBottomPair() {
|
|
567
|
+
#isBottomPair() {
|
|
596
568
|
if (this.evaluation.isBottomPair !== undefined) return this.evaluation.isBottomPair;
|
|
597
|
-
if (this.evaluation.isPair === undefined) this
|
|
569
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
598
570
|
if (!this.evaluation.isPair) return this.evaluation.isBottomPair = false;
|
|
599
571
|
const rankCounts = this.#getRankCounts();
|
|
600
572
|
return this.evaluation.isBottomPair = rankCounts[this.cards[this.cards.length - 1].slice(0, -1)] === 2;
|
|
@@ -611,9 +583,9 @@ class HandEvaluator {
|
|
|
611
583
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Kd', 'Kc', 'Qs']);
|
|
612
584
|
* evaluator.isTopAndMiddlePair(); // Returns true (aces and kings)
|
|
613
585
|
*/
|
|
614
|
-
isTopAndMiddlePair() {
|
|
586
|
+
#isTopAndMiddlePair() {
|
|
615
587
|
if (this.evaluation.isTopAndMiddlePair !== undefined) return this.evaluation.isTopAndMiddlePair;
|
|
616
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
588
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
617
589
|
if (!this.evaluation.isTwoPair) return this.evaluation.isTopAndMiddlePair = false;
|
|
618
590
|
const ranks = this.#getRankCountsSorted();
|
|
619
591
|
return this.evaluation.isTopAndMiddlePair = ranks[0].count === 2 && ranks[1].count === 2;
|
|
@@ -628,9 +600,9 @@ class HandEvaluator {
|
|
|
628
600
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Kd', 'Qc', '2s', '2h']);
|
|
629
601
|
* evaluator.isTopAndBottomPair(); // Returns true (aces and deuces)
|
|
630
602
|
*/
|
|
631
|
-
isTopAndBottomPair() {
|
|
603
|
+
#isTopAndBottomPair() {
|
|
632
604
|
if (this.evaluation.isTopAndBottomPair !== undefined) return this.evaluation.isTopAndBottomPair;
|
|
633
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
605
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
634
606
|
if (!this.evaluation.isTwoPair) return this.evaluation.isTopAndBottomPair = false;
|
|
635
607
|
const ranks = this.#getRankCountsSorted();
|
|
636
608
|
return this.evaluation.isTopAndBottomPair = ranks[0].count === 2 && ranks[ranks.length - 1].count === 2;
|
|
@@ -645,9 +617,9 @@ class HandEvaluator {
|
|
|
645
617
|
* const evaluator = new HandEvaluator(['As', 'Kh', 'Kd', '2c', '2s']);
|
|
646
618
|
* evaluator.isMiddleAndBottomPair(); // Returns true (kings and deuces)
|
|
647
619
|
*/
|
|
648
|
-
isMiddleAndBottomPair() {
|
|
620
|
+
#isMiddleAndBottomPair() {
|
|
649
621
|
if (this.evaluation.isMiddleAndBottomPair !== undefined) return this.evaluation.isMiddleAndBottomPair;
|
|
650
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
622
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
651
623
|
if (!this.evaluation.isTwoPair) return this.evaluation.isMiddleAndBottomPair = false;
|
|
652
624
|
const ranks = this.#getRankCountsSorted();
|
|
653
625
|
return this.evaluation.isMiddleAndBottomPair = ranks.length === 3 && ranks[1].count === 2 && ranks[2].count === 2;
|
|
@@ -664,9 +636,9 @@ class HandEvaluator {
|
|
|
664
636
|
* const evaluator = new HandEvaluator(['As', 'Ah', 'Ad', 'Kc', 'Qs']);
|
|
665
637
|
* evaluator.isTopThreeOfAKind(); // Returns true (three aces)
|
|
666
638
|
*/
|
|
667
|
-
isTopThreeOfAKind() {
|
|
639
|
+
#isTopThreeOfAKind() {
|
|
668
640
|
if (this.evaluation.isTopThreeOfAKind !== undefined) return this.evaluation.isTopThreeOfAKind;
|
|
669
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
641
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
670
642
|
if (!this.evaluation.isThreeOfAKind) return this.evaluation.isTopThreeOfAKind = false;
|
|
671
643
|
const rankCounts = this.#getRankCounts();
|
|
672
644
|
return this.evaluation.isTopThreeOfAKind = rankCounts[this.cards[0].slice(0, -1)] === 3;
|
|
@@ -681,9 +653,9 @@ class HandEvaluator {
|
|
|
681
653
|
* const evaluator = new HandEvaluator(['As', 'Kh', 'Kd', 'Kc', 'Qs']);
|
|
682
654
|
* evaluator.isMiddleThreeOfAKind(); // Returns true (three kings)
|
|
683
655
|
*/
|
|
684
|
-
isMiddleThreeOfAKind() {
|
|
656
|
+
#isMiddleThreeOfAKind() {
|
|
685
657
|
if (this.evaluation.isMiddleThreeOfAKind !== undefined) return this.evaluation.isMiddleThreeOfAKind;
|
|
686
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
658
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
687
659
|
if (!this.evaluation.isThreeOfAKind) return this.evaluation.isMiddleThreeOfAKind = false;
|
|
688
660
|
const ranks = this.#getRankCountsSorted();
|
|
689
661
|
return this.evaluation.isMiddleThreeOfAKind = ranks.length === 3 && ranks[1].count === 3;
|
|
@@ -698,9 +670,9 @@ class HandEvaluator {
|
|
|
698
670
|
* const evaluator = new HandEvaluator(['As', 'Kh', '2d', '2c', '2s']);
|
|
699
671
|
* evaluator.isBottomThreeOfAKind(); // Returns true (three deuces)
|
|
700
672
|
*/
|
|
701
|
-
isBottomThreeOfAKind() {
|
|
673
|
+
#isBottomThreeOfAKind() {
|
|
702
674
|
if (this.evaluation.isBottomThreeOfAKind !== undefined) return this.evaluation.isBottomThreeOfAKind;
|
|
703
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
675
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
704
676
|
if (!this.evaluation.isThreeOfAKind) return this.evaluation.isBottomThreeOfAKind = false;
|
|
705
677
|
const rankCounts = this.#getRankCounts();
|
|
706
678
|
return this.evaluation.isBottomThreeOfAKind = rankCounts[this.cards[this.cards.length - 1].slice(0, -1)] === 3;
|
|
@@ -716,9 +688,9 @@ class HandEvaluator {
|
|
|
716
688
|
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', '9s']);
|
|
717
689
|
* evaluator.isFlush(); // Returns true (all spades)
|
|
718
690
|
*/
|
|
719
|
-
isFlush() {
|
|
691
|
+
#isFlush() {
|
|
720
692
|
if (this.evaluation.isFlush !== undefined) return this.evaluation.isFlush;
|
|
721
|
-
const suits = this
|
|
693
|
+
const suits = this.#countSuits();
|
|
722
694
|
return this.evaluation.isFlush = Object.values(suits).some(count => count === 5);
|
|
723
695
|
}
|
|
724
696
|
|
|
@@ -731,11 +703,11 @@ class HandEvaluator {
|
|
|
731
703
|
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', '9h']);
|
|
732
704
|
* evaluator.isFlushDraw(); // Returns true (4 spades, 1 heart)
|
|
733
705
|
*/
|
|
734
|
-
isFlushDraw() {
|
|
706
|
+
#isFlushDraw() {
|
|
735
707
|
if (this.evaluation.isFlushDraw !== undefined) return this.evaluation.isFlushDraw;
|
|
736
|
-
if (this.evaluation.isFlush === undefined) this
|
|
708
|
+
if (this.evaluation.isFlush === undefined) this.#isFlush();
|
|
737
709
|
if (this.evaluation.isFlush) return this.evaluation.isFlushDraw = false;
|
|
738
|
-
const suits = this
|
|
710
|
+
const suits = this.#countSuits();
|
|
739
711
|
return this.evaluation.isFlushDraw = Object.values(suits).some(count => count === 4);
|
|
740
712
|
}
|
|
741
713
|
|
|
@@ -748,12 +720,12 @@ class HandEvaluator {
|
|
|
748
720
|
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Jh', '9h']);
|
|
749
721
|
* evaluator.isBackdoorFlushDraw(); // Returns true (3 spades, 2 hearts)
|
|
750
722
|
*/
|
|
751
|
-
isBackdoorFlushDraw() {
|
|
723
|
+
#isBackdoorFlushDraw() {
|
|
752
724
|
if (this.evaluation.isBackdoorFlushDraw !== undefined) return this.evaluation.isBackdoorFlushDraw;
|
|
753
|
-
if (this.evaluation.isFlush === undefined) this
|
|
754
|
-
if (this.evaluation.isFlushDraw === undefined) this
|
|
725
|
+
if (this.evaluation.isFlush === undefined) this.#isFlush();
|
|
726
|
+
if (this.evaluation.isFlushDraw === undefined) this.#isFlushDraw();
|
|
755
727
|
if (this.evaluation.isFlush || this.evaluation.isFlushDraw) return this.evaluation.isBackdoorFlushDraw = false;
|
|
756
|
-
const suits = this
|
|
728
|
+
const suits = this.#countSuits();
|
|
757
729
|
return this.evaluation.isBackdoorFlushDraw = Object.values(suits).some(count => count === 3);
|
|
758
730
|
}
|
|
759
731
|
|
|
@@ -773,18 +745,18 @@ class HandEvaluator {
|
|
|
773
745
|
* const evaluator = new HandEvaluator(['As', '2s', '3s', '4s', '5s']);
|
|
774
746
|
* evaluator.isStraight(); // Returns true (wheel: A-2-3-4-5)
|
|
775
747
|
*/
|
|
776
|
-
isStraight() {
|
|
748
|
+
#isStraight() {
|
|
777
749
|
if (this.evaluation.isStraight !== undefined) return this.evaluation.isStraight;
|
|
778
|
-
if (this.evaluation.isPair === undefined) this
|
|
779
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
780
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
781
|
-
if (this.evaluation.isFullHouse === undefined) this
|
|
782
|
-
if (this.evaluation.isFourOfAKind === undefined) this
|
|
750
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
751
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
752
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
753
|
+
if (this.evaluation.isFullHouse === undefined) this.#isFullHouse();
|
|
754
|
+
if (this.evaluation.isFourOfAKind === undefined) this.#isFourOfAKind();
|
|
783
755
|
if (this.evaluation.isPair || this.evaluation.isTwoPair || this.evaluation.isThreeOfAKind || this.evaluation.isFullHouse || this.evaluation.isFourOfAKind) return this.evaluation.isStraight = false;
|
|
784
|
-
this
|
|
785
|
-
const distance = this
|
|
756
|
+
this.#sort();
|
|
757
|
+
const distance = this.#getDistance();
|
|
786
758
|
if (distance === 4) return this.evaluation.isStraight = true;
|
|
787
|
-
|
|
759
|
+
if (this.#isStraightWheel()) return this.evaluation.isStraight = true;
|
|
788
760
|
return this.evaluation.isStraight = false;
|
|
789
761
|
}
|
|
790
762
|
|
|
@@ -797,16 +769,16 @@ class HandEvaluator {
|
|
|
797
769
|
* const evaluator = new HandEvaluator(['As', '2s', '3s', '4s', '5s']);
|
|
798
770
|
* evaluator.isStraightWheel(); // Returns true (wheel: A-2-3-4-5)
|
|
799
771
|
*/
|
|
800
|
-
isStraightWheel() {
|
|
772
|
+
#isStraightWheel() {
|
|
801
773
|
if (this.evaluation.isStraightWheel !== undefined) return this.evaluation.isStraightWheel;
|
|
802
|
-
if (this.evaluation.isPair === undefined) this
|
|
803
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
804
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
805
|
-
if (this.evaluation.isFullHouse === undefined) this
|
|
806
|
-
if (this.evaluation.isFourOfAKind === undefined) this
|
|
774
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
775
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
776
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
777
|
+
if (this.evaluation.isFullHouse === undefined) this.#isFullHouse();
|
|
778
|
+
if (this.evaluation.isFourOfAKind === undefined) this.#isFourOfAKind();
|
|
807
779
|
if (this.evaluation.isPair || this.evaluation.isTwoPair || this.evaluation.isThreeOfAKind || this.evaluation.isFullHouse || this.evaluation.isFourOfAKind) return this.evaluation.isStraightWheel = false;
|
|
808
|
-
this
|
|
809
|
-
const distance = this
|
|
780
|
+
this.#sortWheel();
|
|
781
|
+
const distance = this.#getDistanceWheel();
|
|
810
782
|
return this.evaluation.isStraightWheel = distance === 4;
|
|
811
783
|
}
|
|
812
784
|
|
|
@@ -819,10 +791,10 @@ class HandEvaluator {
|
|
|
819
791
|
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', 'Ts']);
|
|
820
792
|
* evaluator.isRoyalFlush(); // Returns true
|
|
821
793
|
*/
|
|
822
|
-
isRoyalFlush() {
|
|
794
|
+
#isRoyalFlush() {
|
|
823
795
|
if (this.evaluation.isRoyalFlush !== undefined) return this.evaluation.isRoyalFlush;
|
|
824
|
-
if (this.evaluation.isFlush === undefined) this
|
|
825
|
-
if (this.evaluation.isStraight === undefined) this
|
|
796
|
+
if (this.evaluation.isFlush === undefined) this.#isFlush();
|
|
797
|
+
if (this.evaluation.isStraight === undefined) this.#isStraight();
|
|
826
798
|
if (!this.evaluation.isFlush || !this.evaluation.isStraight) return this.evaluation.isRoyalFlush = false;
|
|
827
799
|
const ranks = new Set(this.cards.map(c => c.slice(0, -1)));
|
|
828
800
|
return this.evaluation.isRoyalFlush = ranks.has('A') && ranks.has('K') && ranks.has('Q') && ranks.has('J') && ranks.has('T');
|
|
@@ -837,11 +809,11 @@ class HandEvaluator {
|
|
|
837
809
|
* const evaluator = new HandEvaluator(['9s', '8s', '7s', '6s', '5s']);
|
|
838
810
|
* evaluator.isStraightFlush(); // Returns true
|
|
839
811
|
*/
|
|
840
|
-
isStraightFlush() {
|
|
812
|
+
#isStraightFlush() {
|
|
841
813
|
if (this.evaluation.isStraightFlush !== undefined) return this.evaluation.isStraightFlush;
|
|
842
|
-
if (this.evaluation.isFlush === undefined) this
|
|
843
|
-
if (this.evaluation.isStraight === undefined) this
|
|
844
|
-
if (this.evaluation.isRoyalFlush === undefined) this
|
|
814
|
+
if (this.evaluation.isFlush === undefined) this.#isFlush();
|
|
815
|
+
if (this.evaluation.isStraight === undefined) this.#isStraight();
|
|
816
|
+
if (this.evaluation.isRoyalFlush === undefined) this.#isRoyalFlush();
|
|
845
817
|
return this.evaluation.isStraightFlush = this.evaluation.isFlush && this.evaluation.isStraight && !this.evaluation.isRoyalFlush;
|
|
846
818
|
}
|
|
847
819
|
|
|
@@ -857,24 +829,24 @@ class HandEvaluator {
|
|
|
857
829
|
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', '9h']);
|
|
858
830
|
* evaluator.isOpenEndedStraightDraw(); // Returns true (needs T or 9 to complete straight)
|
|
859
831
|
*/
|
|
860
|
-
isOpenEndedStraightDraw() {
|
|
832
|
+
#isOpenEndedStraightDraw() {
|
|
861
833
|
if (this.evaluation.isOpenEndedStraightDraw !== undefined) return this.evaluation.isOpenEndedStraightDraw;
|
|
862
|
-
if (this.evaluation.isPair === undefined) this
|
|
863
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
864
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
865
|
-
if (this.evaluation.isFullHouse === undefined) this
|
|
866
|
-
if (this.evaluation.isFourOfAKind === undefined) this
|
|
867
|
-
if (this.evaluation.isStraight === undefined) this
|
|
834
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
835
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
836
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
837
|
+
if (this.evaluation.isFullHouse === undefined) this.#isFullHouse();
|
|
838
|
+
if (this.evaluation.isFourOfAKind === undefined) this.#isFourOfAKind();
|
|
839
|
+
if (this.evaluation.isStraight === undefined) this.#isStraight();
|
|
868
840
|
if (this.evaluation.isPair || this.evaluation.isTwoPair || this.evaluation.isThreeOfAKind || this.evaluation.isFullHouse || this.evaluation.isFourOfAKind || this.evaluation.isStraight) {
|
|
869
841
|
// Check wheel equivalent before returning false
|
|
870
|
-
if (this
|
|
842
|
+
if (this.#isOpenEndedStraightDrawWheel()) return this.evaluation.isOpenEndedStraightDraw = true;
|
|
871
843
|
return this.evaluation.isOpenEndedStraightDraw = false;
|
|
872
844
|
}
|
|
873
|
-
this
|
|
874
|
-
const result = this
|
|
845
|
+
this.#sort();
|
|
846
|
+
const result = this.#getDistance(this.cards.slice(0,4)) === 3 || this.#getDistance(this.cards.slice(1,5)) === 3;
|
|
875
847
|
if (result) return this.evaluation.isOpenEndedStraightDraw = true;
|
|
876
848
|
// Check wheel equivalent
|
|
877
|
-
if (this
|
|
849
|
+
if (this.#isOpenEndedStraightDrawWheel()) return this.evaluation.isOpenEndedStraightDraw = true;
|
|
878
850
|
return this.evaluation.isOpenEndedStraightDraw = false;
|
|
879
851
|
}
|
|
880
852
|
|
|
@@ -885,17 +857,17 @@ class HandEvaluator {
|
|
|
885
857
|
*
|
|
886
858
|
* @returns {boolean} True if the hand is a wheel-equivalent open-ended straight draw, false otherwise
|
|
887
859
|
*/
|
|
888
|
-
isOpenEndedStraightDrawWheel() {
|
|
860
|
+
#isOpenEndedStraightDrawWheel() {
|
|
889
861
|
if (this.evaluation.isOpenEndedStraightDrawWheel !== undefined) return this.evaluation.isOpenEndedStraightDrawWheel;
|
|
890
|
-
if (this.evaluation.isPair === undefined) this
|
|
891
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
892
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
893
|
-
if (this.evaluation.isFullHouse === undefined) this
|
|
894
|
-
if (this.evaluation.isFourOfAKind === undefined) this
|
|
895
|
-
if (this.evaluation.isStraightWheel === undefined) this
|
|
862
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
863
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
864
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
865
|
+
if (this.evaluation.isFullHouse === undefined) this.#isFullHouse();
|
|
866
|
+
if (this.evaluation.isFourOfAKind === undefined) this.#isFourOfAKind();
|
|
867
|
+
if (this.evaluation.isStraightWheel === undefined) this.#isStraightWheel();
|
|
896
868
|
if (this.evaluation.isPair || this.evaluation.isTwoPair || this.evaluation.isThreeOfAKind || this.evaluation.isFullHouse || this.evaluation.isFourOfAKind || this.evaluation.isStraightWheel) return this.evaluation.isOpenEndedStraightDrawWheel = false;
|
|
897
|
-
this
|
|
898
|
-
return this.evaluation.isOpenEndedStraightDrawWheel = this
|
|
869
|
+
this.#sortWheel();
|
|
870
|
+
return this.evaluation.isOpenEndedStraightDrawWheel = this.#getDistanceWheel(this.cards.slice(0,4)) === 3 || this.#getDistanceWheel(this.cards.slice(1,5)) === 3;
|
|
899
871
|
}
|
|
900
872
|
|
|
901
873
|
/**
|
|
@@ -908,25 +880,25 @@ class HandEvaluator {
|
|
|
908
880
|
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', '9h']);
|
|
909
881
|
* evaluator.isInsideStraightDraw(); // Returns true (needs T in the middle)
|
|
910
882
|
*/
|
|
911
|
-
isInsideStraightDraw() {
|
|
883
|
+
#isInsideStraightDraw() {
|
|
912
884
|
if (this.evaluation.isInsideStraightDraw !== undefined) return this.evaluation.isInsideStraightDraw;
|
|
913
|
-
if (this.evaluation.isPair === undefined) this
|
|
914
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
915
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
916
|
-
if (this.evaluation.isFullHouse === undefined) this
|
|
917
|
-
if (this.evaluation.isFourOfAKind === undefined) this
|
|
918
|
-
if (this.evaluation.isStraight === undefined) this
|
|
885
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
886
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
887
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
888
|
+
if (this.evaluation.isFullHouse === undefined) this.#isFullHouse();
|
|
889
|
+
if (this.evaluation.isFourOfAKind === undefined) this.#isFourOfAKind();
|
|
890
|
+
if (this.evaluation.isStraight === undefined) this.#isStraight();
|
|
919
891
|
if (this.evaluation.isPair || this.evaluation.isTwoPair || this.evaluation.isThreeOfAKind || this.evaluation.isFullHouse || this.evaluation.isFourOfAKind || this.evaluation.isStraight) {
|
|
920
892
|
// Check wheel equivalent before returning false
|
|
921
|
-
if (this
|
|
893
|
+
if (this.#isInsideStraightDrawWheel()) return this.evaluation.isInsideStraightDraw = true;
|
|
922
894
|
return this.evaluation.isInsideStraightDraw = false;
|
|
923
895
|
}
|
|
924
896
|
|
|
925
|
-
this
|
|
926
|
-
const result = this
|
|
897
|
+
this.#sort();
|
|
898
|
+
const result = this.#getDistance(this.cards.slice(0,4)) === 4 || this.#getDistance(this.cards.slice(1,5)) === 4;
|
|
927
899
|
if (result) return this.evaluation.isInsideStraightDraw = true;
|
|
928
900
|
// Check wheel equivalent
|
|
929
|
-
if (this
|
|
901
|
+
if (this.#isInsideStraightDrawWheel()) return this.evaluation.isInsideStraightDraw = true;
|
|
930
902
|
return this.evaluation.isInsideStraightDraw = false;
|
|
931
903
|
}
|
|
932
904
|
|
|
@@ -937,18 +909,18 @@ class HandEvaluator {
|
|
|
937
909
|
*
|
|
938
910
|
* @returns {boolean} True if the hand is a wheel-equivalent inside straight draw, false otherwise
|
|
939
911
|
*/
|
|
940
|
-
isInsideStraightDrawWheel() {
|
|
912
|
+
#isInsideStraightDrawWheel() {
|
|
941
913
|
if (this.evaluation.isInsideStraightDrawWheel !== undefined) return this.evaluation.isInsideStraightDrawWheel;
|
|
942
|
-
if (this.evaluation.isPair === undefined) this
|
|
943
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
944
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
945
|
-
if (this.evaluation.isFullHouse === undefined) this
|
|
946
|
-
if (this.evaluation.isFourOfAKind === undefined) this
|
|
947
|
-
if (this.evaluation.isStraightWheel === undefined) this
|
|
914
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
915
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
916
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
917
|
+
if (this.evaluation.isFullHouse === undefined) this.#isFullHouse();
|
|
918
|
+
if (this.evaluation.isFourOfAKind === undefined) this.#isFourOfAKind();
|
|
919
|
+
if (this.evaluation.isStraightWheel === undefined) this.#isStraightWheel();
|
|
948
920
|
if (this.evaluation.isPair || this.evaluation.isTwoPair || this.evaluation.isThreeOfAKind || this.evaluation.isFullHouse || this.evaluation.isFourOfAKind || this.evaluation.isStraightWheel) return this.evaluation.isInsideStraightDrawWheel = false;
|
|
949
921
|
|
|
950
|
-
this
|
|
951
|
-
return this.evaluation.isInsideStraightDrawWheel = this
|
|
922
|
+
this.#sortWheel();
|
|
923
|
+
return this.evaluation.isInsideStraightDrawWheel = this.#getDistanceWheel(this.cards.slice(0,4)) === 4 || this.#getDistanceWheel(this.cards.slice(1,5)) === 4;
|
|
952
924
|
}
|
|
953
925
|
|
|
954
926
|
/**
|
|
@@ -964,10 +936,10 @@ class HandEvaluator {
|
|
|
964
936
|
* const evaluator = new HandEvaluator(['As', 'Ks', 'Qs', 'Js', '8h']);
|
|
965
937
|
* evaluator.isStraightDraw(); // Returns true (inside straight draw)
|
|
966
938
|
*/
|
|
967
|
-
isStraightDraw() {
|
|
939
|
+
#isStraightDraw() {
|
|
968
940
|
if (this.evaluation.isStraightDraw !== undefined) return this.evaluation.isStraightDraw;
|
|
969
|
-
if (this.evaluation.isOpenEndedStraightDraw === undefined) this
|
|
970
|
-
if (this.evaluation.isInsideStraightDraw === undefined) this
|
|
941
|
+
if (this.evaluation.isOpenEndedStraightDraw === undefined) this.#isOpenEndedStraightDraw();
|
|
942
|
+
if (this.evaluation.isInsideStraightDraw === undefined) this.#isInsideStraightDraw();
|
|
971
943
|
return this.evaluation.isStraightDraw = this.evaluation.isOpenEndedStraightDraw || this.evaluation.isInsideStraightDraw;
|
|
972
944
|
}
|
|
973
945
|
|
|
@@ -982,14 +954,14 @@ class HandEvaluator {
|
|
|
982
954
|
*/
|
|
983
955
|
#getStraightCards() {
|
|
984
956
|
// Try standard straight first
|
|
985
|
-
this
|
|
957
|
+
this.#sort();
|
|
986
958
|
const gaps = this.#getGaps();
|
|
987
959
|
if (gaps.every(g => g === 1)) {
|
|
988
960
|
return [...this.cards].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
989
961
|
}
|
|
990
962
|
|
|
991
963
|
// Try wheel straight
|
|
992
|
-
this
|
|
964
|
+
this.#sortWheel();
|
|
993
965
|
const gapsWheel = this.#getGapsWheel();
|
|
994
966
|
if (gapsWheel.every(g => g === 1)) {
|
|
995
967
|
// Convert back to standard sorting for return
|
|
@@ -999,6 +971,390 @@ class HandEvaluator {
|
|
|
999
971
|
return [];
|
|
1000
972
|
}
|
|
1001
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
|
+
|
|
1002
1358
|
/**
|
|
1003
1359
|
* Helper method to extract the 4 cards that form a straight draw.
|
|
1004
1360
|
* Handles both standard and wheel straight draws.
|
|
@@ -1008,29 +1364,29 @@ class HandEvaluator {
|
|
|
1008
1364
|
*/
|
|
1009
1365
|
#getStraightDrawCards() {
|
|
1010
1366
|
// Try standard straight draw
|
|
1011
|
-
this
|
|
1367
|
+
this.#sort();
|
|
1012
1368
|
const firstFour = this.cards.slice(0, 4);
|
|
1013
1369
|
const lastFour = this.cards.slice(1, 5);
|
|
1014
1370
|
|
|
1015
|
-
const firstFourDist = this
|
|
1371
|
+
const firstFourDist = this.#getDistance(firstFour);
|
|
1016
1372
|
if (firstFourDist === 3 || firstFourDist === 4) {
|
|
1017
1373
|
return [...firstFour].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1018
1374
|
}
|
|
1019
|
-
const lastFourDist = this
|
|
1375
|
+
const lastFourDist = this.#getDistance(lastFour);
|
|
1020
1376
|
if (lastFourDist === 3 || lastFourDist === 4) {
|
|
1021
1377
|
return [...lastFour].sort((a, b) => this.#getRankValue(b) - this.#getRankValue(a));
|
|
1022
1378
|
}
|
|
1023
1379
|
|
|
1024
1380
|
// Try wheel straight draw
|
|
1025
|
-
this
|
|
1381
|
+
this.#sortWheel();
|
|
1026
1382
|
const firstFourWheel = this.cards.slice(0, 4);
|
|
1027
1383
|
const lastFourWheel = this.cards.slice(1, 5);
|
|
1028
1384
|
|
|
1029
|
-
const firstFourDistWheel = this
|
|
1385
|
+
const firstFourDistWheel = this.#getDistanceWheel(firstFourWheel);
|
|
1030
1386
|
if (firstFourDistWheel === 3 || firstFourDistWheel === 4) {
|
|
1031
1387
|
return [...firstFourWheel].sort((a, b) => this.#getRankValueWheel(b) - this.#getRankValueWheel(a));
|
|
1032
1388
|
}
|
|
1033
|
-
const lastFourDistWheel = this
|
|
1389
|
+
const lastFourDistWheel = this.#getDistanceWheel(lastFourWheel);
|
|
1034
1390
|
if (lastFourDistWheel === 3 || lastFourDistWheel === 4) {
|
|
1035
1391
|
return [...lastFourWheel].sort((a, b) => this.#getRankValueWheel(b) - this.#getRankValueWheel(a));
|
|
1036
1392
|
}
|
|
@@ -1038,107 +1394,6 @@ class HandEvaluator {
|
|
|
1038
1394
|
return [];
|
|
1039
1395
|
}
|
|
1040
1396
|
|
|
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
|
-
|
|
1142
1397
|
// ==================== HIGH CARD ====================
|
|
1143
1398
|
|
|
1144
1399
|
/**
|
|
@@ -1150,15 +1405,15 @@ class HandEvaluator {
|
|
|
1150
1405
|
* const evaluator = new HandEvaluator(['As', 'Kh', 'Qd', 'Jc', '9s']);
|
|
1151
1406
|
* evaluator.isHighCard(); // Returns true (no made hand)
|
|
1152
1407
|
*/
|
|
1153
|
-
isHighCard() {
|
|
1408
|
+
#isHighCard() {
|
|
1154
1409
|
if (this.evaluation.isHighCard !== undefined) return this.evaluation.isHighCard;
|
|
1155
|
-
if (this.evaluation.isPair === undefined) this
|
|
1156
|
-
if (this.evaluation.isTwoPair === undefined) this
|
|
1157
|
-
if (this.evaluation.isThreeOfAKind === undefined) this
|
|
1158
|
-
if (this.evaluation.isFullHouse === undefined) this
|
|
1159
|
-
if (this.evaluation.isFourOfAKind === undefined) this
|
|
1160
|
-
if (this.evaluation.isFlush === undefined) this
|
|
1161
|
-
if (this.evaluation.isStraight === undefined) this
|
|
1410
|
+
if (this.evaluation.isPair === undefined) this.#isPair();
|
|
1411
|
+
if (this.evaluation.isTwoPair === undefined) this.#isTwoPair();
|
|
1412
|
+
if (this.evaluation.isThreeOfAKind === undefined) this.#isThreeOfAKind();
|
|
1413
|
+
if (this.evaluation.isFullHouse === undefined) this.#isFullHouse();
|
|
1414
|
+
if (this.evaluation.isFourOfAKind === undefined) this.#isFourOfAKind();
|
|
1415
|
+
if (this.evaluation.isFlush === undefined) this.#isFlush();
|
|
1416
|
+
if (this.evaluation.isStraight === undefined) this.#isStraight();
|
|
1162
1417
|
return this.evaluation.isHighCard = !this.evaluation.isPair && !this.evaluation.isTwoPair && !this.evaluation.isThreeOfAKind && !this.evaluation.isFullHouse && !this.evaluation.isFourOfAKind && !this.evaluation.isFlush && !this.evaluation.isStraight;
|
|
1163
1418
|
}
|
|
1164
1419
|
}
|