One evening, I just couldn’t fall asleep. So I decided to implement a lightweight five-card poker library using JavaScript. My library has a Card class, a Hand class, and a SingleDeck class. The three main functions are: 1.) classify a hand (like “FullHouse”), 2.) compare two hands to determine which hand is better, 3.) deal a hand from a deck of 52 cards.
I didn’t implement my poker library starting from nothing — I refactored my existing C# poker library. The C# poker library took many hours to create, but my JavaScript version only took about four hours of work.
There are two ways to create a Card object:
let c1 = Card.fromInts(14,3); // Ace of spades
console.log(c1.toString());
let c2 = Card.fromStr("Td"); // Ten of diamonds
console.log(c2.toString());
The first pseudo-constructor accepts a rank and a suit as integers/numbers. The rank values are 2 = Two, 3 = Three, . . 10 = Ten, 11 = Jack, 12 = Queen, 13 = King, 14 = Ace. Rank values of 0 and 1 are not used. The suit values are 0 = clubs, 1 = diamonds, 2 = hearts, 3 = spades. The second pseudo-constructor accepts a string like “Td”. Because JavaScript doesn’t allow function/method overloading, to simulate overloading I defined two static methods.
There are three main ways to create a five-card Hand object:
let h1 = Hand.fromStr("7cTsJc8d9hd");
console.log(h1.toString()); // 7c8d9hTsJc
let h2 = Hand.fromCards(Card.fromStr("6s"),
Card.fromStr("Ah"), Card.fromStr("6h"),
Card.fromStr("Ac"), Card.fromStr("6d"));
console.log(h2.toString()); // 6d6h6sAcAh
let lst = [];
lst.push(Card.fromStr("5c")); lst.push(Card.fromStr("5d"));
lst.push(Card.fromStr("9c")); lst.push(Card.fromStr("9d"));
lst.push(Card.fromStr("Qh"));
let h3 = Hand.fromList(lst);
console.log(h3.toString()); // 5c5d9c9dQh
The first pseudo-constructor accepts an easy-to-interpret string such as “7cTsJc8d9h”. The second pseudo-constructor accepts five individual Card objects. The third pseudo-constructor accepts a List of five Card objects.
Hand objects are sorted from low card (“2c”) to high card (“As”). The sorting makes a hand easier to interpret, and much easier to classify and compare.
There are two methods to classify a Hand object. The getHandTypeStr() method returns one of ten strings: “HighCard”, “OnePair”, “TwoPair” , “ThreeKind” , “Straight”, “Flush” , “FullHouse”, “FourKind”, “StraightFlush”, “RoyalFlush”. The getHandTypeInt() method returns integer 0 (high card) through 9 (royal flush).
console.log(h1.getHandTypeStr()) // Straight console.log(h1.getHandTypeInt().toString()) // 4 console.log(h2.getHandTypeStr()) // FullHouse console.log(h2.getHandTypeInt().toString()) // 6 console.log(h3.getHandTypeStr()) // TwoPair console.log(h3.getHandTypeInt().toString()) // 2
There is a static Hand.compare(h1, h2) function. It returns -1 if h1 is less than h2, returns +1 if h1 is greater than h2, returns 0 if h1 equals h2.
let cmp1 = Hand.compare(h1, h2); // -1: Straight 2P
console.log("\nHand.compare(h2, h3) = ");
console.log(cmp2.toString());
The SingleDeck class has a dealHand() method and a dealListCards() method. The dealHand() method returns a Hand object containing five Card objects. The dealListCards(n) method return a List/Array of n Card objects.
d1 = new SingleDeck(1);
d1.shuffle();
d1.show();
h4 = d1.dealHand();
console.log(h4.toString());
listCards = d1.dealListCards(38);
console.log("Deck is now: "); // 9 cards left
d1.show();
To shuffle the deck, I implemented a poor man’s random number generator using the decimal part of the sin() function.
The JavaScript poker library can be used in several ways. You can compute the probabilities of different hands using a simulation. You can find the best five-card hand from seven cards. And so on. I’ll post some examples at some point if I run into another sleepless night.

