Blurb Poker Solution (JavaScript ES6)
"use strict";
class Card {
constructor(cardCode) {
const faceValues = {'J': 11, 'Q': 12, 'K': 13, 'A': 14}; // Face cards to numeric value dictionary
//Set internal card variables containing individual card properties
this.cardCode = cardCode; // Sets Card Code
this.suit = cardCode.slice(-1); // Sets Suit
this.face = cardCode.slice(0, cardCode.length - 1); // Sets Value
this.value = this.face in faceValues ? faceValues[this.face] : parseInt(this.face); // Converts face card to numerical ranking
//Check if Card has an invalid Suit or Face Value, if so raise error
if (isNaN(this.value) || (['C', 'D', 'S', 'H'].indexOf(this.suit) === -1)) {
throw new Error("Card data needs to be in proper format!");
}
}
get value() {
return this._value;
}
set value(val) {
this._value = val;
}
get suit() {
return this._suit;
}
set suit(suit) {
this._suit = suit;
}
get face() {
return this._face;
}
set face(face) {
this._face = face;
}
get cardCode() {
return this._cardCode;
}
set cardCode(cardCode) {
this._cardCode = cardCode;
}
printCard() { // Prints card to console
console.log("Suit:" + this.suit + " | Face:" + this.face + " | Value:" + this.value);
}
}
class Hand {
constructor(cardsStr) {
this.cards = []; // Initializes empty hand
this.cardsStr = cardsStr; // Stores card input string
let parseCardsStr = () => cardsStr.split(' ').forEach((card) => this.cards.push(new Card(card)));
parseCardsStr(); // Parses string into individual card objects and populates cards array
}
static handCheckMethods() { // Returns an object containing all hand configurations to tests
return {
highCard: function () { // Check for high card
this.handStats.highCard = this.cards[this.cards.length - 1]; // Returns last(highest ranked) card
},
checkPairs: function () { // Check for pairs
this.cards.forEach((card) => this.handStats.count[card.value] = (this.handStats.count[card.value] || 0) + 1);
for (let key in this.handStats.count) { // Iterate through every card grouped in count
if (this.handStats.count[key] === 2) { // Check for pair
this.handStats.pairs.push(key);
}
else if (this.handStats.count[key] === 3) { //Check for 3 of a kind
this.handStats.threeOfAKind.push(key);
}
else if (this.handStats.count[key] === 4) { //Check for 4 of a kind
this.handStats.fourOfAKind.push(key);
}
}
},
checkStraight: function () {
this.handStats.hasStraight = this.cards.every((card, i) => (i === this.cards.length - 1) || (card.value === this.cards[i + 1].value - 1));
},
checkFlush: function () {
this.handStats.hasFlush = this.cards.every((card, i) => (i === this.cards.length - 1) || (card.suit === this.cards[i + 1].suit));
}
};
}
calculateHandStats() {
//Initialize blank stats object
this.handStats = {
count: {}, // Counts number of occurrences of face values
highCard: null, // Reference to the highCard
pairs: [], // Pair info
threeOfAKind: [], // Three of a Kind info
fourOfAKind: [], // Four of a Kind info
hasStraight: false, // Tracks whether hand is a straight
hasFlush: false // Tracks whether hand is a flush
};
this.cards.sort((a, b) => a.value - b.value); // Sorts hand initially to easily find High Card and Straights
let buildResultArr = () => { //Builds result array to return based on calculated hand stats
let result = [];
//Adds High Card
result.unshift("High Card:" + this.handStats['highCard'].cardCode);
//Check for Full House, Pairs, and Three/Four of a Kind
if (this.handStats.threeOfAKind.length === 1 && this.handStats.pairs.length === 1) {
result.unshift("Full House");
}
else {
//Checks for Pairs
if (this.handStats.pairs.length > 0) {
result.unshift(this.handStats.pairs.length + " pair");
}
//Checks for Three of a Kind
if (this.handStats.threeOfAKind.length > 0) {
result.unshift("Three of a Kind");
}
//Checks for Four of a Kind
if (this.handStats.fourOfAKind.length > 0) {
result.unshift("Four of a Kind");
}
}
//Check for Royal Flush, Flush, and Straights
if (this.handStats.hasFlush || this.handStats.hasStraight) {
let straightFlushArray = [];
if (this.handStats.hasFlush && this.handStats.hasStraight && this.handStats.highCard.value === 14) {
straightFlushArray.push("Royal Flush");
} else {
if (this.handStats.hasStraight) {
straightFlushArray.push("Straight")
}
if (this.handStats.hasFlush) {
straightFlushArray.push("Flush");
}
}
result.unshift(straightFlushArray.join(' ')); // Adds royal/flush/straight info to result array
}
return result;
};
//Runs a check for every possible hand configuration stored in handCalculations
let handCheckMethods = Hand.handCheckMethods();
for (let checkMethod in handCheckMethods) {
handCheckMethods[checkMethod].call(this);
}
return buildResultArr();
}
get cards() {
return this._cards;
}
set cards(cards) {
this._cards = cards;
}
get handStats() {
return this._handStats;
}
set handStats(handStats) {
this._handStats = handStats;
}
get cardsStr() { // Returns input card string for display purposes
return this._cardsStr;
}
set cardsStr(cardsStr) {
this._cardsStr = cardsStr;
}
printCards() { // Prints hand to console
this.cards.forEach((card) => card.printCard());
}
printStats() { // Prints out calculated hand stats
console.log("Hand Stats:", this.handStats);
}
}
function hands(handStr) {
let hand = new Hand(handStr);
console.log("Running test for Hand [" + hand.cardsStr + "]");
return hand.calculateHandStats();
}
function testHands(tests) {
for (let method in tests) {
let test = tests[method];
console.log("---------------------------------------------");
console.log("Test Name:", test.testName);
let result = hands(test.handStr);
console.log("Test Expected Result:", test.expectedResult);
console.log("Test Actual Results:", result);
let passed = (JSON.stringify(test.expectedResult) === JSON.stringify(result));
console.log("Test Passed? " + passed);
console.log("---------------------------------------------");
}
}
let tests = {
highCard: { //Test for High Card
testName: "High Card",
expectedResult: ['High Card:AS'],
handStr: "AS 2D 4C 7D JD"
},
onePair: { //Test for 1 Pair
testName: "1 Pair",
expectedResult: ['1 pair', 'High Card:AS'],
handStr: "AS 2D 4C JC JD"
},
twoPair: { //Test for 2 Pair
testName: "2 Pair",
expectedResult: ['2 pair', 'High Card:AS'],
handStr: "AS 2D 2S JC JD"
},
threeOfAKind: { // Test for Three of a Kind
testName: "Three of a Kind",
expectedResult: ['Three of a Kind', 'High Card:AS'],
handStr: "AS 2D JS JC JD"
},
fourOfAKind: { // Test for Four of a Kind
testName: "Four of a Kind",
expectedResult: ['Four of a Kind', 'High Card:AS'],
handStr: "AS JH JS JC JD"
},
fullHouse: { // Test for Full house
testName: "Full House",
expectedResult: ['Full House', 'High Card:AH'],
handStr: "AS AH JS JC JD"
},
straight: { // Test for Straight
testName: "Straight",
expectedResult: ['Straight', 'High Card:6D'],
handStr: "2S 3H 4S 5C 6D"
},
flush: { // Test for Flush
testName: "Flush",
expectedResult: ['Flush', 'High Card:10H'],
handStr: "10H 3H 4H 5H 6H"
},
straightFlush: { // Test for Straight Flush
testName: "Straight Flush",
expectedResult: ['Straight Flush', 'High Card:6H'],
handStr: "2H 3H 4H 5H 6H"
},
royalFlush: { // Test for Royal Flush
testName: "Royal Flush",
expectedResult: ['Royal Flush', 'High Card:AH'],
handStr: "AH KH QH 10H JH"
}
};
testHands(tests);