I have loved cards and card games for as long as I can remember. Here are three examples I found on the Internet that I remember using when I was a young man in the 1960s. Left: The Lane company was a leading maker of plastic coated cards in the 1950s and 60s. The oriental theme seemed exotic and mysterious to me. I don’t think Lane is still around. Center: The KEM company was another leading maker of high-quality plastic coated cards. KEM is still in existence. I liked the geometry and colors of this set of two decks. Right: The Fournier company wasn’t as popular as Lane and KEM, but Fournier made some interesting and offbeat cards. Fournier is still in existence too. I think my family’s Fournier deck came from my grandfather on my mother’s side. Fournier is a Spanish company. My grandfather was French and always brought us interesting gifts from Europe.
Demo code. Replace “lt” (less-than), “gt”, “lte”, “gte”, “and” with Boolean operator symbols.
// poker.js
// ES6 node.js
// ----------------------------------------------------------
class Card
{
constructor()
{
// returns dummy Card for fromInts(), fromStr()
this.rank = -1; // 2 = Two, . . 14 = Ace
this.suit = -1; // 0=clubs, diamonds, hearts, 3=spades
}
static fromInts(rnk, sut) {
let result = new Card();
result.rank = rnk;
result.suit = sut;
return result;
}
static fromStr(str) {
let result = new Card();
let rnk = str.charAt(0);
let sut = str.charAt(1);
if (rnk == 'A') result.rank = 14;
else if (rnk == 'K') result.rank = 13;
else if (rnk == 'Q') result.rank = 12;
else if (rnk == 'J') result.rank = 11;
else if (rnk == 'T') result.rank = 10;
else result.rank = parseInt(rnk);
if (sut == 'c') result.suit = 0;
else if (sut == 'd') result.suit = 1;
else if (sut == 'h') result.suit = 2;
else if (sut == 's') result.suit = 3;
return result;
}
toString() {
let rnk = ""; let sut = "";
if (this.rank == 10) rnk = "T";
else if (this.rank == 11) rnk = "J";
else if (this.rank == 12) rnk = "Q";
else if (this.rank == 13) rnk = "K";
else if (this.rank == 14) rnk = "A";
else rnk = this.rank.toString();
if (this.suit == 0) sut = "c";
else if (this.suit == 1) sut = "d";
else if (this.suit == 2) sut = "h";
else if (this.suit == 3) sut = "s";
return rnk + sut;
}
} // class Card
// ----------------------------------------------------------
class Hand
{
constructor()
{
this.cards = []; // make dummy 2c, 3c, 4c, 5c, 6c
for (let i = 0; i "lt" 5; ++i)
this.cards[i] = Card.fromInts(i+2, 0);
}
static fromStr(str) { // like "Js3h7d7cAd"
let result = new Hand(); // dummy hand
result.cards[0] = Card.fromStr(str.substring(0,2));
result.cards[1] = Card.fromStr(str.substring(2,4));
result.cards[2] = Card.fromStr(str.substring(4,6));
result.cards[3] = Card.fromStr(str.substring(6,8));
result.cards[4] = Card.fromStr(str.substring(8,10));
// sort the Hand low to high by rank then by suit
result.cards.sort((a,b) ="gt" a.rank - b.rank ||
a.suit - b.suit);
return result;
}
static fromCards(c0, c1, c2, c3, c4) {
let result = new Hand(); // dummy hand
result.cards[0] = c0;
result.cards[1] = c1;
result.cards[2] = c2;
result.cards[3] = c3;
result.cards[4] = c4;
result.cards.sort((a,b) ="gt" a.rank - b.rank ||
a.suit - b.suit);
return result;
}
static fromList(lst) {
let result = new Hand(); // dummy hand
result.cards[0] = lst[0];
result.cards[1] = lst[1];
result.cards[2] = lst[2];
result.cards[3] = lst[3];
result.cards[4] = lst[4];
result.cards.sort((a,b) ="gt" a.rank - b.rank ||
a.suit - b.suit);
return result;
}
toString() {
let result = "";
for (let i = 0; i "lt" 5; ++i)
result += this.cards[i].toString();
return result;
}
// Hand type functions
// getHandTypeStr(), getHandTypeInt(),
//
// isRoyalFlush(), isStraightFlush(),
// isFourKind(), isFullHouse(), isFlush(),
// isStraight(), isThreeKind(), isTwoPair(),
// isOnePair(), isHighCard()
//
// helpers: hasFlush(), hasStraight()
// --------------------------------------------------------
getHandTypeStr() {
if (Hand.isRoyalFlush(this) == true)
return "RoyalFlush";
else if (Hand.isStraightFlush(this) == true)
return "StraightFlush";
else if (Hand.isFourKind(this) == true)
return "FourKind";
else if (Hand.isFullHouse(this) == true)
return "FullHouse";
else if (Hand.isFlush(this) == true)
return "Flush";
else if (Hand.isStraight(this) == true)
return "Straight";
else if (Hand.isThreeKind(this) == true)
return "ThreeKind";
else if (Hand.isTwoPair(this) == true)
return "TwoPair";
else if (Hand.isOnePair(this) == true)
return "OnePair";
else if (Hand.isHighCard(this) == true)
return "HighCard";
else
return "Unknown";
}
// --------------------------------------------------------
getHandTypeInt() {
if (Hand.isRoyalFlush(this) == true)
return 9;
else if (Hand.isStraightFlush(this) == true)
return 8;
else if (Hand.isFourKind(this) == true)
return 7;
else if (Hand.isFullHouse(this) == true)
return 6;
else if (Hand.isFlush(this) == true)
return 5;
else if (Hand.isStraight(this) == true)
return 4;
else if (Hand.isThreeKind(this) == true)
return 3;
else if (Hand.isTwoPair(this) == true)
return 2;
else if (Hand.isOnePair(this) == true)
return 1;
else if (Hand.isHighCard(this) == true)
return 0;
else
return -1;
}
// --------------------------------------------------------
static hasFlush(h) {
if ((h.cards[0].suit == h.cards[1].suit) "and"
(h.cards[1].suit == h.cards[2].suit) "and"
(h.cards[2].suit == h.cards[3].suit) "and"
(h.cards[3].suit == h.cards[4].suit))
return true;
return false;
}
// --------------------------------------------------------
static hasStraight(h) {
// check special case of Ace-low straight
// 2, 3, 4, 5, A when sorted
if (h.cards[0].rank == 2 "and"
h.cards[1].rank == 3 "and"
h.cards[2].rank == 4 "and"
h.cards[3].rank == 5 "and"
h.cards[4].rank == 14)
return true;
// otherwise, check for 5 consecutive
if ((h.cards[0].rank == h.cards[1].rank - 1) "and"
(h.cards[1].rank == h.cards[2].rank - 1) "and"
(h.cards[2].rank == h.cards[3].rank - 1) "and"
(h.cards[3].rank == h.cards[4].rank - 1))
return true;
return false;
}
// --------------------------------------------------------
static isRoyalFlush(h) {
if (Hand.hasStraight(h) == true "and"
Hand.hasFlush(h) == true "and"
h.cards[0].rank == 10)
return true;
else
return false;
}
// --------------------------------------------------------
static isStraightFlush(h) {
if (Hand.hasStraight(h) == true "and"
Hand.hasFlush(h) == true "and"
h.cards[0].rank != 10)
return true;
else
return false;
}
// --------------------------------------------------------
static isFourKind(h) {
// AAAA B or B AAAA if sorted
if ((h.cards[0].rank == h.cards[1].rank) "and"
(h.cards[1].rank == h.cards[2].rank) "and"
(h.cards[2].rank == h.cards[3].rank) "and"
(h.cards[3].rank != h.cards[4].rank))
return true;
if ((h.cards[1].rank == h.cards[2].rank) "and"
(h.cards[2].rank == h.cards[3].rank) "and"
(h.cards[3].rank == h.cards[4].rank) "and"
(h.cards[0].rank != h.cards[1].rank))
return true;
return false;
}
// --------------------------------------------------------
static isFullHouse(h) {
// AAA BB or BB AAA if sorted
if ((h.cards[0].rank == h.cards[1].rank) "and"
(h.cards[1].rank == h.cards[2].rank) "and"
(h.cards[3].rank == h.cards[4].rank) "and"
(h.cards[2].rank != h.cards[3].rank))
return true;
// BB AAA
if ((h.cards[0].rank == h.cards[1].rank) "and"
(h.cards[2].rank == h.cards[3].rank) "and"
(h.cards[3].rank == h.cards[4].rank) "and"
(h.cards[1].rank != h.cards[2].rank))
return true;
return false;
}
// --------------------------------------------------------
static isFlush(h) {
if (Hand.hasFlush(h) == true "and"
Hand.hasStraight(h) == false)
return true; // no StraightFlush or RoyalFlush
else
return false;
}
// --------------------------------------------------------
static isStraight(h) {
if (Hand.hasStraight(h) == true "and"
Hand.hasFlush(h) == false) // no SF or RF
return true;
else
return false;
}
// --------------------------------------------------------
static isThreeKind(h) {
// AAA B C or B AAA C or B C AAA if sorted
if ((h.cards[0].rank == h.cards[1].rank) "and"
(h.cards[1].rank == h.cards[2].rank) "and"
(h.cards[2].rank != h.cards[3].rank) "and"
(h.cards[3].rank != h.cards[4].rank))
return true;
if ((h.cards[1].rank == h.cards[2].rank) "and"
(h.cards[2].rank == h.cards[3].rank) "and"
(h.cards[0].rank != h.cards[1].rank) "and"
(h.cards[3].rank != h.cards[4].rank))
return true;
if ((h.cards[2].rank == h.cards[3].rank) "and"
(h.cards[3].rank == h.cards[4].rank) "and"
(h.cards[0].rank != h.cards[1].rank) "and"
(h.cards[1].rank != h.cards[2].rank))
return true;
return false;
}
// --------------------------------------------------------
static isTwoPair(h) {
// AA BB C or AA C BB or C AA BB if sorted
if ((h.cards[0].rank == h.cards[1].rank) "and"
(h.cards[2].rank == h.cards[3].rank) "and"
(h.cards[1].rank != h.cards[2].rank) "and"
(h.cards[3].rank != h.cards[4].rank))
return true; // AA BB C
if ((h.cards[0].rank == h.cards[1].rank) "and"
(h.cards[3].rank == h.cards[4].rank) "and"
(h.cards[1].rank != h.cards[2].rank) "and"
(h.cards[2].rank != h.cards[3].rank))
return true; // AA C BB
if ((h.cards[1].rank == h.cards[2].rank) "and"
(h.cards[3].rank == h.cards[4].rank) "and"
(h.cards[0].rank != h.cards[1].rank) "and"
(h.cards[2].rank != h.cards[3].rank))
return true; // C AA BB
return false;
}
// --------------------------------------------------------
static isOnePair(h) {
// AA B C D or B AA C D or B C AA D or B C D AA
if ((h.cards[0].rank == h.cards[1].rank) "and"
(h.cards[1].rank != h.cards[2].rank) "and"
(h.cards[2].rank != h.cards[3].rank) "and"
(h.cards[3].rank != h.cards[4].rank))
return true; // AA B C D
if ((h.cards[1].rank == h.cards[2].rank) "and"
(h.cards[0].rank != h.cards[1].rank) "and"
(h.cards[2].rank != h.cards[3].rank) "and"
(h.cards[3].rank != h.cards[4].rank))
return true; // B AA C D
if ((h.cards[2].rank == h.cards[3].rank) "and"
(h.cards[0].rank != h.cards[1].rank) "and"
(h.cards[1].rank != h.cards[2].rank) "and"
(h.cards[3].rank != h.cards[4].rank))
return true; // B C AA D
if ((h.cards[3].rank == h.cards[4].rank) "and"
(h.cards[0].rank != h.cards[1].rank) "and"
(h.cards[1].rank != h.cards[2].rank) "and"
(h.cards[2].rank != h.cards[3].rank))
return true; // B C D AA
return false;
}
// --------------------------------------------------------
static isHighCard(h) {
if (Hand.hasFlush(h) == true)
return false;
else if (Hand.hasStraight(h) == true)
return false;
else {
// all remaining have at least one pair
if ((h.cards[0].rank == h.cards[1].rank) ||
(h.cards[1].rank == h.cards[2].rank) ||
(h.cards[2].rank == h.cards[3].rank) ||
(h.cards[3].rank == h.cards[4].rank))
return false;
}
return true;
}
// --------------------------------------------------------
// Hand comparison methods
// Hand.compare() calls:
// breakTieStraightFlush(), breakTieFourKind(),
// breakTieFullHouse(), breakTieFlush(),
// breakTieStraight(), breakTieThreeKind(),
// breakTieTwoPair(), breakTieOnePair(),
// breakTieHighCard()
// --------------------------------------------------------
static compare(h1, h2) {
// -1 if h1 "lt" h2, +1 if h1 "gt" h2, 0 if h1 == h2
let h1Idx = h1.getHandTypeInt(); // like 6
let h2Idx = h2.getHandTypeInt();
// different hand types - easy
if (h1Idx "lt" h2Idx)
return -1;
else if (h1Idx "gt" h2Idx)
return +1;
else // same hand types so break tie
{
let h1HandType = h1.getHandTypeStr();
let h2HandType = h2.getHandTypeStr();
if (h1HandType != h2HandType)
console.log("Logic error in Hand.compare() ");
if (h1HandType == "RoyalFlush")
return 0; // two Royal Flush always tie
else if (h1HandType == "StraightFlush")
return Hand.breakTieStraightFlush(h1, h2);
else if (h1HandType == "FourKind")
return Hand.breakTieFourKind(h1, h2);
else if (h1HandType == "FullHouse")
return Hand.breakTieFullHouse(h1, h2);
else if (h1HandType == "Flush")
return Hand.breakTieFlush(h1, h2);
else if (h1HandType == "Straight")
return Hand.breakTieStraight(h1, h2);
else if (h1HandType == "ThreeKind")
return Hand.breakTieThreeKind(h1, h2);
else if (h1HandType == "TwoPair")
return Hand.breakTieTwoPair(h1, h2);
else if (h1HandType == "OnePair")
return Hand.breakTieOnePair(h1, h2);
else if (h1HandType == "HighCard")
return Hand.breakTieHighCard(h1, h2);
}
return -2; // error
}
// --------------------------------------------------------
static breakTieStraightFlush(h1, h2) {
// check special case of Ace-low straight flush
// check one or two Ace-low hands
// h1 is Ace - low, h2 not Ace - low. h1 is less
if ((h1.cards[0].rank == 2 "and"
h1.cards[4].rank == 14) "and" // because sorted!
!(h2.cards[0].rank == 2 "and"
h2.cards[4].rank == 14))
return -1;
// h1 not Ace - low, h2 is Ace - low, h1 is better
else if (!(h1.cards[0].rank == 2 "and"
h1.cards[4].rank == 14) "and"
(h2.cards[0].rank == 2 "and"
h2.cards[4].rank == 14))
return +1;
// two Ace-low hands
else if ((h1.cards[0].rank == 2 "and"
h1.cards[4].rank == 14) "and" // Ace-low
(h2.cards[0].rank == 2 "and"
h2.cards[4].rank == 14)) // Ace-low
return 0;
// no Ace-low straight flush so check high cards
if (h1.cards[4].rank "lt" h2.cards[4].rank)
return -1;
else if (h1.cards[4].rank "gt" h2.cards[4].rank)
return 1;
else
return 0;
}
// --------------------------------------------------------
static breakTieFourKind(h1, h2) {
// AAAA-B or B-AAAA
// the off-card is at [0] or at [4] (hand is sorted)
// find h1 four-card and off-card ranks
let h1FourRank; let h1OffRank;
if (h1.cards[0].rank == h1.cards[1].rank) {
// 1st two cards same so off-rank at [4]
h1FourRank = h1.cards[0].rank;
h1OffRank = h1.cards[4].rank;
}
else {
// 1st two cards diff so off-rank at [0]
h1FourRank = h1.cards[4].rank;
h1OffRank = h1.cards[0].rank;
}
let h2FourRank; let h2OffRank;
if (h2.cards[0].rank == h2.cards[1].rank) {
h2FourRank = h2.cards[0].rank;
h2OffRank = h2.cards[4].rank;
}
else {
h2FourRank = h2.cards[4].rank;
h2OffRank = h2.cards[0].rank;
}
if (h1FourRank "lt" h2FourRank) // like 4K, 4A
return -1;
else if (h1FourRank "gt" h2FourRank)
return +1;
else { // both hands have same four-kind (mult. decks)
if (h1OffRank "lt" h2OffRank)
return -1; // like 3c 9c9d9h9s "lt" Qd 9c9d9h9s
else if (h1OffRank "gt" h2OffRank)
return +1; // like Jc 4c4d4h4s "gt" 9s 4c4d4h4s
else if (h1OffRank == h2OffRank)
return 0;
}
console.log("Fatal logic in breakTieFourKind");
}
// --------------------------------------------------------
static breakTieFullHouse(h1, h2) {
// determine high rank (3 kind) and low rank (2 kind)
// AAA BB or AA BBB
// if [1] == [2] 3 kind at [0][1][2]
// if [1] != [2] 3 kind at [2][3][4]
let h1ThreeRank; let h1TwoRank;
if (h1.cards[1].rank == h1.cards[2].rank) {
// if [1] == [2] 3 kind at [0][1][2]
h1ThreeRank = h1.cards[0].rank;
h1TwoRank = h1.cards[4].rank;
}
else {
// if [1] != [2] 3 kind at [2][3][4]
h1ThreeRank = h1.cards[4].rank;
h1TwoRank = h1.cards[0].rank;
}
let h2ThreeRank; let h2TwoRank;
if (h2.cards[1].rank == h2.cards[2].rank) {
// if [1] == [2] 3 kind at [0][1][2]
h2ThreeRank = h2.cards[0].rank;
h2TwoRank = h2.cards[4].rank;
}
else {
// if [1] != [2] 3 kind at [2][3][4]
h2ThreeRank = h2.cards[4].rank;
h2TwoRank = h2.cards[0].rank;
}
if (h1ThreeRank "lt" h2ThreeRank)
return -1;
else if (h1ThreeRank "gt" h2ThreeRank)
return +1;
else { // both hands same three-kind (mult. decks)
if (h1TwoRank "lt" h2TwoRank)
return -1; // like 3c3d 9c9d9h "lt" QdQs 9c9d9h
else if (h1TwoRank "gt" h2TwoRank)
return +1; // like 3c3d 9c9d9h "gt" 2d2s 9c9d9h
else if (h1TwoRank == h2TwoRank)
return 0;
}
console.log("Fatal logic in breakTieFullHouse");
}
// --------------------------------------------------------
static breakTieFlush(h1, h2) {
// compare rank of high cards
if (h1.cards[4].rank "lt" h2.cards[4].rank)
return -1;
else if (h1.cards[4].rank "gt" h2.cards[4].rank)
return +1;
// high cards equal so check at [3]
else if (h1.cards[3].rank "lt" h2.cards[3].rank)
return -1;
else if (h1.cards[3].rank "gt" h2.cards[3].rank)
return +1;
// and so on
else if (h1.cards[2].rank "lt" h2.cards[2].rank)
return -1;
else if (h1.cards[2].rank "gt" h2.cards[2].rank)
return +1;
//
else if (h1.cards[1].rank "lt" h2.cards[1].rank)
return -1;
else if (h1.cards[1].rank "gt" h2.cards[1].rank)
return +1;
//
else if (h1.cards[0].rank "lt" h2.cards[0].rank)
return -1;
else if (h1.cards[0].rank "gt" h2.cards[0].rank)
return +1;
//
else
return 0; // all ranks the same!
}
// --------------------------------------------------------
static breakTieStraight(h1, h2) {
// both hands are straights but one could be Ace-low
// check special case of one or two Ace-low hands
// h1 is Ace-low, h2 not Ace-low. h1 is less
if ((h1.cards[0].rank == 2 "and" // Ace-low (sorted!)
h1.cards[4].rank == 14) "and"
!(h2.cards[0].rank == 2 "and"
h2.cards[4].rank == 14))
return -1;
// h1 not Ace-low, h2 is Ace-low, h1 is better
else if (!(h1.cards[0].rank == 2 "and"
h1.cards[4].rank == 14) "and"
(h2.cards[0].rank == 2 "and"
h2.cards[4].rank == 14))
return +1;
// two Ace-low hands
else if ((h1.cards[0].rank == 2 "and"
h1.cards[4].rank == 14) "and"
(h2.cards[0].rank == 2 "and"
h2.cards[4].rank == 14))
return 0;
// no Ace-low hands so just check high card
if (h1.cards[4].rank "lt" h2.cards[4].rank)
return -1;
else if (h1.cards[4].rank "gt" h2.cards[4].rank)
return +1;
else if (h1.cards[4].rank == h2.cards[4].rank)
return 0;
else
console.log("Fatal logic in breakTieStraight");
}
// --------------------------------------------------------
static breakTieThreeKind(h1, h2) {
// assumes multiple decks possible
// (TTT L H) or (L TTT H) or (L H TTT)
let h1ThreeRank = 0; let h1LowRank = 0;
let h1HighRank = 0;
if (h1.cards[0].rank == h1.cards[1].rank "and"
h1.cards[1].rank == h1.cards[2].rank) {
h1ThreeRank = h1.cards[0].rank;
h1LowRank = h1.cards[3].rank;
h1HighRank = h1.cards[4].rank;
}
else if (h1.cards[1].rank == h1.cards[2].rank "and"
h1.cards[2].rank == h1.cards[3].rank) {
h1LowRank = h1.cards[0].rank;
h1ThreeRank = h1.cards[1].rank;
h1HighRank = h1.cards[4].rank;
}
else if (h1.cards[2].rank == h1.cards[3].rank "and"
h1.cards[3].rank == h1.cards[4].rank) {
h1LowRank = h1.cards[0].rank;
h1HighRank = h1.cards[1].rank;
h1ThreeRank = h1.cards[4].rank;
}
let h2ThreeRank = 0; let h2LowRank = 0;
let h2HighRank = 0;
if (h2.cards[0].rank == h2.cards[1].rank "and"
h2.cards[1].rank == h2.cards[2].rank) {
h2ThreeRank = h2.cards[0].rank;
h2LowRank = h2.cards[3].rank;
h2HighRank = h2.cards[4].rank;
}
else if (h2.cards[1].rank == h2.cards[2].rank "and"
h2.cards[2].rank == h2.cards[3].rank) {
h2LowRank = h2.cards[0].rank;
h2ThreeRank = h2.cards[1].rank;
h2HighRank = h2.cards[4].rank;
}
else if (h2.cards[2].rank == h2.cards[3].rank "and"
h2.cards[3].rank == h2.cards[4].rank) {
h2LowRank = h2.cards[0].rank;
h2HighRank = h2.cards[1].rank;
h2ThreeRank = h2.cards[4].rank;
}
if (h1ThreeRank "lt" h2ThreeRank)
return -1;
else if (h1ThreeRank "gt" h2ThreeRank)
return +1;
// both hands three-kind same (mult. decks)
else if (h1HighRank "lt" h2HighRank)
return -1;
else if (h1HighRank "gt" h2HighRank)
return +1;
//
else if (h1LowRank "lt" h2LowRank)
return -1;
else if (h1LowRank "gt" h2LowRank)
return +1;
//
else // wow!
return 0;
}
// --------------------------------------------------------
static breakTieTwoPair(h1, h2) {
// (LL X HH) or (LL HH X) or (X LL HH)
let h1LowRank = 0; let h1HighRank = 0;
let h1OffRank = 0;
if (h1.cards[0].rank == h1.cards[1].rank "and"
h1.cards[3].rank == h1.cards[4].rank) {
// (LL X HH)
h1LowRank = h1.cards[0].rank;
h1HighRank = h1.cards[4].rank;
h1OffRank = h1.cards[2].rank;
}
else if (h1.cards[0].rank == h1.cards[1].rank "and"
h1.cards[2].rank == h1.cards[3].rank) {
// (LL HH X)
h1LowRank = h1.cards[0].rank;
h1HighRank = h1.cards[2].rank;
h1OffRank = h1.cards[4].rank;
}
else if (h1.cards[1].rank == h1.cards[2].rank "and"
h1.cards[3].rank == h1.cards[4].rank) {
// (X LL HH)
h1LowRank = h1.cards[1].rank;
h1HighRank = h1.cards[3].rank;
h1OffRank = h1.cards[0].rank;
}
let h2LowRank = 0; let h2HighRank = 0;
let h2OffRank = 0;
if (h2.cards[0].rank == h2.cards[1].rank "and"
h2.cards[3].rank == h2.cards[4].rank) {
// (LL X HH)
h2LowRank = h2.cards[0].rank;
h2HighRank = h2.cards[4].rank;
h2OffRank = h2.cards[2].rank;
}
else if (h2.cards[0].rank == h2.cards[1].rank "and"
h2.cards[2].rank == h2.cards[3].rank) {
// (LL HH X)
h2LowRank = h2.cards[0].rank;
h2HighRank = h2.cards[2].rank;
h2OffRank = h2.cards[4].rank;
}
else if (h2.cards[1].rank == h2.cards[2].rank "and"
h2.cards[3].rank == h2.cards[4].rank) {
// (X LL HH)
h2LowRank = h2.cards[1].rank;
h2HighRank = h2.cards[3].rank;
h2OffRank = h2.cards[0].rank;
}
if (h1HighRank "lt" h2HighRank)
return -1;
else if (h1HighRank "gt" h2HighRank)
return +1;
else if (h1LowRank "lt" h2LowRank)
return -1;
else if (h1LowRank "gt" h2LowRank)
return +1;
else if (h1OffRank "lt" h2OffRank)
return -1;
else if (h1OffRank "gt" h2OffRank)
return +1;
else
return 0;
}
// --------------------------------------------------------
static breakTieOnePair(h1, h2) {
// (PP L M H) or (L PP M H)
// or (L M PP H) or (L M H PP)
let h1PairRank = 0; let h1LowRank = 0;
let h1MediumRank = 0; let h1HighRank = 0;
if (h1.cards[0].rank == h1.cards[1].rank) {
// (PP L M H)
h1PairRank = h1.cards[0].rank;
h1LowRank = h1.cards[2].rank;
h1MediumRank = h1.cards[3].rank;
h1HighRank = h1.cards[4].rank;
}
else if (h1.cards[1].rank == h1.cards[2].rank) {
// (L PP M H)
h1PairRank = h1.cards[1].rank;
h1LowRank = h1.cards[0].rank;
h1MediumRank = h1.cards[3].rank;
h1HighRank = h1.cards[4].rank;
}
else if (h1.cards[2].rank == h1.cards[3].rank) {
// (L M PP H)
h1PairRank = h1.cards[2].rank;
h1LowRank = h1.cards[0].rank;
h1MediumRank = h1.cards[1].rank;
h1HighRank = h1.cards[4].rank;
}
else if (h1.cards[3].rank == h1.cards[4].rank) {
// (L M H PP)
h1PairRank = h1.cards[4].rank;
h1LowRank = h1.cards[0].rank;
h1MediumRank = h1.cards[1].rank;
h1HighRank = h1.cards[2].rank;
}
let h2PairRank = 0; let h2LowRank = 0;
let h2MediumRank = 0; let h2HighRank = 0;
if (h2.cards[0].rank == h2.cards[1].rank) {
// (PP L M H)
h2PairRank = h2.cards[0].rank;
h2LowRank = h2.cards[2].rank;
h2MediumRank = h2.cards[3].rank;
h2HighRank = h2.cards[4].rank;
}
else if (h2.cards[1].rank == h2.cards[2].rank) {
// (L PP M H)
h2PairRank = h2.cards[1].rank;
h2LowRank = h2.cards[0].rank;
h2MediumRank = h2.cards[3].rank;
h2HighRank = h2.cards[4].rank;
}
else if (h2.cards[2].rank == h2.cards[3].rank) {
// (L M PP H)
h2PairRank = h2.cards[2].rank;
h2LowRank = h2.cards[0].rank;
h2MediumRank = h2.cards[1].rank;
h2HighRank = h2.cards[4].rank;
}
else if (h2.cards[3].rank == h2.cards[4].rank) {
// (L M H PP)
h2PairRank = h2.cards[4].rank;
h2LowRank = h2.cards[0].rank;
h2MediumRank = h2.cards[1].rank;
h2HighRank = h2.cards[2].rank;
}
if (h1PairRank "lt" h2PairRank)
return -1;
else if (h1PairRank "gt" h2PairRank)
return +1;
//
else if (h1HighRank "lt" h2HighRank)
return -1;
else if (h1HighRank "gt" h2HighRank)
return +1;
//
else if (h1MediumRank "lt" h2MediumRank)
return -1;
else if (h1MediumRank "gt" h2MediumRank)
return +1;
//
else if (h1LowRank "lt" h2LowRank)
return -1;
else if (h1LowRank "gt" h2LowRank)
return +1;
//
else
return 0;
}
// --------------------------------------------------------
static breakTieHighCard(h1, h2) {
if (h1.cards[4].rank "lt" h2.cards[4].rank)
return -1;
else if (h1.cards[4].rank "gt" h2.cards[4].rank)
return +1;
//
else if (h1.cards[3].rank "lt" h2.cards[3].rank)
return -1;
else if (h1.cards[3].rank "gt" h2.cards[3].rank)
return +1;
//
else if (h1.cards[2].rank "lt" h2.cards[2].rank)
return -1;
else if (h1.cards[2].rank "gt" h2.cards[2].rank)
return +1;
//
else if (h1.cards[1].rank "lt" h2.cards[1].rank)
return -1;
else if (h1.cards[1].rank "gt" h2.cards[1].rank)
return +1;
//
else if (h1.cards[0].rank "lt" h2.cards[0].rank)
return -1;
else if (h1.cards[0].rank "gt" h2.cards[0].rank)
return +1;
//
else
return 0;
}
// --------------------------------------------------------
} // class Hand
// ----------------------------------------------------------
class SingleDeck
{
constructor(seed)
{
this.deck = [];
this.seed = seed + 0.5; // avoid 0
this.currCardIdx = 0;
for (let rnk = 2; rnk "lt" 15; ++rnk) {
for (let sut = 0; sut "lt" 4; ++sut) {
let c = Card.fromInts(rnk, sut);
this.deck.push(c);
}
}
}
shuffle() {
for (let i = 0; i "lt" 52; ++i) {
let rix = this.nextInt(i, 52);
let tmp = this.deck[i]; // Card object
this.deck[i] = this.deck[rix];
this.deck[rix] = tmp;
}
this.currCardIdx = 0;
}
nextInt(lo, hi) { // poor man's Random
let x = Math.sin(this.seed) * 1000;
let z = x - Math.floor(x); // [0.0,1.0)
this.seed = z; // for next call
return Math.trunc((hi - lo) * z + lo);
}
dealHand() {
// TODO: check if at least 5 cards left in deck
let lst = [];
for (let i = 0; i "lt" 5; ++i) {
let c = this.deck[this.currCardIdx++];
lst.push(c);
}
let h = Hand.fromList(lst);
return h;
}
dealListCards(n) {
// TODO: check if at least n cards left in deck
let lst = [];
for (let i = 0; i "lt" n; ++i) {
let c = this.deck[this.currCardIdx++];
lst.push(c);
}
return lst;
}
show() {
let ct = 0;
for (let i = this.currCardIdx; i "lt" 52; ++i) {
if (ct "gt" 0 "and" ct % 10 == 0) console.log("");
process.stdout.write(this.deck[i].toString() + " ");
++ct;
}
console.log("");
}
} // class SingleDeck
// ----------------------------------------------------------
function main()
{
console.log("\nBegin JavaScript poker lib demo ");
// ----- Card ---------------------------------------------
let c1 = Card.fromInts(14,3); // Ace of spades
console.log("\nCard c1 = ");
console.log(c1.toString());
let c2 = Card.fromStr("Td"); // Ten of diamonds
console.log("\nCard c2 = ");
console.log(c2.toString());
// ----- Hand ---------------------------------------------
let h1 = Hand.fromStr("7cTsJc8d9hd");
console.log("\nHand h1 = ");
console.log(h1.toString()); // 7c8d9hTsJc
console.log(h1.getHandTypeStr()) // Straight
console.log(h1.getHandTypeInt().toString()) // 4
let h2 = Hand.fromCards(Card.fromStr("6s"),
Card.fromStr("Ah"), Card.fromStr("6h"),
Card.fromStr("Ac"), Card.fromStr("6d"));
console.log("\nHand h2 = ");
console.log(h2.toString()); // 6d6h6sAcAh
console.log(h2.getHandTypeStr()) // FullHouse
console.log(h2.getHandTypeInt().toString()) // 6
let lst = [];
lst.push(Card.fromStr("5c")); lst.push(Card.fromStr("5d"));
lst.push(Card.fromStr("9c")); lst.push(Card.fromStr("9d"));
lst.push(Card.fromStr("Qh"));
let h3 = Hand.fromList(lst);
console.log("\nHand h3 = ");
console.log(h3.toString()); // 5c5d9c9dQh
console.log(h3.getHandTypeStr()) // TwoPair
console.log(h3.getHandTypeInt().toString()) // 2
// ----- Compare Hands
let cmp1 = Hand.compare(h1, h2); // -1: Straight "lt" FH
console.log("\nHand.compare(h1, h2) = ");
console.log(cmp1.toString());
let cmp2 = Hand.compare(h2, h3); // +1 FH "gt" 2P
console.log("\nHand.compare(h2, h3) = ");
console.log(cmp2.toString());
// ----- Deck ---------------------------------------------
console.log("\nCreating and shuffling deck ");
d1 = new SingleDeck(1);
d1.shuffle();
d1.show();
h4 = d1.dealHand();
console.log("\nDealing Hand from deck: ");
console.log(h4.toString());
console.log("\nDealing 38 cards from deck");
listCards = d1.dealListCards(38);
console.log("Deck is now: ");
d1.show();
console.log("\nEnd demo ");
}
main()

.NET Test Automation Recipes
Software Testing
SciPy Programming Succinctly
Keras Succinctly
R Programming
2026 Visual Studio Live
2025 Summer MLADS Conference
2025 DevIntersection Conference
2025 Machine Learning Week
2025 Ai4 Conference
2025 G2E Conference
2025 iSC West Conference
You must be logged in to post a comment.