diff --git a/.eslintrc.js b/.eslintrc.js
index 2567d5f9..18683374 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -3,22 +3,20 @@ module.exports = {
browser: true,
es2020: true,
},
- extends: [
- 'airbnb-base',
- ],
+ extends: ["airbnb-base"],
parserOptions: {
ecmaVersion: 11,
},
rules: {
// Don't enforce control flow closing curly brace needs to be
// on same line as next control flow opening statement
- 'brace-style': 'off',
+ "brace-style": "off",
// Disable linebreak style to prevent ESLint errors on Windows line endings
// https://eslint.org/docs/rules/linebreak-style
- 'linebreak-style': 'off',
+ "linebreak-style": "off",
// Allow console for students to debug
- 'no-console': 'off',
+ "no-console": "off",
// Allow function param reassign for array or object elements or properties
- 'no-param-reassign': ['error', { props: false }],
+ "no-param-reassign": ["error", { props: false }],
},
};
diff --git a/README.md b/README.md
index 1d3a8a65..d2d33e06 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,4 @@
# Rocket Academy Coding Bootcamp: Video Poker
+
+# Github Pages
+https://noellimx.github.io/video-poker-bootcamp/
\ No newline at end of file
diff --git a/index-test.html b/index-test.html
new file mode 100644
index 00000000..910a67a8
--- /dev/null
+++ b/index-test.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..e7cab9fd
--- /dev/null
+++ b/index.html
@@ -0,0 +1,52 @@
+
+
+
+ 1️⃣ 6️⃣ 8️⃣ HUAT POKER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main.js b/main.js
new file mode 100644
index 00000000..7462121b
--- /dev/null
+++ b/main.js
@@ -0,0 +1,61 @@
+const MODES = {
+ TEST: `test`,
+ DEMO: `demo`,
+};
+
+const getMode = () => document.getElementById(`root`).getAttribute(`mode`);
+
+const getDefaultElementRoot = () => document.getElementById(`root`);
+
+/**
+ * @typedef {Object} Core
+ * @property {Player} player
+ * @property {HTMLElement} elementRoot
+ *
+ */
+
+const newCore = () => {
+ return {
+ player: newPlayer(),
+ elementRoot: getDefaultElementRoot(),
+ };
+};
+
+const getPlayerOfCore = (core) => core.player;
+const getPlayerCreditOfCore = (core) => getPlayerCredit(core.player);
+const minusPlayerCreditOfCore = (core, credit) =>
+ minusPlayerCredit(getPlayerOfCore(core), credit);
+const addPlayerCreditOfCore = (core, credit) =>
+ addPlayerCredit(getPlayerOfCore(core), credit);
+const getPlayerNameOfCore = (core) => getPlayerName(core.player);
+const getElementRootOfCore = (core) => core.elementRoot;
+
+// -------- START --------
+
+(() => {
+ console.log(`START`);
+ const mode = getMode();
+ if (mode === MODES.TEST) {
+ TEST_ALL();
+ }
+ if (mode === MODES.DEMO) {
+ const core = newCore();
+
+ const imgLoading = document.createElement(`img`);
+ imgLoading.setAttribute(`src`, `./static/img/circleOfCards.gif`);
+ imgLoading.setAttribute(`alt`, `A loading page gif`);
+ imgLoading.setAttribute(
+ `credit`,
+ `https://tenor.com/view/gamble-cards-pokers-gif-7257796`
+ );
+
+ const imgLoadDiv = document.createElement(`div`);
+ imgLoadDiv.className += ` img-loading-div`;
+ appendChild(imgLoadDiv, imgLoading);
+ appendChild(core.elementRoot, imgLoadDiv);
+ setTimeout(() => {
+ detachAllChildren(core.elementRoot);
+ goToPlayerConfigPage(core);
+ }, 2000);
+ }
+})();
diff --git a/scripts/general-helpers.js b/scripts/general-helpers.js
new file mode 100644
index 00000000..fccd8422
--- /dev/null
+++ b/scripts/general-helpers.js
@@ -0,0 +1,37 @@
+/**
+ *
+ * @param {*} arg
+ * @returns {boolean} returns true if argument is null or undefined.
+ */
+const isNoU = (arg) => arg === undefined || arg === null;
+
+/**
+ *
+ * @param {[]<*>} args
+ * @returns
+ */
+const isAnyNoU = (...args) => args.some((arg) => isNoU(arg));
+
+const incrementOneToObjProp = (obj, property) => {
+ if (typeof obj[property] !== `number`) {
+ if (isNoU(obj[property])) {
+ obj[property] = 0;
+ } else {
+ console.warn(
+ `[addOneToObjProp] the property ${property} of object is assigned to a non-scalar value.`
+ );
+ return;
+ }
+ }
+ obj[property] += 1;
+};
+
+/**
+ *
+ * @param {*} obj
+ * @param {Array<*>} prop
+ * @param {*} element
+ */
+const addElementToPropInObject = (obj, prop, element) => {
+ obj[prop].push(element);
+};
diff --git a/scripts/src/card.js b/scripts/src/card.js
new file mode 100644
index 00000000..51cf0e9d
--- /dev/null
+++ b/scripts/src/card.js
@@ -0,0 +1,330 @@
+const POKER_HAND_SIZE = 5;
+
+const CARD_VALUE = {
+ ONE: `ONE`,
+ TWO: `TWO`,
+ THREE: `THREE`,
+ FOUR: `FOUR`,
+ FIVE: `FIVE`,
+ SIX: `SIX`,
+ SEVEN: `SEVEN`,
+ EIGHT: `EIGHT`,
+ NINE: `NINE`,
+ TEN: `TEN`,
+ JACK: `JACK`,
+ QUEEN: `QUEEN`,
+ KING: `KING`,
+};
+
+const CARD_SUITS = {
+ HEART: `HEARTS`,
+ CLUB: `CLUBS`,
+ SPADE: `SPADES`,
+ DIAMOND: `DIAMONDS`,
+};
+
+/**
+ * @typedef {Object} Card
+ * @property {string} cardValue
+ * @property {string} suit
+ */
+
+/**
+ * @returns {Card}
+ */
+const newCard = (cardValue, suit) => {
+ if (isAnyNoU(cardValue, suit)) {
+ return new Error(`card value and suit must be satisfied.`);
+ }
+ return {
+ cardValue,
+ suit,
+ };
+};
+
+/**
+ *
+ * @param {Card} card
+ * @returns {string} Card Value
+ */
+const getCardValue = (card) => card.cardValue;
+
+/**
+ *
+ * @param {Card} card
+ * @returns {string} Card Suit
+ */
+const getCardSuit = (card) => card.suit;
+
+/**
+ *
+ * @param {Card} card
+ * @returns {number} The numerical value of the card.
+ */
+const getCardOrdinal = (card) => {
+ const cardValue = getCardValue(card);
+ switch (cardValue) {
+ case CARD_VALUE.ONE:
+ return 14;
+ case CARD_VALUE.TWO:
+ return 2;
+ case CARD_VALUE.THREE:
+ return 3;
+ case CARD_VALUE.FOUR:
+ return 4;
+ case CARD_VALUE.FIVE:
+ return 5;
+ case CARD_VALUE.SIX:
+ return 6;
+ case CARD_VALUE.SEVEN:
+ return 7;
+ case CARD_VALUE.EIGHT:
+ return 8;
+ case CARD_VALUE.NINE:
+ return 9;
+ case CARD_VALUE.TEN:
+ return 10;
+ case CARD_VALUE.JACK:
+ return 11;
+ case CARD_VALUE.QUEEN:
+ return 12;
+ case CARD_VALUE.KING:
+ return 13;
+ }
+};
+
+/**
+ * @typedef {Object} InPlayCard
+ * @property {Card} value
+ * @property {HTMLElement} element
+ * @property {boolean} faceUp
+ * @property {boolean} isDiscarded
+ */
+
+const getInPlayCardSuit = (inPlayCard) => getCardSuit(inPlayCard.value);
+const getInPlayCardValue = (inPlayCard) => getCardValue(inPlayCard.value);
+const getInPlayCardRankAndSuitString = (inPlayCard) =>
+ `${getInPlayCardSuit(inPlayCard)}-${getInPlayCardValue(inPlayCard)}`;
+const getInPlayCardOrdinal = (inPlayCard) => getCardOrdinal(inPlayCard.value);
+const getInPlayCardElement = (inPlayCard) => inPlayCard.element;
+const isInPlayCardFaceUp = (inPlayCard) => inPlayCard.faceUp;
+const isInPlayCardDiscarded = (inPlayCard) => inPlayCard.isDiscarded;
+
+/**
+ *
+ * @param {Card} card
+ * @returns {InPlayCard}
+ */
+const newInPlayCard = (card) => {
+ return {
+ value: card,
+ faceUp: true,
+ };
+};
+
+/**
+ * @typedef {Array} Hand
+ */
+
+/**
+ *
+ * @param {Hand} hand
+ * @returns {number} Size of hand
+ */
+const getHandSize = (hand) => hand.length;
+
+/**
+ *
+ * @returns {Hand}
+ */
+const newHand = () => {
+ return [];
+};
+
+/**
+ *
+ * @param {Hand} hand
+ * @param {Card} card
+ */
+const addCardToHand = (hand, card) => hand.push(newInPlayCard(card));
+
+/**
+ *
+ * @param {Hand} hand
+ * @param {InPlayCard} inPlayCard
+ * @returns
+ */
+const addInPlayCardToHand = (hand, inPlayCard) => hand.push(inPlayCard);
+/**
+ *
+ * @param {Hand} hand
+ * @param {InPlayCard} inPlayCard
+ * @returns
+ */
+const addInPlayCardsToHand = (hand, inPlayCards) =>
+ inPlayCards.forEach((c) => hand.push(c));
+
+const getHandAsString = (hand) => {
+ const cardStrings = [];
+ for (const card of hand) {
+ cardStrings.push(getInPlayCardRankAndSuitString(card));
+ }
+ return `[ ${cardStrings.join(` | `)} ]`;
+};
+/**
+ *
+ * @param {Hand} hand
+ * @param {card} card
+ */
+const addCardsToHand = (hand, cards) =>
+ cards.forEach((card) => addCardToHand(hand, card));
+
+const newHandWithCards = (cards) => {
+ const hand = newHand();
+ addCardsToHand(hand, cards);
+ return hand;
+};
+
+/**
+ *
+ * @param {*} hand
+ * @param {*} currentIndex
+ * @param {*} sizePerHandCombination
+ * @param {*} toTake
+ * @param {*} result
+ * @param {*} currentCombination
+ * @returns
+ */
+const ______WARN_addHandCombinations = (
+ hand,
+ currentIndex,
+ sizePerHandCombination,
+ toTake,
+ result,
+ currentCombination
+) => {
+ if (currentCombination.length === sizePerHandCombination) {
+ result.push(currentCombination);
+ return;
+ }
+ if (0 === toTake) {
+ return;
+ }
+
+ if (currentIndex === hand.length) {
+ return;
+ }
+
+ const combination = [...currentCombination, hand[currentIndex]];
+
+ ______WARN_addHandCombinations(
+ hand,
+ currentIndex + 1,
+ sizePerHandCombination,
+ toTake,
+ result,
+ currentCombination
+ );
+ ______WARN_addHandCombinations(
+ hand,
+ currentIndex + 1,
+ sizePerHandCombination,
+ toTake - 1,
+ result,
+ combination
+ );
+};
+
+/**
+ * Note: permutations is NOT combinations
+ * WARNING: If no. of combinations is expected to be big, this method will fry your browser.
+ * If you want to get the best hand immediate @see ______WARN_getHandBestCombination
+ * @param {Hand} hand hand of arbitrary size
+ * @param {number} sizePerHandCombination No. of cards per hand combination.
+ */
+const ______WARN_getHandCombinations = (hand, sizePerHandCombination) => {
+ if (isNoU(sizePerHandCombination)) {
+ throw new Error(`Please specify no. of card per hand.`);
+ }
+ const result = [];
+ const currentCombination = [];
+
+ ______WARN_addHandCombinations(
+ hand,
+ 0,
+ sizePerHandCombination,
+ sizePerHandCombination,
+ result,
+ currentCombination
+ );
+ return result;
+};
+
+const ______WARN_setSimpleBestHandCombinations = (
+ hand,
+ currentIndex,
+ sizePerHandCombination,
+ toTake,
+ bestScoreProfile,
+ currentCombination
+) => {
+ const { bestScore } = bestScoreProfile;
+ if (currentCombination.length === sizePerHandCombination) {
+ const score = getScoreType(currentCombination);
+ if (getRankOfScoringType(bestScore) < getRankOfScoringType(score)) {
+ bestScoreProfile.bestScore = score;
+ bestScoreProfile.simpleBestHand = currentCombination;
+ }
+ return;
+ }
+ if (0 === toTake) {
+ return;
+ }
+
+ if (currentIndex === hand.length) {
+ return;
+ }
+
+ ______WARN_setSimpleBestHandCombinations(
+ hand,
+ currentIndex + 1,
+ sizePerHandCombination,
+ toTake,
+ bestScoreProfile,
+ currentCombination
+ );
+
+ const combination = [...currentCombination, hand[currentIndex]];
+
+ ______WARN_setSimpleBestHandCombinations(
+ hand,
+ currentIndex + 1,
+ sizePerHandCombination,
+ toTake - 1,
+ bestScoreProfile,
+ combination
+ );
+};
+
+/**
+ * This method compares during first pass and efficiently returns the best hand. The best hand is compared simply and does not gaurantee return of highest subranking within a scoring type.
+ * @param {*} hand of arbitrary size
+ * @returns
+ */
+const ______WARN_getHandSimpleBestCombination = (hand) => {
+ const currentCombination = [];
+
+ const bestScoreProfile = {
+ bestScore: SCORING.UNKNOWN,
+ simpleBestHand: null,
+ };
+ ______WARN_setSimpleBestHandCombinations(
+ hand,
+ 0,
+ POKER_HAND_SIZE,
+ POKER_HAND_SIZE,
+ bestScoreProfile,
+ currentCombination
+ );
+ return bestScoreProfile;
+};
diff --git a/scripts/src/deck.js b/scripts/src/deck.js
new file mode 100644
index 00000000..08109d7d
--- /dev/null
+++ b/scripts/src/deck.js
@@ -0,0 +1,44 @@
+/**
+ * @typedef {Hand} Deck
+ */
+const newDeck = newHand;
+const addCardToDeck = addCardToHand;
+const getDeckSize = getHandSize;
+
+const drawCard = (deck) => deck.pop();
+
+/**
+ * @returns {Deck}
+ */
+const newSampleSingleSuitDeck = (suit) => {
+ const deck = newDeck();
+ suit = suit || CARD_SUITS.HEART;
+ for (const value of Object.values(CARD_VALUE)) {
+ const card = newCard(value, suit);
+ addCardToDeck(deck, card);
+ }
+ return deck;
+};
+
+const newStandardDeck = (isToRiffle = true) => {
+ const values = Object.values(CARD_VALUE);
+ const suits = Object.values(CARD_SUITS);
+
+ const deck = newDeck();
+ for (const v of values) {
+ for (const s of suits) {
+ const card = newCard(v, s);
+ addCardToDeck(deck, card);
+ }
+ }
+
+ if (isToRiffle) {
+ const le = deck.length;
+ for (let i = 0; i < le; i += 1) {
+ const j = Math.floor(Math.random() * (le - i)) + i;
+ [deck[j], deck[i]] = [deck[i], deck[j]];
+ }
+ }
+
+ return deck;
+};
diff --git a/scripts/src/elementHelpers/compositeHelpers.js b/scripts/src/elementHelpers/compositeHelpers.js
new file mode 100644
index 00000000..026c13b2
--- /dev/null
+++ b/scripts/src/elementHelpers/compositeHelpers.js
@@ -0,0 +1,94 @@
+// Player Config Page
+
+const _updatePlayerInputHandler =
+ (playerConfig) =>
+ ({ target: { value } }) => {
+ if (value === ``) {
+ hideStartButton(playerConfig);
+ } else {
+ showStartButton(playerConfig);
+ }
+ setPlayerNameOfPlayerConfig(playerConfig, value);
+ updateNameDisplayInGameConfig(playerConfig);
+ };
+
+const setGameModeAndUpdateDisability = (playerConfig, mode) => {
+ setGameModeForPlayerConfig(playerConfig, mode);
+ updateDisablitiyGameMode(playerConfig);
+};
+const _toggleGameModeHandler = (playerConfig, mode) => () =>
+ setGameModeAndUpdateDisability(playerConfig, mode);
+
+/**
+ *
+ * @param {playerConfig} playerConfig
+ * @returns
+ */
+const newElementNameInputWhichUpdatesPlayerNameOnInput = (playerConfig) => {
+ const element = newElementNameInput();
+ element.addEventListener(
+ `input`,
+ _updatePlayerInputHandler(playerConfig, element)
+ );
+ return element;
+};
+
+const newElementButtonStartGameWithStart = (playerConfig) => {
+ const element = newElementButtonStartGame();
+ element.addEventListener(`click`, () => {
+ _clearPlayerConfigDisplayAndGoToGame(playerConfig);
+ });
+
+ return element;
+};
+
+const newElementButtonGameModeAndSetToggle = (desc, mode, config) => {
+ const element = newElementButtonGameMode(desc);
+ element.addEventListener(`click`, _toggleGameModeHandler(config, mode));
+ return element;
+};
+
+const newLabelProbFlag = (foo, config, desc) => {
+ const label = document.createElement(`label`);
+ label.className += ` ${CLASS_NAME_CHECKBOX_LABEL} col`;
+
+ const checkbox = document.createElement(`input`);
+ checkbox.className += ` ${CLASS_NAME_CHECKBOX}`;
+ checkbox.setAttribute(`type`, `checkbox`);
+ checkbox.addEventListener(`change`, ({ target: { checked } }) => {
+ foo(config, checked);
+ });
+
+ const checkboxDesc = document.createElement(`div`);
+ checkboxDesc.innerText = desc;
+ checkboxDesc.className += ` ${CLASS_NAME_CHECKBOX_DESC}`;
+ appendChild(label, checkbox);
+ appendChild(label, checkboxDesc);
+ return label;
+};
+const newElementCheckboxesProbabilityWithFlagToggles = (config) => {
+ const labelFourCards = newLabelProbFlag(
+ setFlagProbFourCardsOfPlayerConfig,
+ config,
+ `🃏🃏🃏🃏`
+ );
+
+ const labelThreeCards = newLabelProbFlag(
+ setFlagProbThreeCardsOfPlayerConfig,
+ config,
+ `🃏🃏🃏`
+ );
+
+ const groupDesc = document.createElement(`div`);
+ groupDesc.innerText = `Calculate % on: `;
+ groupDesc.className += ` ${CLASS_WRAPPER_PROB_GROUP_DESC} col`;
+
+ const group = document.createElement(`div`);
+
+ group.className += ` ${CLASS_WRAPPER_PROB_CHECKBOXES} row`;
+ group.appendChild(groupDesc);
+ group.appendChild(labelThreeCards);
+ group.appendChild(labelFourCards);
+ return group;
+};
+// Stud Seven Page
diff --git a/scripts/src/elementHelpers/constructorHelpers.js b/scripts/src/elementHelpers/constructorHelpers.js
new file mode 100644
index 00000000..3cb85661
--- /dev/null
+++ b/scripts/src/elementHelpers/constructorHelpers.js
@@ -0,0 +1,96 @@
+// CSS Class Names: Player
+const CLASS_NAME_INPUT = `poker-name-input`;
+const CLASS_NAME_BANNER = `poker-name-banner`;
+const CLASS_WRAPPER_NAME_CONFIG = `poker-wrapper-config-name`;
+const CLASS_NAME_DISPLAY = `poker-name-display`;
+const CLASS_PLAYER_CREDIT = `poker-game-config-credit`;
+// CSS Class Names: Game Config button
+const CLASS_BUTTON_WRAPPER_INPUT = `poker-wrapper-game-modes`;
+const CLASS_BUTTON_GAME_MODE_STUD_SEVEN = `poker-game-mode-stud-seven`;
+const CLASS_GAME_MODE_DISPLAY = `poker-game-mode-display`;
+
+// CSS Class Names: Game Phase Transition Buttons
+const CLASS_BUTTON_START_GAME = `poker-game-config-button-start`;
+const CLASS_NAME_CHECKBOX = `poker-game-config-checkbox`;
+const CLASS_NAME_CHECKBOX_DESC = `poker-game-config-checkbox-desc`;
+const CLASS_NAME_CHECKBOX_LABEL = `poker-game-checkbox-label`;
+const CLASS_WRAPPER_PROB_CHECKBOXES = `poker-game-wrapper-prob-checkboxes-group`;
+const CLASS_WRAPPER_PROB_GROUP_DESC = `poker-game-wrapper-prob-checkboxes-desc`;
+
+// General Helpers
+const _addClassToElement = (element, className) =>
+ (element.className += ` ${className}`);
+const _newElementTextInput = (className) => {
+ const element = document.createElement(`input`);
+ element.setAttribute(`type`, `text`);
+ _addClassToElement(element, className);
+ return element;
+};
+
+const _newElementSlider = (className, min, max) => {
+ const element = document.createElement("input");
+ element.className += ` ${className}`;
+ element.setAttribute(`type`, `range`);
+ element.setAttribute(`step`, 1);
+ element.value = min;
+ element.min = min;
+ element.max = max;
+ element.step = 1;
+ return element;
+};
+
+const _newElementDiv = (className) => {
+ const element = document.createElement(`div`);
+ _addClassToElement(element, className);
+ return element;
+};
+const _newElementButton = (className, desc) => {
+ const element = document.createElement(`button`);
+ element.className += ` ${className}`;
+ element.innerText = `${desc}`;
+ return element;
+};
+
+const _newElementImg = (className, src, alt) => {
+ const element = document.createElement(`img`);
+ element.className += ` ${className}`;
+ element.setAttribute(`src`, src);
+ element.setAttribute(`alt`, alt);
+ return element;
+};
+
+// New Elements
+
+// New Elements: Players
+
+const newElementWrapperNameConfig = () => {
+ const element = _newElementDiv(CLASS_WRAPPER_NAME_CONFIG);
+ element.className += ` row flex-column align-items-center justify-content-center`;
+ return element;
+};
+
+const newElementNameInput = () => {
+ const element = _newElementTextInput(CLASS_NAME_INPUT);
+ element.className += ` col text-center`;
+ return element;
+};
+
+const newElementNameDisplay = () =>
+ _newElementDiv(`${CLASS_NAME_DISPLAY} col text-center`);
+const newElementPlayerCreditDisplay = (config) => {
+ const credit = getMoneyOfPlayerConfig(config);
+ const element = _newElementDiv(` ${CLASS_PLAYER_CREDIT} text-center`);
+ setInnerText(element, `💰 ${credit} `);
+ return element;
+};
+
+// New Elements: Game Mode
+const newElementWrapperButtonGameMode = () =>
+ _newElementDiv(CLASS_BUTTON_WRAPPER_INPUT);
+const newElementButtonGameMode = (desc) =>
+ _newElementButton(CLASS_BUTTON_GAME_MODE_STUD_SEVEN, desc);
+const newElementDisplayGameMode = () => _newElementDiv(CLASS_GAME_MODE_DISPLAY);
+
+// New Elements: Game Transition
+const newElementButtonStartGame = () =>
+ _newElementButton(`${CLASS_BUTTON_START_GAME}`, `START`);
diff --git a/scripts/src/elementHelpers/contentHelpers.js b/scripts/src/elementHelpers/contentHelpers.js
new file mode 100644
index 00000000..9ab60d1b
--- /dev/null
+++ b/scripts/src/elementHelpers/contentHelpers.js
@@ -0,0 +1,82 @@
+// COMMON
+/**
+ *
+ * @param {HTMLElement} element
+ * @param {HTMLElement} child
+ * @returns
+ */
+const appendChild = (element, child) => element.appendChild(child);
+
+/**
+ *
+ * @param {HTMLElement} element
+ * @param {HTMLElement[]} childs
+ * @returns
+ */
+const appendChilds = (element, childs) =>
+ childs.forEach((c) => appendChild(element, c));
+
+const replaceChilds = (element, ...childs) =>
+ element.replaceChildren(...childs);
+const detachAllChildren = (element) => {
+ while (element.lastChild) element.removeChild(element.lastChild);
+};
+
+const setInnerText = (element, text) => (element.innerText = text);
+
+const setElementStyleDisplay = (element, display) =>
+ (element.style.display = display);
+const showElement = (element, display = `flex`) =>
+ setElementStyleDisplay(element, display);
+const hideElement = (element) => setElementStyleDisplay(element, `none`);
+
+// GameConfig
+
+const updateNameDisplayInGameConfig = (playerConfig) => {
+ const elementDisplay = getElementNameDisplayOfPlayerConfig(playerConfig);
+ const playerName = getPlayerNameOfPlayerConfig(playerConfig);
+ setInnerText(elementDisplay, playerName);
+};
+const clearPlayerConfigPage = (config) => {
+ const elementRoot = getElementRootOfPlayerConfig(config);
+ detachAllChildren(elementRoot);
+};
+const _clearPlayerConfigDisplayAndGoToGame = (config) => {
+ clearPlayerConfigPage(config);
+
+ const core = getCoreOfPlayerConfig(config);
+ const gameMode = getGameModeFromPlayerConfig(config);
+ const flags = getFlagsOfPlayerConfig(config);
+ goToGame(core, flags, gameMode);
+};
+
+const hideStartButton = (config) => {
+ const element = getElementButtonStartOfPlayerConfig(config);
+ hideElement(element);
+};
+
+const showStartButton = (config) => {
+ const element = getElementButtonStartOfPlayerConfig(config);
+ showElement(element);
+};
+
+const toggleDisabilityClick = (element, is) => {
+ if (element) {
+ element.disabled = is;
+ }
+};
+const _disableClick = (element) => toggleDisabilityClick(element, true);
+const _enableClick = (element) => toggleDisabilityClick(element, false);
+
+const updateDisablitiyGameMode = (config) => {
+ const activeElementGameModeButton =
+ getElementGameModeActiveOfPlayerConfig(config);
+ _enableClick(activeElementGameModeButton);
+ const currentMode = getGameModeFromPlayerConfig(config);
+ const elementCurrentMode = getElementGameModeOfPlayerConfig(
+ config,
+ currentMode
+ );
+ _disableClick(elementCurrentMode);
+ setElementGameModeActiveOfPlayerConfig(config, elementCurrentMode);
+};
diff --git a/scripts/src/elementHelpers/eventHelpers.js b/scripts/src/elementHelpers/eventHelpers.js
new file mode 100644
index 00000000..e69de29b
diff --git a/scripts/src/elementHelpers/styleHelpers.js b/scripts/src/elementHelpers/styleHelpers.js
new file mode 100644
index 00000000..e69de29b
diff --git a/scripts/src/moment/inGame/gameStudSeven.js b/scripts/src/moment/inGame/gameStudSeven.js
new file mode 100644
index 00000000..85e3d4ca
--- /dev/null
+++ b/scripts/src/moment/inGame/gameStudSeven.js
@@ -0,0 +1,456 @@
+const CLASS_NAME_CARD_HOLDER = `card-holder`;
+const CLASS_NAME_CARD = `card`;
+const CLASS_NAME_CARD_IMG = `card-img`;
+const CLASS_NAME_HAND = `poker-hand`;
+const URL_CARD_FACE_DOWN = `static/img/cards/JOKER-RED.png`;
+const CLASS_NAME_ACTION_AREA = `action-area`;
+const CLASS_NAME_BUTTON_BET = `action-bet-button`;
+const CLASS_NAME_SLIDER_BET = `slider-bet`;
+const CLASS_NAME_BUSTED = `desc busted`;
+
+const PHASE_BET_INITIAL = 0;
+const PHASE_BET_ROUND_ONE = 1;
+const PHASE_BET_ROUND_TWO = 2;
+const PHASE_BET_ROUND_THREE = 3;
+
+/**
+ *
+ * @returns {CardHolder}
+ */
+const newElementCardHolder = () => _newElementDiv(CLASS_NAME_CARD_HOLDER);
+const newElementCard = (src, alt = `A card element`) =>
+ _newElementImg(CLASS_NAME_CARD_IMG, src, alt);
+const newElementCardBack = () =>
+ _newElementImg(
+ CLASS_NAME_CARD_IMG,
+ URL_CARD_FACE_DOWN,
+ `A card element representing back of the card`
+ );
+
+const newElementCardEmpty = () => newElementCard(null, ``);
+
+const newElementStagingCard = () => {
+ const elementStagingCardImg = newElementCardEmpty(null);
+ elementStagingCardImg.style.borderColor = `green`;
+
+ return elementStagingCardImg;
+};
+
+const newElementHand = () => _newElementDiv(`${CLASS_NAME_HAND} row`);
+const newElementActionArea = () =>
+ _newElementDiv(`${CLASS_NAME_ACTION_AREA} flex-column`);
+const newElementBusted = () => {
+ const element = _newElementDiv(CLASS_NAME_BUSTED);
+ element.innerText = `No more 🍯`;
+ return element;
+};
+const cardToImgUrl = (card) => {
+ const ordinal = getInPlayCardOrdinal(card).toString().padStart(2, "0");
+ const suit = getInPlayCardSuit(card);
+ return `static/img/cards/${ordinal}-${suit}.png`;
+};
+const newElementCoin = () => {
+ return _newElementDiv(`dummy-coin`);
+};
+const newElementBetButton = () => {
+ const element = _newElementButton(CLASS_NAME_BUTTON_BET, `BET`);
+ return element;
+};
+const newElementSliderBet = (min, max) => {
+ return _newElementSlider(CLASS_NAME_SLIDER_BET, min, max);
+};
+
+const ElementDistribution = () => {};
+
+const newGameStudSeven = (core, flags) => {
+ const handSizeToSettle = 7;
+ const hand = newHand();
+ const deck = newStandardDeck();
+
+ const PROBABILTY_FLAG = {
+ FLOP: flags.probability.threeCards,
+ TURN: flags.probability.fourCards,
+ };
+
+ console.log(PROBABILTY_FLAG);
+ let currentCardPosition = 0;
+
+ let initialBet = null;
+
+ let activeBet = 0;
+ let pot = 0;
+ const addToPot = (credit) => (pot += credit);
+
+ const getPot = () => pot;
+ const elePot = document.createElement(`div`);
+ elePot.className += ` money--`;
+ const updatePotDisplay = () => {
+ elePot.innerText = `Pot: $$${getPot()}`;
+ };
+
+ const elePlayerCredit = document.createElement(`div`);
+ elePlayerCredit.className += ` money--`;
+ const updatePlayerCreditDisplay = () => {
+ elePlayerCredit.innerText = `You Have: $$${getPlayerCreditOfCore(core)}`;
+ };
+
+ const eleInitBet = document.createElement(`div`);
+ eleInitBet.className += ` money--`;
+ const updateInitBetDisplay = () => {
+ eleInitBet.innerText = `Initial Bet: $$${
+ initialBet === null ? `-` : initialBet
+ }`;
+ };
+ const eleMoneys = document.createElement(`div`);
+ eleMoneys.className += ` moneys row`;
+ const inGameHeader = document.createElement(`div`);
+ inGameHeader.className += ` in-game-header`;
+ updatePotDisplay();
+ updatePlayerCreditDisplay();
+ updateInitBetDisplay();
+ eleMoneys.appendChild(elePlayerCredit);
+ eleMoneys.appendChild(elePot);
+ eleMoneys.appendChild(eleInitBet);
+
+ const elementNameDisplay = newElementNameDisplay();
+ elementNameDisplay.innerText = getPlayerNameOfCore(core);
+
+ inGameHeader.appendChild(elementNameDisplay);
+ inGameHeader.appendChild(eleMoneys);
+ let phase = PHASE_BET_INITIAL;
+
+ const togglePhase = () => (phase += 1); // do this after current phase wants to end
+ /**
+ *
+ * @param {number} pos
+ * @returns {CardHolder}
+ */
+ const getElementCardHolderByPosition = (pos) =>
+ elements.hand.cardHolders[pos];
+
+ const getElementSliderBet = () => elements.action.bet.slider;
+ const updateDisplayOfCardHolder = (pos, card) => {
+ const elementCardHolder = getElementCardHolderByPosition(pos);
+ const imgSrc = cardToImgUrl(card);
+ const elementCardImg = newElementCard(imgSrc);
+ elementCardHolder.replaceChildren(elementCardImg);
+ };
+
+ const drawCardAndUpdateDisplay = () => {
+ const card = drawCard(deck);
+ addInPlayCardToHand(hand, card);
+ const lastIndex = getHandSize(hand) - 1; // this is abit hacky, assumes added card is in the last position.
+ updateDisplayOfCardHolder(lastIndex, card);
+ };
+
+ /** @typedef {CardHolder[]} cardHolders*/
+ const elementCardHolders = [];
+
+ for (let i = 0; i < handSizeToSettle; i += 1) {
+ const elementHolder = newElementCardHolder();
+ elementHolder.appendChild(newElementCardEmpty());
+ elementCardHolders.push(elementHolder);
+ }
+ replaceChilds(elementCardHolders[0], newElementStagingCard());
+ replaceChilds(elementCardHolders[1], newElementStagingCard());
+
+ const wrapper = newElementHand();
+ appendChilds(wrapper, elementCardHolders);
+
+ const elementActionArea = newElementActionArea();
+
+ const elementButtonBar = _newElementDiv(
+ `button-bar row justify-content-center`
+ );
+ const elementButtonBet = newElementBetButton();
+ const elementBetValue = document.createElement(`div`);
+ elementBetValue.innerText = activeBet;
+ const isBusted = () => getPlayerCreditOfCore(core) <= 0;
+
+ const transferAndUpdateDisplayCredit = (activeBet) => {
+ addToPot(activeBet);
+ minusPlayerCreditOfCore(core, activeBet);
+ updatePotDisplay();
+ updatePlayerCreditDisplay();
+ };
+ elementButtonBet.addEventListener(`click`, () => {
+ // on bet, immediately draw card first, then assess the actions to be taken.
+ setAndDisplayInitialBetIfNull(activeBet);
+ transferAndUpdateDisplayCredit(activeBet);
+
+ activeBet = 0; // reset
+ elementBetValue.innerText = activeBet;
+
+ const slider = getElementSliderBet();
+ slider.max = getBetLimit();
+ slider.value = activeBet;
+
+ if (isBusted()) {
+ elementBetValue.replaceChildren(newElementBusted());
+ slider.parentNode?.removeChild(slider);
+ } else {
+ }
+
+ refreshBetButtonVisibility();
+
+ if (phase === PHASE_BET_INITIAL) {
+ drawCardAndUpdateDisplay();
+ drawCardAndUpdateDisplay();
+ } else if (phase === PHASE_BET_ROUND_ONE) {
+ drawCardAndUpdateDisplay();
+
+ if (PROBABILTY_FLAG.FLOP === true) {
+ const elementLoading = document.createElement("div");
+ elementLoading.innerText = "Please wait for %";
+ elementActionArea.replaceChildren(elementLoading);
+ setTimeout(() => {
+ const scoringDistribution =
+ calcScoringDistributionSevenStudGivenSomeRevealedCards(hand, deck);
+ const scoringDistributionInPercentage = getPMF(scoringDistribution);
+ console.log(scoringDistributionInPercentage);
+ if (isBusted()) {
+ elementActionArea.replaceChildren(
+ elementBetValue,
+ elementButtonBet
+ );
+ } else {
+ elementActionArea.replaceChildren(
+ elementSliderBet,
+ elementBetValue,
+ elementButtonBet
+ );
+ }
+ }, 200);
+ }
+ replaceChilds(getElementCardHolderByPosition(3), newElementStagingCard());
+ } else if (phase === PHASE_BET_ROUND_TWO) {
+ drawCardAndUpdateDisplay();
+ if (PROBABILTY_FLAG.TURN === true) {
+ const elementLoading = document.createElement("div");
+ elementLoading.innerText = "Please wait for %";
+ elementActionArea.replaceChildren(elementLoading);
+ setTimeout(() => {
+ const scoringDistribution =
+ calcScoringDistributionSevenStudGivenSomeRevealedCards(hand, deck);
+ console.table(scoringDistribution);
+ const pmf = getPMF(scoringDistribution);
+
+ const [elementXValue, elementPmfTable] = newDistributionTable(pmf);
+ console.log(scoringDistribution);
+ if (isBusted()) {
+ elementActionArea.replaceChildren(
+ elementBetValue,
+ elementButtonBet
+ );
+ } else {
+ elementActionArea.replaceChildren(
+ elementSliderBet,
+ elementBetValue,
+ elementButtonBet
+ );
+ }
+ // elementActionArea.appendChild(elementXValue); // Please check payout table
+ elementActionArea.appendChild(elementPmfTable);
+ }, 200);
+ }
+ } else if (phase === PHASE_BET_ROUND_THREE) {
+ drawCardAndUpdateDisplay();
+ drawCardAndUpdateDisplay();
+ drawCardAndUpdateDisplay();
+
+ // settle score here, my hand will have seven cards
+ const { bestScore, simpleBestHand } =
+ ______WARN_getHandSimpleBestCombination(hand);
+
+ console.log(getHandAsString(simpleBestHand));
+ console.log(bestScore);
+
+ const netPayout = getPayoutOfScoringType(bestScore, pot);
+ addPlayerCreditOfCore(core, netPayout);
+ const elementBestScore = document.createElement(`div`);
+ elementBestScore.className += ` best-score--`;
+ elementBestScore.innerText = `Your hand: ${getNiceStringOfScoringType(
+ bestScore
+ )}, Base Payout: ${getBasePayoutOfScoringType(
+ bestScore
+ )}, Payout: ${netPayout}, Player Credit After Settlement: ${getPlayerCreditOfCore(
+ core
+ )}`;
+
+ detachAllChildren(elementActionArea);
+
+ const buttonRestart = document.createElement(`button`);
+ buttonRestart.className += ` button-restart`;
+ buttonRestart.innerText = "PLAY AGAIN";
+
+ buttonRestart.addEventListener(`click`, () => {
+ const root = getElementRootOfCore(core);
+ detachAllChildren(root);
+ goToGameStudSeven(core, flags);
+ });
+ elementActionArea.appendChild(elementBestScore);
+ elementActionArea.appendChild(buttonRestart);
+ }
+
+ // display
+
+ if (phase === PHASE_BET_INITIAL) {
+ replaceChilds(getElementCardHolderByPosition(2), newElementStagingCard());
+ } else if (phase === PHASE_BET_ROUND_ONE) {
+ replaceChilds(getElementCardHolderByPosition(3), newElementStagingCard());
+ } else if (phase === PHASE_BET_ROUND_TWO) {
+ replaceChilds(getElementCardHolderByPosition(4), newElementStagingCard());
+ replaceChilds(getElementCardHolderByPosition(5), newElementStagingCard());
+ replaceChilds(getElementCardHolderByPosition(6), newElementStagingCard());
+ } else if (phase === PHASE_BET_ROUND_THREE) {
+ }
+
+ togglePhase();
+ });
+
+ const isInitialBetSet = () => initialBet !== null;
+ const setAndDisplayInitialBetIfNull = (activeBet) => {
+ if (initialBet === null) {
+ initialBet = activeBet;
+ updateInitBetDisplay();
+ }
+ };
+ const getBetLimit = () => {
+ if (isInitialBetSet()) {
+ return Math.min(initialBet, getPlayerCreditOfCore(core));
+ }
+ return 5;
+ };
+ const refreshBetButtonVisibility = () => {
+ if (activeBet > 0) {
+ if (isBusted()) {
+ setInnerText(elementButtonBet, `CHECK`);
+ } else {
+ if (PHASE_BET_INITIAL === phase) {
+ setInnerText(elementButtonBet, `INITIAL BET`);
+ } else {
+ setInnerText(elementButtonBet, `BET`);
+ }
+ }
+ showElement(elementButtonBet);
+ } else {
+ if (isInitialBetSet()) {
+ setInnerText(elementButtonBet, `CHECK`);
+ } else {
+ hideElement(elementButtonBet);
+ }
+ }
+ };
+ const elementSliderBet = newElementSliderBet(0, getBetLimit());
+
+ elementSliderBet.addEventListener(`input`, ({ target: { value } }) => {
+ activeBet = Number(value);
+ elementBetValue.innerText = activeBet;
+ refreshBetButtonVisibility();
+ });
+
+ elementActionArea.appendChild(elementSliderBet);
+ elementActionArea.appendChild(elementBetValue);
+
+ elementButtonBar.appendChild(elementButtonBet);
+
+ elementActionArea.appendChild(elementButtonBar);
+
+ const elements = {
+ header: inGameHeader,
+ hand: { wrapper, cardHolders: elementCardHolders },
+ action: {
+ area: elementActionArea,
+ coin: newElementCoin(),
+ bet: { button: elementButtonBet, slider: elementSliderBet },
+ },
+ };
+ refreshBetButtonVisibility();
+ return {
+ getElementRoot: () => getElementRootOfCore(core),
+ getPlayerName: () => getPlayerNameOfCore(core),
+ getPlayerCredit: () => getPlayerCreditOfCore(core),
+ minusPlayerCredit: (credit) => minusPlayerCreditOfCore(core, credit),
+ addPlayerCredit: (credit) => addPlayerCreditOfCore(core, credit),
+ getHand: () => hand,
+ getCardByPosition: (pos) => hand[pos],
+ getCardPosition: () => currentCardPosition,
+ incrementCardPosition: () => (currentCardPosition += 1),
+
+ getElementHand: () => elements.hand.wrapper,
+ getElementActionArea: () => elements.action.area,
+ getElementHeader: () => elements.header,
+ };
+};
+
+const studSevenGoPaint = (game) => {
+ const root = game.getElementRoot();
+
+ const childHeader = game.getElementHeader();
+ const childHand = game.getElementHand();
+ const childAction = game.getElementActionArea();
+
+ appendChilds(root, [childHeader, childHand, childAction]);
+};
+
+const goToGameStudSeven = (core, flags) => {
+ const game = newGameStudSeven(core, flags);
+ studSevenGoPaint(game);
+};
+
+const newDistributionTable = (pmf) => {
+ const rows = [
+ { text: "Hand", key: "hand" },
+ { text: "%", key: "p" },
+ { text: "Payout", key: "payoutRatio" },
+ ];
+
+ const arrayData = Object.entries(pmf);
+
+ const expectedValue = arrayData.reduce((sum, [key, { payoutRatio, p }]) => {
+ return sum + payoutRatio * p;
+ }, 0);
+
+ const elementXValue = document.createElement(`div`);
+
+ elementXValue.className += ` x-val--`;
+ elementXValue.innerText = `xPayout: ${(expectedValue / 100).toFixed(2)}`;
+
+ // TABLE
+ const length = arrayData.length;
+
+ const eTable = document.createElement(`table`);
+
+ const eHead = document.createElement(`thead`);
+
+ const eHeaderRow = document.createElement(`tr`);
+
+ for (const { text: colHeaderVal } of rows) {
+ const th = document.createElement(`th`);
+ th.innerText = colHeaderVal;
+ eHeaderRow.appendChild(th);
+ }
+ eHead.appendChild(eHeaderRow);
+
+ const eBody = document.createElement(`tbody`);
+ for (let i = 0; i < length; i += 1) {
+ const [key, rv] = arrayData[i];
+
+ const { occurrence } = rv;
+ if (occurrence > 0) {
+ const eRow = document.createElement(`tr`);
+ for (const { key } of rows) {
+ const td = document.createElement(`td`);
+ td.innerText = rv[key];
+ eRow.appendChild(td);
+ }
+
+ eBody.appendChild(eRow);
+ }
+ }
+
+ eTable.appendChild(eHead);
+ eTable.appendChild(eBody);
+
+ return [elementXValue, eTable];
+};
diff --git a/scripts/src/moment/inGame/index.js b/scripts/src/moment/inGame/index.js
new file mode 100644
index 00000000..9043925b
--- /dev/null
+++ b/scripts/src/moment/inGame/index.js
@@ -0,0 +1,5 @@
+const goToGame = (core, flags, mode) => {
+ if (mode === GAME_MODE_STUD_SEVEN) {
+ goToGameStudSeven(core, flags);
+ }
+};
diff --git a/scripts/src/moment/outGame/playerConfig.js b/scripts/src/moment/outGame/playerConfig.js
new file mode 100644
index 00000000..c6b0d3bc
--- /dev/null
+++ b/scripts/src/moment/outGame/playerConfig.js
@@ -0,0 +1,309 @@
+const GAME_MODE_STUD_SEVEN = `stud-seven`;
+
+const GAME_MODES = [GAME_MODE_STUD_SEVEN];
+
+const getGameModeFromPlayerConfig = (config) => config.gameMode;
+
+const setGameModeForPlayerConfig = (config, mode) => (config.gameMode = mode);
+
+const getCoreOfPlayerConfig = (config) => config.core;
+const getFlagsOfPlayerConfig = (config) => config.flags;
+const getFlagProbThreeCardsOfPlayerConfig = (config) =>
+ config.flags.probability.threeCards;
+const getFlagProbFourCardsOfPlayerConfig = (config) =>
+ config.flags.probability.fourCards;
+
+const setFlagProbThreeCardsOfPlayerConfig = (config, is) =>
+ (config.flags.probability.threeCards = is);
+const setFlagProbFourCardsOfPlayerConfig = (config, is) =>
+ (config.flags.probability.fourCards = is);
+
+const getPlayerOfPlayerConfig = (config) => {
+ const core = getCoreOfPlayerConfig(config);
+ return getPlayerOfCore(core);
+};
+
+const getPlayerNameOfPlayerConfig = (config) => {
+ const player = getPlayerOfPlayerConfig(config);
+ return getPlayerName(player);
+};
+
+const setPlayerNameOfPlayerConfig = (config, name) => {
+ const player = getPlayerOfPlayerConfig(config);
+ setPlayerName(player, name);
+};
+const _getElementProbFlagsOfPlayerConfig = (playerConfig) =>
+ playerConfig.elements.probFlags;
+
+const _getElementGameGroupOfPlayerConfig = (playerConfig) =>
+ playerConfig.elements.gameMode;
+const getElementGameModeWrapperOfPlayerConfig = (config) =>
+ _getElementGameGroupOfPlayerConfig(config).wrapper;
+const getElementGameModeOfPlayerConfig = (config, mode) =>
+ _getElementGameGroupOfPlayerConfig(config).modes[mode];
+const getElementGameDisplayOfPlayerConfig = (config) =>
+ _getElementGameGroupOfPlayerConfig(config).display;
+const getElementProbWrapperOfPlayerConfig = (playerConfig) =>
+ _getElementProbFlagsOfPlayerConfig(playerConfig).wrapper;
+
+const setElementGameModeActiveOfPlayerConfig = (config, element) =>
+ (_getElementGameGroupOfPlayerConfig(config).active = element);
+const getElementGameModeActiveOfPlayerConfig = (config) =>
+ _getElementGameGroupOfPlayerConfig(config).active;
+const getElementButtonsGameModeOfPlayerConfigAsObject = (config) =>
+ _getElementGameGroupOfPlayerConfig(config).modes;
+const getElementButtonsGameModeOfPlayerConfigAsList = (config) =>
+ Object.entries(getElementButtonsGameModeOfPlayerConfigAsObject(config));
+const setElementGameModeWrapperOfPlayerConfig = (playerConfig, wrapper) =>
+ (_getElementGameGroupOfPlayerConfig(playerConfig).wrapper = wrapper);
+const setElementGameModeOfPlayerConfig = (playerConfig, button, mode) =>
+ (_getElementGameGroupOfPlayerConfig(playerConfig).modes[mode] = button);
+const setElementGameDisplayOfPlayerConfig = (playerConfig, display) => {
+ _getElementGameGroupOfPlayerConfig(playerConfig).display = display;
+};
+
+const setElementProbWrapperOfPlayerConfig = (playerConfig, element) =>
+ (_getElementProbFlagsOfPlayerConfig(playerConfig).wrapper = element);
+
+const getElementGameModeCurrentOfPlayerConfig = (config) =>
+ _getElementGameGroupOfPlayerConfig(config).current;
+const setElementGameModeCurrentOfPlayerConfig = (config) =>
+ (_getElementGameGroupOfPlayerConfig(config, element).current = element);
+
+const setElementButtonStartOfPlayConfig = (playerConfig, element) =>
+ (playerConfig.elements.start = element);
+
+const getElementPlayerOfPlayerConfig = (playerConfig) =>
+ playerConfig.elements.player;
+/**
+ *
+ * @param {PlayerConfig} playerConfig
+ * @returns {Object.} the group of elements related to player name
+ */
+const getElementPlayerGroupOfPlayerConfig = (playerConfig) =>
+ getElementPlayerOfPlayerConfig(playerConfig).name;
+const getElementNameBannerOfPlayerConfig = (playerConfig) =>
+ getElementPlayerOfPlayerConfig(playerConfig).name.banner;
+/**
+ *
+ * @param {PlayerConfig} config
+ * @returns {HTMLElement} element which wraps the name elements
+ */
+const getElementPlayerWrapperOfPlayerConfig = (config) =>
+ getElementPlayerGroupOfPlayerConfig(config).wrapper;
+
+/**
+ *
+ * @param {PlayerConfig} config
+ * @returns {HTMLElement} element which allows player input
+ */
+const getElementNameInputOfPlayerConfig = (config) =>
+ getElementPlayerGroupOfPlayerConfig(config).input;
+
+/**
+ *
+ * @param {PlayerConfig} config
+ * @returns {HTMLElement} element which displays the player name
+ */
+const getElementNameDisplayOfPlayerConfig = (config) =>
+ getElementPlayerGroupOfPlayerConfig(config).display;
+
+const getElementPlayerCreditOfPlayerConfig = (config) =>
+ getElementPlayerGroupOfPlayerConfig(config).credit;
+
+const getElementButtonStartOfPlayerConfig = (config) => config.elements.start;
+/**
+ * set wrapper element for the group of elements related to player name
+ * @param {PlayerConfig} config
+ * @param {HTMLElement} element
+ * @returns
+ */
+const setElementNameWrapperOfPlayerConfig = (config, element) =>
+ (getElementPlayerGroupOfPlayerConfig(config).wrapper = element);
+
+/**
+ * set input element for the group of elements related to player name
+ * @param {PlayerConfig} config
+ * @param {HTMLElement} element
+ * @returns
+ */
+const setElementNameInputOfPlayerConfig = (config, element) =>
+ (getElementPlayerGroupOfPlayerConfig(config).input = element);
+const setElementPlayerCreditOfPlayerConfig = (config, element) =>
+ (getElementPlayerGroupOfPlayerConfig(config).credit = element);
+
+/**
+ * set display element for the group of elements related to player name
+ * @param {PlayerConfig} config
+ * @param {HTMLElement} element
+ * @returns
+ */
+const setElementNameDisplayOfPlayerConfig = (config, element) =>
+ (getElementPlayerGroupOfPlayerConfig(config).display = element);
+
+const setElementNameBannerOfPlayerConfig = (config, element) =>
+ (getElementPlayerGroupOfPlayerConfig(config).banner = element);
+const getMoneyOfPlayerConfig = (config) => {
+ const player = getPlayerOfPlayerConfig(config);
+ return getPlayerCredit(player);
+};
+
+// CONSTRUCTOR
+
+/**
+ *
+ * @param {*} core
+ * @returns {PlayerConfig} a plain PlayerConfig object
+ */
+const _playerConfigStruct = (core) => {
+ return {
+ core,
+ gameMode: null,
+ flags: { probability: { threeCards: false, fourCards: false } },
+ elements: {
+ player: {
+ name: { wrapper: null, input: null, display: null },
+ credit: { display: null },
+ },
+ probFlags: {
+ wrapper: null,
+ },
+ gameMode: {
+ wrapper: null,
+ modes: { [GAME_MODE_STUD_SEVEN]: null },
+ current: null,
+ },
+ start: { button: null },
+ },
+ };
+};
+
+const getElementRootOfPlayerConfig = (playerConfig) => {
+ const thisCore = getCoreOfPlayerConfig(playerConfig);
+ const elementRoot = getElementRootOfCore(thisCore);
+ return elementRoot;
+};
+
+/**
+ * Create default HTML elements with default props and attach event listeners.
+ * @param {PlayerConfig} playerConfig
+ * @returns {PlayerConfig}
+ */
+const _playerConfigReadyInteractivePaint = (playerConfig) => {
+ // Elements: Player Info : Name
+ const elementPlayerWrapper = newElementWrapperNameConfig();
+ setElementNameWrapperOfPlayerConfig(playerConfig, elementPlayerWrapper);
+
+ const elementNameBanner = _newElementDiv(CLASS_NAME_BANNER);
+ elementNameBanner.className += ` row`;
+ elementNameBanner.innerText = `Your name, please 🙂`;
+ setElementNameBannerOfPlayerConfig(playerConfig, elementNameBanner);
+
+ const elementNameDisplay = newElementNameDisplay(playerConfig);
+ setElementNameDisplayOfPlayerConfig(playerConfig, elementNameDisplay);
+
+ const elementNameInput =
+ newElementNameInputWhichUpdatesPlayerNameOnInput(playerConfig);
+ setElementNameInputOfPlayerConfig(playerConfig, elementNameInput);
+
+ elementNameInput.value = getPlayerNameOfPlayerConfig(playerConfig);
+ updateNameDisplayInGameConfig(playerConfig);
+
+ // Elements: Player Info : Money
+
+ const elementCredit = newElementPlayerCreditDisplay(playerConfig);
+ setElementPlayerCreditOfPlayerConfig(playerConfig, elementCredit);
+
+ // Elements: Game Mode
+ const elementButtonGameModeWrapper = newElementWrapperButtonGameMode();
+
+ for (const mode of GAME_MODES) {
+ const elementButtonGameMode = newElementButtonGameModeAndSetToggle(
+ `Stud Seven`,
+ mode,
+ playerConfig
+ );
+ setElementGameModeOfPlayerConfig(playerConfig, elementButtonGameMode, mode);
+ }
+
+ const elementPropCheckboxesWrapper =
+ newElementCheckboxesProbabilityWithFlagToggles(playerConfig);
+ setElementProbWrapperOfPlayerConfig(
+ playerConfig,
+ elementPropCheckboxesWrapper
+ );
+
+ setElementGameModeWrapperOfPlayerConfig(
+ playerConfig,
+ elementButtonGameModeWrapper
+ );
+
+ // Start button
+ const elementButtonStart = newElementButtonStartGameWithStart(playerConfig);
+ setElementButtonStartOfPlayConfig(playerConfig, elementButtonStart);
+ return playerConfig;
+};
+
+// Tree Building
+const _playerConfigGoPaint = (playerConfig) => {
+ const elementRoot = getElementRootOfPlayerConfig(playerConfig);
+
+ const elementPlayerWrapper =
+ getElementPlayerWrapperOfPlayerConfig(playerConfig);
+
+ const elementNameBanner = getElementNameBannerOfPlayerConfig(playerConfig);
+ const elementNameInput = getElementNameInputOfPlayerConfig(playerConfig);
+ const elementNameDisplay = getElementNameDisplayOfPlayerConfig(playerConfig);
+
+ const elementCredit = getElementPlayerCreditOfPlayerConfig(playerConfig);
+
+ appendChilds(elementPlayerWrapper, [
+ elementNameBanner,
+ elementNameInput,
+ elementNameDisplay,
+ elementCredit,
+ ]);
+
+ const elementWrapperGameMode =
+ getElementGameModeWrapperOfPlayerConfig(playerConfig);
+
+ const elementGameModeButtons =
+ getElementButtonsGameModeOfPlayerConfigAsList(playerConfig);
+
+ const buttons = [];
+ for (const [_, button] of elementGameModeButtons) {
+ buttons.push(button);
+ }
+ appendChilds(elementWrapperGameMode, buttons);
+
+ const elementProbCheckboxes =
+ getElementProbWrapperOfPlayerConfig(playerConfig);
+ const elementStartButton = getElementButtonStartOfPlayerConfig(playerConfig);
+
+ appendChilds(elementRoot, [
+ elementPlayerWrapper,
+ elementWrapperGameMode,
+ elementProbCheckboxes,
+ elementStartButton,
+ ]);
+};
+
+const newPlayerConfig = (core) => _playerConfigStruct(core);
+
+/**
+ *
+ * @param {Core} core
+ */
+const goToPlayerConfigPage = (core) => {
+ // data structure
+ const playerConfig = newPlayerConfig(core);
+
+ // ui
+ _playerConfigReadyInteractivePaint(playerConfig);
+
+ // defaults
+ setGameModeAndUpdateDisability(playerConfig, GAME_MODE_STUD_SEVEN);
+
+ // ui
+ _playerConfigGoPaint(playerConfig);
+};
diff --git a/scripts/src/player.js b/scripts/src/player.js
new file mode 100644
index 00000000..7b3686f7
--- /dev/null
+++ b/scripts/src/player.js
@@ -0,0 +1,76 @@
+const DEFAULT_PLAYER_NAME = "PLAYER 1";
+const DEFAULT_PLAYER_CREDIT = 100;
+
+/**
+ * @typedef {Object} Player
+ * @property {Hand} hand
+ * @property {string} name
+ * @property {number} credit
+ */
+
+/**
+ *
+ * @param {Player} player
+ * @returns
+ */
+const getPlayerCredit = (player) => player.credit;
+
+const minusPlayerCredit = (player, credit) => (player.credit -= credit);
+const addPlayerCredit = (player, credit) => (player.credit += credit);
+/**
+ *
+ * @param {Player} player
+ * @returns
+ */
+const getPlayerHand = (player) => player.hand;
+/**
+ *
+ * @param {Player} player
+ * @returns
+ */
+const getPlayerName = (player) => player.name;
+
+/**
+ *
+ * @param {Player} player
+ * @returns
+ */
+const setPlayerCredit = (player, credit) => (player.credit = credit);
+/**
+ *
+ * @param {Player} player
+ * @returns
+ */
+const setPlayerHand = (player, hand) => (player.hand = hand);
+/**
+ *
+ * @param {Player} player
+ * @returns
+ */
+const setPlayerName = (player, name) => (player.name = name);
+
+// CONSTRUCTOR
+
+/**
+ *
+ * @returns {Player} A plain Player object
+ */
+const _playerStruct = () => {
+ return {
+ name: null,
+ credit: null,
+ };
+};
+
+/**
+ *
+ * @param {string} name
+ * @param {number} credit
+ * @returns {Player}
+ */
+const newPlayer = (name, credit) => {
+ const player = _playerStruct();
+ setPlayerName(player, name || DEFAULT_PLAYER_NAME);
+ setPlayerCredit(player, credit || DEFAULT_PLAYER_CREDIT);
+ return player;
+};
diff --git a/scripts/src/scoring.js b/scripts/src/scoring.js
new file mode 100644
index 00000000..afe8407a
--- /dev/null
+++ b/scripts/src/scoring.js
@@ -0,0 +1,603 @@
+const SCORING = {
+ STRAIGHT_FLUSH: `SCORING_TYPE_STRAIGHT_FLUSH`,
+ FOUR_OF_A_KIND: `SCORING_TYPE_FOOK`,
+ FULL_HOUSE: `SCORING_TYPE_FULL_HOUSE`,
+ FLUSH: `SCORING_TYPE_FLUSH`,
+ STRAIGHTS: `SCORING_TYPE_STRAIGHTS`,
+ TRIPS: `SCORING_TYPE_TRIPS`,
+ DOUBLE: `SCORING_TYPE_DOUBLE`,
+ PAIR: `SCORING_TYPE_PAIR`,
+ JOB: `SCORING_TYPE_JOB`, // Jacks Or Better
+ HIGH: `SCORING_TYPE_HIGH`,
+ SIZE_MISMATCH: `SCORING_SIZE_MISMATCH`,
+ UNKNOWN: `SCORING_TYPE_UNKNOWN`,
+};
+
+const getRankOfScoringType = (scoringType) => {
+ switch (scoringType) {
+ case SCORING.STRAIGHT_FLUSH:
+ return 15;
+ case SCORING.FOUR_OF_A_KIND:
+ return 14;
+ case SCORING.FULL_HOUSE:
+ return 13;
+ case SCORING.FLUSH:
+ return 12;
+ case SCORING.STRAIGHTS:
+ return 11;
+ case SCORING.TRIPS:
+ return 10;
+ case SCORING.DOUBLE:
+ return 9;
+ case SCORING.PAIR:
+ return 8;
+ case SCORING.JOB:
+ return 7;
+ case SCORING.HIGH:
+ return 6;
+ default:
+ return -1;
+ }
+};
+
+const getNiceStringOfScoringType = (scoringType) => {
+ switch (scoringType) {
+ case SCORING.STRAIGHT_FLUSH:
+ return `STRAIGHT FLUSH`;
+ case SCORING.FOUR_OF_A_KIND:
+ return `FOUR OF A KIND`;
+ case SCORING.FULL_HOUSE:
+ return `FULL HOUSE`;
+ case SCORING.FLUSH:
+ return `FLUSH`;
+ case SCORING.STRAIGHTS:
+ return `STRAIGHTS`;
+ case SCORING.TRIPS:
+ return `TRIPS`;
+ case SCORING.DOUBLE:
+ return `DOUBLE`;
+ case SCORING.PAIR:
+ return `PAIR`;
+ case SCORING.JOB:
+ return `> JACKS`;
+ case SCORING.HIGH:
+ return `HIGH`;
+ default:
+ return -1;
+ }
+};
+
+const getBasePayoutOfScoringType = (scoringType, pot = 1) => {
+ switch (scoringType) {
+ case SCORING.STRAIGHT_FLUSH:
+ return 51;
+ case SCORING.FOUR_OF_A_KIND:
+ return 15;
+ case SCORING.FULL_HOUSE:
+ return 4;
+ case SCORING.FLUSH:
+ return 3;
+ case SCORING.STRAIGHTS:
+ return 2;
+ case SCORING.TRIPS:
+ return 1;
+ default:
+ return 0;
+ }
+};
+
+const getPayoutOfScoringType = (scoringType, pot = 1) => {
+ return getBasePayoutOfScoringType(scoringType) * pot;
+};
+
+/**
+ *
+ * @param {InPlayCard} card
+ */
+const getInPlayCardSuitAsRank = (card) => {
+ const color = getInPlayCardSuit(card);
+ switch (color) {
+ case CARD_SUITS.HEART:
+ return 3;
+ case CARD_SUITS.CLUB:
+ return 1;
+ case CARD_SUITS.SPADE:
+ return 4;
+ case CARD_SUITS.DIAMOND:
+ return 2;
+ }
+ throw new Error(`Undefined rank`);
+};
+
+const getPrettiedHand = (hand) => {
+ const scoreType = getScoreType(hand);
+ if (scoreType === SCORING.STRAIGHT_FLUSH) {
+ return newAscendingHand(hand);
+ }
+};
+
+/**
+ * SCORING TYPES
+ */
+
+const getBagPropByCount = (pokerHand, accessor) => {
+ if (getHandSize(pokerHand) !== POKER_HAND_SIZE) {
+ throw new Error(`hand must be a poker hand`);
+ }
+ const bagCountByProp = {};
+ const bagPropByCount = { 1: [], 2: [], 3: [], 4: [], 5: [] };
+ for (const card of pokerHand) {
+ const prop = accessor(card);
+ incrementOneToObjProp(bagCountByProp, prop);
+ }
+ for (const [prop, count] of Object.entries(bagCountByProp)) {
+ addElementToPropInObject(bagPropByCount, count, prop);
+ }
+ return bagPropByCount;
+};
+
+const getBagSuitByCount = (pokerHand) =>
+ getBagPropByCount(pokerHand, getInPlayCardSuit);
+
+const getBagValueByCount = (pokerHand) =>
+ getBagPropByCount(pokerHand, getInPlayCardValue);
+
+/**
+ *
+ * @param {Hand} hand
+ */
+const getHandBags = (hand) => {
+ return {
+ bagSuitByCount: getBagSuitByCount(hand),
+ bagValueByCount: getBagValueByCount(hand),
+ };
+};
+
+const usingPrimer = (primer) => (a, b) => primer(a) - primer(b);
+
+const comparatorCardOrdinal = usingPrimer(getInPlayCardOrdinal);
+
+const getCardRankDifference = comparatorCardOrdinal;
+
+const newAscendingHand = (hand) => {
+ const thisHand = hand.slice();
+ thisHand.sort(comparatorCardOrdinal);
+ return thisHand;
+};
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandFullHouse = ({ bagValueByCount }) =>
+ bagValueByCount[3].length === 1 && bagValueByCount[2].length === 1;
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandTrips = ({ bagValueByCount }) =>
+ bagValueByCount[3].length === 1 && bagValueByCount[2].length !== 1;
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandDouble = ({ bagValueByCount }) => bagValueByCount[2].length === 2;
+
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandPair = ({ bagValueByCount }) =>
+ bagValueByCount[2].length === 1 && bagValueByCount[1].length === 3;
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandFourOfAKind = ({ bagValueByCount }) =>
+ bagValueByCount[4].length === 1;
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandStraightFlush = ({ hand, bagSuitByCount }) =>
+ _isHandStraight({ hand }) && _isHandFlush({ bagSuitByCount });
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandFlush = ({ bagSuitByCount }) => bagSuitByCount[5].length === 1;
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandStraight = ({ hand }) => {
+ const thisHand = newAscendingHand(hand);
+ for (let i = 1; i < POKER_HAND_SIZE; i += 1) {
+ const thisCard = thisHand[i - 1];
+ const forward = thisHand[i];
+ if (getCardRankDifference(forward, thisCard) !== 1) {
+ return false;
+ }
+ }
+ return true;
+};
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandJacksOrBetter = ({ bagValueByCount, hand }) => {
+ return (
+ bagValueByCount[1].length === 5 &&
+ hand.some((card) => {
+ const value = getInPlayCardValue(card);
+
+ return (
+ value === CARD_VALUE.JACK ||
+ value === CARD_VALUE.QUEEN ||
+ value === CARD_VALUE.KING ||
+ value === CARD_VALUE.ONE
+ );
+ })
+ );
+};
+/**
+ * Warning: inner helper function of @see getScoreType
+ * @param {*} param0
+ * @returns
+ */
+const _isHandHigh = ({ bagValueByCount }) => {
+ return bagValueByCount[1].length === 5;
+};
+
+/**
+ *
+ * @param {Hand} hand
+ * @returns {string} SCORING
+ */
+const getScoreType = (hand) => {
+ if (getHandSize(hand) !== POKER_HAND_SIZE) {
+ return SCORING.SIZE_MISMATCH;
+ }
+
+ const bagValueByCount = getBagValueByCount(hand);
+ const bagSuitByCount = getBagSuitByCount(hand);
+ const handAndItsBags = { bagValueByCount, bagSuitByCount, hand };
+
+ if (_isHandStraightFlush(handAndItsBags)) {
+ return SCORING.STRAIGHT_FLUSH;
+ }
+ if (_isHandFourOfAKind(handAndItsBags)) {
+ return SCORING.FOUR_OF_A_KIND;
+ }
+ if (_isHandFullHouse(handAndItsBags)) {
+ return SCORING.FULL_HOUSE;
+ }
+ if (_isHandTrips(handAndItsBags)) {
+ return SCORING.TRIPS;
+ }
+ if (_isHandDouble(handAndItsBags)) {
+ return SCORING.DOUBLE;
+ }
+ if (_isHandFlush(handAndItsBags)) {
+ return SCORING.FLUSH;
+ }
+ if (_isHandStraight(handAndItsBags)) {
+ return SCORING.STRAIGHTS;
+ }
+ if (_isHandPair(handAndItsBags)) {
+ return SCORING.PAIR;
+ }
+ if (_isHandJacksOrBetter(handAndItsBags)) {
+ return SCORING.JOB;
+ }
+ if (_isHandHigh(handAndItsBags)) {
+ return SCORING.HIGH;
+ }
+ return SCORING.UNKNOWN;
+};
+
+const _comparatorSubRankSuitOfInPlayCards = (handA, handB) =>
+ getInPlayCardSuitAsRank(handA) - getInPlayCardSuitAsRank(handB);
+
+/**
+ * Compares suits then value of the lowest card
+ * @param {InPlayCard} handA A straight flush
+ * @param {InPlayCard} handB A straight flush
+ * @returns {number}
+ */
+const _comparatorSubRankStraightFlush = (handA, handB) => {
+ // NOTE: Call this only if both hands are straight flush.
+ if (isAnyNoU(handA, handB)) {
+ throw new Error(`Null received. handA ${handA} handB ${handB}`);
+ }
+ const handALowestCard = newAscendingHand(handA)[0];
+ const handBLowestCard = newAscendingHand(handB)[0];
+ const suitComparison = _comparatorSubRankSuitOfInPlayCards(
+ handALowestCard,
+ handBLowestCard
+ );
+
+ if (suitComparison === 0) {
+ return (
+ getInPlayCardOrdinal(handALowestCard) -
+ getInPlayCardOrdinal(handBLowestCard)
+ );
+ } else {
+ return suitComparison;
+ }
+};
+
+/**
+ *
+ * @param {InPlayCard} handA
+ * @param {InPlayCard} handB
+ * @returns
+ */
+const _comparatorSubRankStraight = (handA, handB) => {
+ if (isAnyNoU(handA, handB)) {
+ throw new Error(`Null received. handA ${handA} handB ${handB}`);
+ }
+ const handALowestCard = newAscendingHand(handA)[0];
+ const handBLowestCard = newAscendingHand(handB)[0];
+ return (
+ getInPlayCardOrdinal(handALowestCard) -
+ getInPlayCardOrdinal(handBLowestCard)
+ );
+};
+
+const _isSameScoringTypesLessThan = (handA, handB) => {
+ const scoringTypeA = getScoreType(handA);
+ const scoringTypeB = getScoreType(handB);
+ if (scoringTypeA !== scoringTypeB) {
+ throw new Error(
+ `This method should be called only if scoring types are same`
+ );
+ }
+ if (scoringTypeA === SCORING.STRAIGHT_FLUSH) {
+ return _comparatorSubRankStraightFlush(handA, handB);
+ }
+
+ if (_isHandStraight({ hand: handA })) {
+ return _comparatorSubRankStraight(handA, handB);
+ }
+
+ return undefined;
+};
+
+/**
+ *
+ * @param {*} handA
+ * @param {*} handB
+ * @returns {number}
+ */
+const comparatorHandRanking = (handA, handB) => {
+ const scoringTypeA = getScoreType(handA);
+ const scoringTypeB = getScoreType(handB);
+ const rankA = getRankOfScoringType(scoringTypeA);
+ const rankB = getRankOfScoringType(scoringTypeB);
+ if (rankA === rankB) {
+ return _isSameScoringTypesLessThan(handA, handB);
+ }
+ return rankA - rankB;
+};
+
+/**
+ *
+ * @param {Hand[]} handCombinations Hands of poker hand size (5 cards)
+ * @returns
+ */
+const getCombinationsSortedByRankAscending = (handCombinations) => {
+ const hands = handCombinations.slice();
+ hands.sort(comparatorHandRanking);
+ return hands;
+};
+
+/**
+ *
+ * @param {Hand[]} handCombinations Hands of poker hand size (5 cards)
+ * @returns
+ */
+const getBestCombination = (handCombinations) => {
+ const hands = getCombinationsSortedByRankAscending(handCombinations);
+ return hands[hands.length - 1];
+};
+
+/**
+ * STATISTICS
+ */
+
+/**
+ *
+ * @typedef ScoringDistribution
+ * @type {Object}
+ */
+
+const newScoringDistribution = () => {
+ return {
+ [SCORING.STRAIGHT_FLUSH]: 0,
+ [SCORING.FOUR_OF_A_KIND]: 0,
+ [SCORING.FULL_HOUSE]: 0,
+ [SCORING.FLUSH]: 0,
+ [SCORING.STRAIGHTS]: 0,
+ [SCORING.TRIPS]: 0,
+ [SCORING.DOUBLE]: 0,
+ [SCORING.PAIR]: 0,
+ [SCORING.HIGH]: 0,
+ [SCORING.SIZE_MISMATCH]: 0,
+ [SCORING.UNKNOWN]: 0,
+ };
+};
+
+const addToScoringDistribution = (distribution, scoreType) => {
+ if (isNoU(distribution[scoreType])) {
+ distribution[scoreType] = 0;
+ }
+ distribution[scoreType] += 1;
+};
+
+/**
+ *
+ * @param {Hand} hand
+ * @param {number} currentIndex of hand
+ * @param {number} sizePerHandCombination constant throughout iterations
+ * @param {number} toTake
+ * @param {ScoringDistribution} distribution Counts the scoring type occurrence of valid poker hands in a multi-card hand
+ * @param {Hand} currentCombination
+ * @returns
+ */
+const _calcActualScoringDistribution = (
+ hand,
+ currentIndex,
+ sizePerHandCombination,
+ toTake,
+ distribution,
+ currentCombination
+) => {
+ if (currentCombination.length === sizePerHandCombination) {
+ const scoringType = getScoreType(currentCombination);
+ addToScoringDistribution(distribution, scoringType);
+ return;
+ }
+ if (0 === toTake) {
+ return;
+ }
+
+ if (currentIndex === hand.length) {
+ return;
+ }
+
+ const combination = [...currentCombination, hand[currentIndex]];
+
+ _calcActualScoringDistribution(
+ hand,
+ currentIndex + 1,
+ sizePerHandCombination,
+ toTake,
+ distribution,
+ currentCombination
+ );
+ _calcActualScoringDistribution(
+ hand,
+ currentIndex + 1,
+ sizePerHandCombination,
+ toTake - 1,
+ distribution,
+ combination
+ );
+};
+
+/**
+ * @param {Hand|Deck} hand Sample Space
+ */
+const calcActualScoringDistribution = (hand, distribution) => {
+ const handSize = POKER_HAND_SIZE;
+ distribution = distribution || newScoringDistribution();
+ const currentCombination = [];
+
+ _calcActualScoringDistribution(
+ hand,
+ 0,
+ handSize,
+ handSize,
+ distribution,
+ currentCombination
+ );
+ return distribution;
+};
+
+const _calcScoringDistributionSevenStudGivenSomeRevealedCards = (
+ revealed,
+ deck,
+ currentIndex,
+ optionsSize,
+ toTake,
+ distribution,
+ optionsFromDeck
+) => {
+ if (optionsFromDeck.length === optionsSize) {
+ // I reach the size limit to choose unrevealed cards (options from deck)
+ const handOfX = newHand(); // x = no. of revealed + no. of options card
+ addInPlayCardsToHand(handOfX, revealed);
+ addInPlayCardsToHand(handOfX, optionsFromDeck);
+
+ const { bestScore: scoringType } =
+ ______WARN_getHandSimpleBestCombination(handOfX);
+ addToScoringDistribution(distribution, scoringType);
+ return;
+ }
+ if (0 === toTake) {
+ return;
+ }
+
+ if (currentIndex === deck.length) {
+ return;
+ }
+
+ _calcScoringDistributionSevenStudGivenSomeRevealedCards(
+ revealed,
+ deck,
+ currentIndex + 1,
+ optionsSize,
+ toTake,
+ distribution,
+ optionsFromDeck
+ );
+ const thisCard = deck[currentIndex];
+ const optionsIncludingThisCard = [...optionsFromDeck, thisCard];
+
+ _calcScoringDistributionSevenStudGivenSomeRevealedCards(
+ revealed,
+ deck,
+ currentIndex + 1,
+ optionsSize,
+ toTake - 1,
+ distribution,
+ optionsIncludingThisCard
+ );
+};
+
+/**
+ * @param {Hand|Deck} revealed The cards that are revealed
+ * @param {Hand|Deck} deck The deck to draw cards from to form hands of size handSize.
+ */
+const calcScoringDistributionSevenStudGivenSomeRevealedCards = (
+ revealed,
+ deck,
+ handSize = 7
+) => {
+ const distribution = newScoringDistribution();
+ const noOfCardsToTake = handSize - getHandSize(revealed);
+ _calcScoringDistributionSevenStudGivenSomeRevealedCards(
+ revealed,
+ deck,
+ 0,
+ noOfCardsToTake,
+ noOfCardsToTake,
+ distribution,
+ []
+ );
+ return distribution;
+};
+
+const getPMF = (distribution) => {
+ const totalSamples = Object.values(distribution).reduce((s, i) => s + i, 0);
+ return Object.entries(distribution).reduce((pmf, [type, occurrence]) => {
+ return {
+ ...pmf,
+ [type]: {
+ occurrence,
+ p: ((occurrence / totalSamples) * 100).toFixed(2),
+ payoutRatio: getPayoutOfScoringType(type),
+ hand: getNiceStringOfScoringType(type),
+ },
+ };
+ }, {});
+};
diff --git a/scripts/test/card.js b/scripts/test/card.js
new file mode 100644
index 00000000..e45de387
--- /dev/null
+++ b/scripts/test/card.js
@@ -0,0 +1,48 @@
+const TEST_CARDS = () => {
+ runTest(`testSingleCardOrdinal`, () => {
+ const cardAce = newCard(CARD_VALUE.ONE, CARD_SUITS.CLUB);
+ assertLogTrue(
+ 14,
+ getCardOrdinal(cardAce),
+ () => `[testSingleCardOrdinal] Ordinal mismatch`
+ );
+ });
+
+ runTest(`testGetHandCombinations`, () => {
+ const handSevenCard = newHand();
+
+ const card1 = newCard(CARD_VALUE.ONE, CARD_SUITS.HEART);
+ const card2 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const card3 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const card4 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const card5 = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const card6 = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const card7 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+
+ for (const card of [card1, card2, card3, card4, card5, card6, card7]) {
+ addCardToHand(handSevenCard, card);
+ }
+
+ const expectedHandSize = 7;
+ assertLogTrue(
+ expectedHandSize,
+ getHandSize(handSevenCard),
+ (e, a) => `[testGetHandCombinations] Hand Size Fail`
+ );
+
+ const sizePerHandCombination = 5;
+
+ const expectedCombinations = 21; // C(7,5)
+ const handCombinations = ______WARN_getHandCombinations(
+ handSevenCard,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ expectedCombinations,
+ handCombinations.length,
+ () => `[testGetHandCombinations] Combinations not matching`
+ );
+
+ assertToDo();
+ });
+};
diff --git a/scripts/test/helpers.js b/scripts/test/helpers.js
new file mode 100644
index 00000000..7cc63be6
--- /dev/null
+++ b/scripts/test/helpers.js
@@ -0,0 +1,65 @@
+/**
+ * @callback descIfFalseCallback
+ * @param {Object} e Expected value
+ * @param {Object} a Actual value
+ * @returns {string} Description if assertion is false.
+ */
+
+/**
+ *
+ * @param {*} expected
+ * @param {*} actual
+ * @param {descIfFalseCallback} descIfFalse
+ */
+const assertLogTrue = (expected, actual, descIfFalse) => {
+ const predicate = expected === actual;
+ if (predicate === true) {
+ } else if (predicate === false) {
+ console.error(
+ descIfFalse(expected, actual) +
+ ` Expected ${expected} Actual ${actual}`
+ );
+ } else {
+ console.error(`[assertLogTrue] assertion not specified.`);
+ }
+};
+
+/**
+ *
+ * @param {*} unexpected The value that should not be actual
+ * @param {*} actual
+ * @param {descIfFalseCallback} descIfTrue
+ */
+const assertLogNotTrue = (unexpected, actual, descIfTrue) => {
+ const predicate = unexpected === actual;
+ if (predicate === true) {
+ console.error(
+ descIfTrue(unexpected, actual) +
+ ` Expected Unequal Values But Values are Equal. Value: ${actual}`
+ );
+ } else if (predicate === false) {
+ } else {
+ console.error(`[assertLogNotTrue] assertion not specified.`);
+ }
+};
+
+const assertToDo = (desc = `??`) => console.info(`Yet to implement: ${desc}`);
+
+/**
+ * Runs a test.
+ * @param {string} desc Test description
+ * @param {function} testFunction Test function
+ */
+const runTest = (desc, testFunction) => {
+ testFunction();
+};
+
+/**
+ * Ignores a test operation.
+ * @param {string} desc Test description
+ * @param {function} testFunction Test function
+ */
+const ignoreTest = (desc, testFunction) => {
+ console.group(`${desc} - Test Ignored`);
+ console.groupEnd();
+};
diff --git a/scripts/test/index.js b/scripts/test/index.js
new file mode 100644
index 00000000..2fdeec93
--- /dev/null
+++ b/scripts/test/index.js
@@ -0,0 +1,11 @@
+const TEST_SUITE = (desc, testSuite) => {
+ console.group(`---------- ${desc}`);
+ testSuite();
+ console.groupEnd();
+};
+
+const TEST_ALL = () => {
+ TEST_SUITE(`TEST_CARDS`, TEST_CARDS);
+ TEST_SUITE(`TEST_PLAYERS`, TEST_PLAYERS);
+ TEST_SUITE(`TEST_SCORINGS`, TEST_SCORINGS);
+};
diff --git a/scripts/test/player.js b/scripts/test/player.js
new file mode 100644
index 00000000..ff369fad
--- /dev/null
+++ b/scripts/test/player.js
@@ -0,0 +1,10 @@
+const TEST_PLAYERS = () => {
+ runTest(`testDefaultPlayerCredit`, () => {
+ const player = newPlayer();
+ assertLogTrue(
+ 100,
+ getPlayerCredit(player),
+ () => `[testDefaultPlayerCredit] Fail`
+ );
+ });
+};
diff --git a/scripts/test/scoring.js b/scripts/test/scoring.js
new file mode 100644
index 00000000..0c934882
--- /dev/null
+++ b/scripts/test/scoring.js
@@ -0,0 +1,880 @@
+const TEST_SCORINGS = () => {
+ runTest(`testScoreShouldBeJacksOrBetter`, () => {
+ const card1 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const card2 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const card3 = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const card4 = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const cardJOB1 = newCard(CARD_VALUE.JACK, CARD_SUITS.CLUB);
+ const cardJOB2 = newCard(CARD_VALUE.QUEEN, CARD_SUITS.CLUB);
+ const cardJOB3 = newCard(CARD_VALUE.KING, CARD_SUITS.CLUB);
+
+ const precedingCards = [card1, card2, card3, card4];
+ for (const card of [cardJOB1, cardJOB2, cardJOB3]) {
+ const handJacksOrBetter = newHand();
+ addCardToHand(handJacksOrBetter, card);
+ addCardsToHand(handJacksOrBetter, precedingCards);
+
+ assertLogTrue(
+ SCORING.JOB,
+ getScoreType(handJacksOrBetter),
+ () =>
+ `[testScoreShouldBeHigh] ${getHandAsString(
+ handJacksOrBetter
+ )} should be jacks or better.`
+ );
+ }
+ });
+ assertToDo(`testScoreShouldBeHigh`);
+ runTest(`testScoreShouldBeStraight`, () => {
+ const cardYoungest = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const cardYounger = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const cardYoung = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const cardOld = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const cardOldest = newCard(CARD_VALUE.SEVEN, CARD_SUITS.CLUB);
+ const sortedHand = newHand();
+ const sizePerHandCombination = 5;
+
+ for (const card of [
+ cardYoungest,
+ cardYounger,
+ cardYoung,
+ cardOld,
+ cardOldest,
+ ]) {
+ addCardToHand(sortedHand, card);
+ }
+
+ const handCombinations = ______WARN_getHandCombinations(
+ sortedHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[testGetHandCombinations] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ const thisHand = newAscendingHand(combination);
+
+ for (let i = 0; i < thisHand.length; i++) {
+ assertLogTrue(
+ thisHand[i],
+ combination[i],
+ () => `[testScoreShouldBeStraight] New array should be sorted`
+ );
+ }
+
+ const expectedScore = SCORING.STRAIGHTS;
+ const actualScore = getScoreType(sortedHand);
+ assertLogTrue(
+ expectedScore,
+ actualScore,
+ () => `[testScoreShouldBeStraight] Failed`
+ );
+ });
+ runTest(`testCardsScoreShouldNotBeStraight`, () => {
+ const cardYoungest = newCard(CARD_VALUE.JACK, CARD_SUITS.HEART);
+ const cardYounger = newCard(CARD_VALUE.QUEEN, CARD_SUITS.HEART);
+ const cardYoung = newCard(CARD_VALUE.KING, CARD_SUITS.HEART);
+ const cardNotYoung = newCard(CARD_VALUE.ONE, CARD_SUITS.HEART);
+ const cardNotOld = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const cardOld = newCard(CARD_VALUE.THREE, CARD_SUITS.CLUB);
+ const cardOlder = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const cardOldest = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+
+ const cardRange = [
+ cardYoungest,
+ cardYounger,
+ cardYoung,
+ cardNotYoung,
+ cardNotOld,
+ cardOld,
+ cardOlder,
+ cardOldest,
+ ];
+
+ for (let i = 0; i < 1; i += 1) {
+ const thisCardYoungest = cardRange[i];
+ const thisCardYounger = cardRange[i + 1];
+ const thisCardYoung = cardRange[i + 2];
+ const thisCardOld = cardRange[i + 3];
+ const thisCardOldest = cardRange[i + 4];
+
+ const sortedHand = newHand();
+ const sizePerHandCombination = 5;
+ const thisRange = [
+ thisCardYoungest,
+ thisCardYounger,
+ thisCardYoung,
+ thisCardOld,
+ thisCardOldest,
+ ];
+ for (const card of thisRange) {
+ addCardToHand(sortedHand, card);
+ }
+
+ const handCombinations = ______WARN_getHandCombinations(
+ sortedHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[testCardsScoreShouldNotBeStraight] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ const thisHand = newAscendingHand(combination);
+
+ assertLogNotTrue(
+ SCORING.STRAIGHTS,
+ getScoreType(thisHand),
+ (e) => `[testCardsScoreShouldNotBeStraight] Expected Hand to Be ${e}`
+ );
+ }
+ assertToDo(`new test to assert the expected scoring type of the samples`);
+ });
+
+ runTest(`testCardsScoreShouldBeFlush`, () => {
+ const cardThemed1 = newCard(CARD_VALUE.JACK, CARD_SUITS.HEART);
+ const cardThemed2 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const cardThemed3 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+ const cardThemed4 = newCard(CARD_VALUE.ONE, CARD_SUITS.HEART);
+ const cardThemed5 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+
+ const cardRange = [
+ cardThemed1,
+ cardThemed2,
+ cardThemed3,
+ cardThemed4,
+ cardThemed5,
+ ];
+
+ const themedHand = newHand();
+ const sizePerHandCombination = 5;
+
+ for (const card of cardRange) {
+ addCardToHand(themedHand, card);
+ }
+
+ const handCombinations = ______WARN_getHandCombinations(
+ themedHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[testCardsScoreShouldBeFlush] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < themedHand.length; i++) {
+ assertLogTrue(
+ themedHand[i],
+ combination[i],
+ () =>
+ `[testCardsScoreShouldBeFlush] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+
+ const scoreType = getScoreType(themedHand);
+ assertLogTrue(
+ SCORING.FLUSH,
+ scoreType,
+ () => `[testCardsScoreShouldBeFlush] Score Type assertion failed.`
+ );
+ assertLogNotTrue(
+ SCORING.STRAIGHTS,
+ scoreType,
+ () => `[testCardsScoreShouldBeFlush] Unexpected Score Type`
+ );
+ });
+
+ runTest(`testCardScoreShouldBeFourOfAKind`, () => {
+ const cardPack1 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const cardPack2 = newCard(CARD_VALUE.TWO, CARD_SUITS.CLUB);
+ const cardPack3 = newCard(CARD_VALUE.TWO, CARD_SUITS.DIAMOND);
+ const cardPack4 = newCard(CARD_VALUE.TWO, CARD_SUITS.SPADE);
+ const cardHigh = newCard(CARD_VALUE.JACK, CARD_SUITS.HEART);
+
+ const cardRange = [cardPack1, cardPack2, cardPack3, cardPack4, cardHigh];
+
+ const themedHand = newHand();
+ const sizePerHandCombination = 5;
+
+ for (const card of cardRange) {
+ addCardToHand(themedHand, card);
+ }
+
+ const handCombinations = ______WARN_getHandCombinations(
+ themedHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[testCardScoreShouldBeFourOfAKind] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < themedHand.length; i++) {
+ assertLogTrue(
+ themedHand[i],
+ combination[i],
+ () =>
+ `[testCardScoreShouldBeFourOfAKind] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+
+ const scoreType = getScoreType(themedHand);
+ assertLogTrue(
+ SCORING.FOUR_OF_A_KIND,
+ scoreType,
+ () => `[testCardScoreShouldBeFourOfAKind] Score Type assertion failed.`
+ );
+ });
+
+ runTest(`testCardScoreShouldBeHouse`, () => {
+ const functionName = `testCardScoreShouldBeHouse`;
+ const cardPack1 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const cardPack2 = newCard(CARD_VALUE.TWO, CARD_SUITS.DIAMOND);
+ const cardPack3 = newCard(CARD_VALUE.TWO, CARD_SUITS.SPADE);
+ const cardPair1 = newCard(CARD_VALUE.JACK, CARD_SUITS.CLUB);
+ const cardPair2 = newCard(CARD_VALUE.JACK, CARD_SUITS.HEART);
+
+ const cardRange = [cardPack1, cardPair1, cardPack2, cardPack3, cardPair2];
+ const housedHand = newHand();
+ const sizePerHandCombination = 5;
+ for (const card of cardRange) {
+ addCardToHand(housedHand, card);
+ }
+ const handCombinations = ______WARN_getHandCombinations(
+ housedHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[${functionName}] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < housedHand.length; i++) {
+ assertLogTrue(
+ housedHand[i],
+ combination[i],
+ () =>
+ `[${functionName}] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+ const scoreType = getScoreType(housedHand);
+ assertLogTrue(
+ SCORING.FULL_HOUSE,
+ scoreType,
+ () => `[${functionName}] Score Type assertion failed.`
+ );
+
+ assertLogNotTrue(
+ SCORING.TRIPS,
+ scoreType,
+ () =>
+ `[testCardsScoreShouldBeFlush] Unexpected Score Type, three of a Kind and TWO of another`
+ );
+ });
+
+ runTest(`testCardScoreShouldBeTrips`, () => {
+ const functionName = `testCardScoreShouldBeTrips`;
+ const cardHigh1 = newCard(CARD_VALUE.JACK, CARD_SUITS.CLUB);
+ const cardHigh2 = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const cardPack1 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const cardPack2 = newCard(CARD_VALUE.TWO, CARD_SUITS.DIAMOND);
+ const cardPack3 = newCard(CARD_VALUE.TWO, CARD_SUITS.SPADE);
+ const cardRange = [cardPack1, cardHigh1, cardPack2, cardPack3, cardHigh2];
+ const housedHand = newHand();
+ const sizePerHandCombination = 5;
+ for (const card of cardRange) {
+ addCardToHand(housedHand, card);
+ }
+ const handCombinations = ______WARN_getHandCombinations(
+ housedHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[${functionName}] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < housedHand.length; i++) {
+ assertLogTrue(
+ housedHand[i],
+ combination[i],
+ () =>
+ `[${functionName}] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+ const scoreType = getScoreType(housedHand);
+ assertLogTrue(
+ SCORING.TRIPS,
+ scoreType,
+ () => `[${functionName}] Score Type assertion failed.`
+ );
+ });
+
+ runTest(`testCardScoreShouldBeStraightFlush`, () => {
+ const functionName = `testCardScoreShouldBeStraightFlush`;
+ const cardPack1 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const cardPack2 = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const cardPack3 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const cardPack4 = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const cardPack5 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+ const cardRange = [cardPack1, cardPack2, cardPack3, cardPack4, cardPack5];
+ const housedHand = newHand();
+ const sizePerHandCombination = 5;
+ for (const card of cardRange) {
+ addCardToHand(housedHand, card);
+ }
+ const handCombinations = ______WARN_getHandCombinations(
+ housedHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[${functionName}] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < housedHand.length; i++) {
+ assertLogTrue(
+ housedHand[i],
+ combination[i],
+ () =>
+ `[${functionName}] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+ const scoreType = getScoreType(housedHand);
+ assertLogTrue(
+ SCORING.STRAIGHT_FLUSH,
+ scoreType,
+ () => `[${functionName}] Score Type assertion failed.`
+ );
+ });
+
+ runTest(`testCardScoreShouldBeDoubs`, () => {
+ const functionName = `testCardScoreShouldBeDoubs`;
+ const cardPair1_1 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const cardPair1_2 = newCard(CARD_VALUE.THREE, CARD_SUITS.CLUB);
+ const cardPair2_1 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const cardPair2_2 = newCard(CARD_VALUE.FOUR, CARD_SUITS.DIAMOND);
+ const cardHigh = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+ const cardRange = [
+ cardPair1_1,
+ cardPair2_2,
+ cardPair1_2,
+ cardPair2_1,
+ cardHigh,
+ ];
+ const doubsHand = newHand();
+ const sizePerHandCombination = 5;
+ for (const card of cardRange) {
+ addCardToHand(doubsHand, card);
+ }
+ const handCombinations = ______WARN_getHandCombinations(
+ doubsHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[${functionName}] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < doubsHand.length; i++) {
+ assertLogTrue(
+ doubsHand[i],
+ combination[i],
+ () =>
+ `[${functionName}] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+ const scoreType = getScoreType(doubsHand);
+ assertLogTrue(
+ SCORING.DOUBLE,
+ scoreType,
+ () => `[${functionName}] Score Type assertion failed.`
+ );
+ });
+
+ runTest(`testCardScoreShouldBePair`, () => {
+ const functionName = `testCardScoreShouldBePair`;
+ const cardPair1 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const cardPair2 = newCard(CARD_VALUE.THREE, CARD_SUITS.CLUB);
+ const cardHigh2 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const cardHigh3 = newCard(CARD_VALUE.JACK, CARD_SUITS.DIAMOND);
+ const cardHigh1 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+ const cardRange = [cardPair1, cardHigh3, cardPair2, cardHigh2, cardHigh1];
+ const doubsHand = newHand();
+ const sizePerHandCombination = 5;
+ for (const card of cardRange) {
+ addCardToHand(doubsHand, card);
+ }
+ const handCombinations = ______WARN_getHandCombinations(
+ doubsHand,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[${functionName}] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < doubsHand.length; i++) {
+ assertLogTrue(
+ doubsHand[i],
+ combination[i],
+ () =>
+ `[${functionName}] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+ const scoreType = getScoreType(doubsHand);
+ assertLogTrue(
+ SCORING.PAIR,
+ scoreType,
+ () => `[${functionName}] Score Type assertion failed.`
+ );
+ });
+
+ runTest(`testCardScoreShouldBeHigh`, () => {
+ const functionName = `testCardScoreShouldBeHigh`;
+ const cardPair1 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const cardPair2 = newCard(CARD_VALUE.THREE, CARD_SUITS.CLUB);
+ const cardHigh2 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const cardHigh3 = newCard(CARD_VALUE.SIX, CARD_SUITS.DIAMOND);
+ const cardHigh1 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+ const cardRange = [cardPair1, cardHigh3, cardPair2, cardHigh2, cardHigh1];
+ const handHigh = newHand();
+ const sizePerHandCombination = 5;
+ for (const card of cardRange) {
+ addCardToHand(handHigh, card);
+ }
+ const handCombinations = ______WARN_getHandCombinations(
+ handHigh,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1,
+ handCombinations.length,
+ () => `[${functionName}] Combinations not matching`
+ );
+ const combination = handCombinations[0];
+
+ for (let i = 0; i < handHigh.length; i++) {
+ assertLogTrue(
+ handHigh[i],
+ combination[i],
+ () =>
+ `[${functionName}] The five cards chosen from a pool of five should be in original order (don't need to change original card order as we are testing for colors)`
+ );
+ }
+ const scoreType = getScoreType(handHigh);
+ assertLogTrue(
+ SCORING.HIGH,
+ scoreType,
+ () => `[${functionName}] Score Type assertion failed.`
+ );
+ });
+
+ runTest(`testSingleSuitDeckDistributionAgainstCombinationsV1`, () => {
+ const functionName = `testSingleSuitDeckDistributionAgainstCombinationsV1`;
+
+ const singleSuitDeck = newSampleSingleSuitDeck();
+ assertLogTrue(
+ 13,
+ getDeckSize(singleSuitDeck),
+ () => `[${functionName}] Combinations not matching`
+ );
+ const sizePerHandCombination = 5;
+
+ const handCombinations = ______WARN_getHandCombinations(
+ singleSuitDeck,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ 1287,
+ handCombinations.length,
+ () =>
+ `[${functionName}] [______WARN_getHandCombinations] No. of Combinations`
+ );
+ const actualScoringDistribution =
+ calcActualScoringDistribution(singleSuitDeck);
+
+ const totalSamples = Object.values(actualScoringDistribution).reduce(
+ (sum, i) => sum + i,
+ 0
+ );
+
+ assertLogTrue(
+ 1287,
+ totalSamples,
+ () => `[${functionName}] [actualScoringDistribution] No. of Combinations`
+ );
+
+ assertLogTrue(
+ 9,
+ actualScoringDistribution[SCORING.STRAIGHT_FLUSH],
+ (e, a) =>
+ `[${functionName}] No. of ${SCORING.STRAIGHT_FLUSH} Combinations`
+ );
+
+ assertLogTrue(
+ 1278,
+ actualScoringDistribution[SCORING.FLUSH],
+ (e, a) =>
+ `[${functionName}] No. of ${SCORING.STRAIGHT_FLUSH} Combinations`
+ );
+
+ assertLogTrue(
+ 0,
+ actualScoringDistribution[SCORING.STRAIGHTS],
+ (e, a) => `[${functionName}] No. of ${SCORING.STRAIGHTS} Combinations`
+ );
+ assertLogTrue(
+ 0,
+ actualScoringDistribution[SCORING.FOUR_OF_A_KIND],
+ (e, a) =>
+ `[${functionName}] No. of ${SCORING.FOUR_OF_A_KIND} Combinations`
+ );
+ assertLogTrue(
+ 0,
+ actualScoringDistribution[SCORING.FULL_HOUSE],
+ (e, a) => `[${functionName}] No. of ${SCORING.FULL_HOUSE} Combinations`
+ );
+ assertLogTrue(
+ 0,
+ actualScoringDistribution[SCORING.DOUBLE],
+ (e, a) => `[${functionName}] No. of ${SCORING.DOUBLE} Combinations`
+ );
+ assertLogTrue(
+ 0,
+ actualScoringDistribution[SCORING.PAIR],
+ (e, a) => `[${functionName}] No. of ${SCORING.PAIR} Combinations`
+ );
+ assertLogTrue(
+ 0,
+ actualScoringDistribution[SCORING.HIGH],
+ (e, a) => `[${functionName}] No. of ${SCORING.HIGH} Combinations`
+ );
+ });
+ runTest(`testTopCombinationShouldBeHighestStraightFlush`, () => {
+ const functionName = `testTopCombinationShouldBeHighestStraightFlush`;
+
+ const handSevenCard = newHand();
+
+ const card1 = newCard(CARD_VALUE.ONE, CARD_SUITS.HEART);
+ const card2 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const card3 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const card4 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const card5 = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const card6 = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const card7 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+
+ for (const card of [card6, card1, card3, card2, card5, card4, card7]) {
+ addCardToHand(handSevenCard, card);
+ }
+
+ const expectedHandSize = 7;
+ assertLogTrue(
+ expectedHandSize,
+ getHandSize(handSevenCard),
+ (e, a) => `[${functionName}] Hand Size Fail`
+ );
+
+ const sizePerHandCombination = 5;
+ const expectedCombinations = 21; // C(7,5)
+
+ const handCombinations = ______WARN_getHandCombinations(
+ handSevenCard,
+ sizePerHandCombination
+ );
+ assertLogTrue(
+ expectedCombinations,
+ handCombinations.length,
+ () => `[${functionName}] No. of Combinations not matching`
+ );
+
+ const expectedBestHandOfSeven = newHandWithCards([
+ card7,
+ card6,
+ card5,
+ card4,
+ card3,
+ ]);
+
+ const actualBestFromCombinationsOfHandOfSeven =
+ getBestCombination(handCombinations);
+
+ const scoringType = getScoreType(expectedBestHandOfSeven);
+
+ assertLogTrue(
+ SCORING.STRAIGHT_FLUSH,
+ scoringType,
+ () => `[${functionName}] Scoring Type of best hand not matching`
+ );
+
+ const rankOfScoringType = getRankOfScoringType(scoringType);
+
+ assertLogTrue(
+ 15,
+ rankOfScoringType,
+ () => `[${functionName}] Rank of Scoring Type of best hand not matching`
+ );
+
+ const sortedHand = newAscendingHand(expectedBestHandOfSeven);
+ const prettiedHand = getPrettiedHand(expectedBestHandOfSeven);
+
+ for (let i = 0; i < prettiedHand.length; i++) {
+ assertLogTrue(
+ sortedHand[i],
+ prettiedHand[i],
+ () =>
+ `[${functionName}] Straight flush should be presented as a straight.`
+ );
+ }
+ });
+
+ runTest(`testComparisionStraighFlushes`, () => {
+ const functionName = `testComparisionStraighFlushes`;
+
+ const card1 = newCard(CARD_VALUE.TWO, CARD_SUITS.HEART);
+ const card2 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const card3 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const card4 = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const card5 = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const card6 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+
+ const lowerStraightFlush = newHand();
+ const higherStraightFlush = newHand();
+
+ for (const card of [card1, card3, card2, card5, card4]) {
+ addCardToHand(lowerStraightFlush, card);
+ }
+
+ for (const card of [card6, card3, card2, card5, card4]) {
+ addCardToHand(higherStraightFlush, card);
+ }
+ const expectedHandSize = 5;
+
+ for (const hand of [lowerStraightFlush, higherStraightFlush]) {
+ assertLogTrue(
+ expectedHandSize,
+ getHandSize(hand),
+ (e, a) => `[${functionName}] Hand Size Fail`
+ );
+ }
+
+ assertLogTrue(
+ true,
+ comparatorHandRanking(lowerStraightFlush, higherStraightFlush) < 0,
+ () => `[${functionName}] Comparision should be lesser`
+ );
+ });
+
+ runTest(`testComparisionStraight`, () => {
+ const functionName = `testComparisionStraight`;
+
+ const card1 = newCard(CARD_VALUE.TWO, CARD_SUITS.SPADE);
+ const card2 = newCard(CARD_VALUE.THREE, CARD_SUITS.CLUB);
+ const card3 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const card4 = newCard(CARD_VALUE.FIVE, CARD_SUITS.DIAMOND);
+ const card5 = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const card6 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.SPADE);
+
+ const lowerStraight = newHand();
+ const higherStraight = newHand();
+
+ for (const card of [card1, card3, card2, card5, card4]) {
+ addCardToHand(lowerStraight, card);
+ }
+
+ for (const card of [card6, card3, card2, card5, card4]) {
+ addCardToHand(higherStraight, card);
+ }
+ const expectedHandSize = 5;
+
+ for (const hand of [lowerStraight, higherStraight]) {
+ assertLogTrue(
+ expectedHandSize,
+ getHandSize(hand),
+ (e, a) => `[${functionName}] Hand Size Fail`
+ );
+ }
+
+ const comparisonResult = comparatorHandRanking(
+ lowerStraight,
+ higherStraight
+ );
+
+ assertLogTrue(
+ true,
+ isNoU(comparisonResult) ? comparisonResult : comparisonResult < 0,
+ () => {
+ const lowerHandString = getHandAsString(lowerStraight);
+ const higherHandString = getHandAsString(higherStraight);
+
+ return `[${functionName}] Comparision should be lesser ${lowerHandString} < ${higherHandString}`;
+ }
+ );
+ });
+
+ runTest(`testComparisionFlush`, () => {
+ const functionName = `testComparisionFlush`;
+
+ // arrange from small ranking hand to big ranking hand
+
+ const cardClubs1 = newCard(CARD_VALUE.FIVE, CARD_SUITS.CLUB);
+ const cardClubs2 = newCard(CARD_VALUE.SIX, CARD_SUITS.CLUB);
+ const cardClubs3 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.CLUB);
+ const cardClubs4 = newCard(CARD_VALUE.EIGHT, CARD_SUITS.CLUB);
+ const cardClubs5 = newCard(CARD_VALUE.NINE, CARD_SUITS.CLUB);
+
+ const clubsRange = [
+ cardClubs1,
+ cardClubs3,
+ cardClubs4,
+ cardClubs5,
+ cardClubs2,
+ ];
+
+ const cardDiamonds1 = newCard(CARD_VALUE.FOUR, CARD_SUITS.DIAMOND);
+ const cardDiamonds2 = newCard(CARD_VALUE.FIVE, CARD_SUITS.DIAMOND);
+ const cardDiamonds3 = newCard(CARD_VALUE.SIX, CARD_SUITS.DIAMOND);
+ const cardDiamonds4 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.DIAMOND);
+ const cardDiamonds5 = newCard(CARD_VALUE.EIGHT, CARD_SUITS.DIAMOND);
+
+ const diamondsRange = [
+ cardDiamonds1,
+ cardDiamonds2,
+ cardDiamonds3,
+ cardDiamonds4,
+ cardDiamonds5,
+ ];
+
+ const cardHearts1 = newCard(CARD_VALUE.THREE, CARD_SUITS.HEART);
+ const cardHearts2 = newCard(CARD_VALUE.FOUR, CARD_SUITS.HEART);
+ const cardHearts3 = newCard(CARD_VALUE.FIVE, CARD_SUITS.HEART);
+ const cardHearts4 = newCard(CARD_VALUE.SIX, CARD_SUITS.HEART);
+ const cardHearts5 = newCard(CARD_VALUE.SEVEN, CARD_SUITS.HEART);
+
+ const heartsRange = [
+ cardHearts1,
+ cardHearts2,
+ cardHearts3,
+ cardHearts4,
+ cardHearts5,
+ ];
+
+ const cardSpadeLower1 = newCard(CARD_VALUE.TWO, CARD_SUITS.SPADE);
+ const cardSpadeLower2 = newCard(CARD_VALUE.THREE, CARD_SUITS.SPADE);
+ const cardSpadeLower3 = newCard(CARD_VALUE.FOUR, CARD_SUITS.SPADE);
+ const cardSpadeLower4 = newCard(CARD_VALUE.FIVE, CARD_SUITS.SPADE);
+ const cardSpadeLower5 = newCard(CARD_VALUE.SIX, CARD_SUITS.SPADE);
+
+ const spadeLowRange = [
+ cardSpadeLower1,
+ cardSpadeLower2,
+ cardSpadeLower3,
+ cardSpadeLower4,
+ cardSpadeLower5,
+ ];
+ const cardSpadeHigher1 = newCard(CARD_VALUE.TEN, CARD_SUITS.SPADE);
+ const cardSpadeHigher2 = newCard(CARD_VALUE.JACK, CARD_SUITS.SPADE);
+ const cardSpadeHigher3 = newCard(CARD_VALUE.QUEEN, CARD_SUITS.SPADE);
+ const cardSpadeHigher4 = newCard(CARD_VALUE.KING, CARD_SUITS.SPADE);
+ const cardSpadeHigher5 = newCard(CARD_VALUE.ONE, CARD_SUITS.SPADE);
+
+ const spadeHighRange = [
+ cardSpadeHigher1,
+ cardSpadeHigher3,
+ cardSpadeHigher2,
+ cardSpadeHigher4,
+ cardSpadeHigher5,
+ ];
+
+ const ranges = [
+ clubsRange,
+ diamondsRange,
+ heartsRange,
+ spadeLowRange,
+ spadeHighRange,
+ ];
+ const hands = [];
+
+ for (let i = 0; i < ranges.length; i += 1) {
+ const hand = newHand();
+ addCardsToHand(hand, ranges[i]);
+ hands.push(hand);
+ }
+
+ const expectedHandSize = 5;
+
+ for (const hand of hands) {
+ assertLogTrue(
+ expectedHandSize,
+ getHandSize(hand),
+ (e, a) => `[${functionName}] No. of Hands Fail`
+ );
+ }
+ for (let i = 1; i < hands.length; i += 1) {
+ const thisHandSmaller = hands[i - 1];
+ const forwardBigger = hands[i];
+ const comparisonResult = comparatorHandRanking(
+ forwardBigger,
+ thisHandSmaller
+ );
+ assertLogTrue(
+ true,
+ isNoU(comparisonResult) ? comparisonResult : comparisonResult > 0,
+ () => {
+ const thisHandAsString = getHandAsString(thisHandSmaller);
+ const forwardAsString = getHandAsString(forwardBigger);
+ return `[${functionName}] [${i}] Comparision expects ${forwardAsString} > ${thisHandAsString}`;
+ }
+ );
+ }
+ });
+
+ runTest(`sevenStud_testScoreShouldBeDoubs`, () => {
+ const card1 = newCard(CARD_VALUE.THREE, CARD_SUITS.DIAMOND);
+ const card2 = newCard(CARD_VALUE.FOUR, CARD_SUITS.SPADE);
+ const card3 = newCard(CARD_VALUE.FIVE, CARD_SUITS.CLUB);
+ const card4 = newCard(CARD_VALUE.ONE, CARD_SUITS.HEART);
+ const card5 = newCard(CARD_VALUE.ONE, CARD_SUITS.DIAMOND);
+ const card6 = newCard(CARD_VALUE.NINE, CARD_SUITS.SPADE);
+ const card7 = newCard(CARD_VALUE.NINE, CARD_SUITS.CLUB);
+ const cardRange = [card1, card2, card3, card4, card5, card6, card7];
+ const handSeven = newHand();
+ addCardsToHand(handSeven, cardRange);
+
+ const combinations = ______WARN_getHandCombinations(
+ handSeven,
+ POKER_HAND_SIZE
+ );
+ console.log(combinations);
+ const best = getBestCombination(combinations);
+ console.log(getHandAsString(best));
+ assertToDo(`Assertion`);
+ // assertLogTrue(
+ // SCORING.DOUBLE,
+ // getScoreType(best),
+ // () =>
+ // `[testScoreShouldBeHigh] ${getHandAsString(
+ // handJacksOrBetter
+ // )} should be jacks or better.`
+ // );
+ });
+};
diff --git a/static/img/buttons/chip.png b/static/img/buttons/chip.png
new file mode 100644
index 00000000..5ec6758c
Binary files /dev/null and b/static/img/buttons/chip.png differ
diff --git a/static/img/cards-spare/01-CLUBS.png b/static/img/cards-spare/01-CLUBS.png
new file mode 100644
index 00000000..3ff8aa60
Binary files /dev/null and b/static/img/cards-spare/01-CLUBS.png differ
diff --git a/static/img/cards-spare/01-DIAMONDS.png b/static/img/cards-spare/01-DIAMONDS.png
new file mode 100644
index 00000000..d98a7c4b
Binary files /dev/null and b/static/img/cards-spare/01-DIAMONDS.png differ
diff --git a/static/img/cards-spare/01-HEARTS.png b/static/img/cards-spare/01-HEARTS.png
new file mode 100644
index 00000000..534cf09a
Binary files /dev/null and b/static/img/cards-spare/01-HEARTS.png differ
diff --git a/static/img/cards-spare/01-SPADES.png b/static/img/cards-spare/01-SPADES.png
new file mode 100644
index 00000000..536a3be4
Binary files /dev/null and b/static/img/cards-spare/01-SPADES.png differ
diff --git a/static/img/cards-spare/02-CLUBS.png b/static/img/cards-spare/02-CLUBS.png
new file mode 100644
index 00000000..c46843a6
Binary files /dev/null and b/static/img/cards-spare/02-CLUBS.png differ
diff --git a/static/img/cards-spare/02-DIAMONDS.png b/static/img/cards-spare/02-DIAMONDS.png
new file mode 100644
index 00000000..82786421
Binary files /dev/null and b/static/img/cards-spare/02-DIAMONDS.png differ
diff --git a/static/img/cards-spare/02-HEARTS.png b/static/img/cards-spare/02-HEARTS.png
new file mode 100644
index 00000000..ed95e567
Binary files /dev/null and b/static/img/cards-spare/02-HEARTS.png differ
diff --git a/static/img/cards-spare/02-SPADES.png b/static/img/cards-spare/02-SPADES.png
new file mode 100644
index 00000000..ae7ba22e
Binary files /dev/null and b/static/img/cards-spare/02-SPADES.png differ
diff --git a/static/img/cards-spare/03-CLUBS.png b/static/img/cards-spare/03-CLUBS.png
new file mode 100644
index 00000000..d9435b44
Binary files /dev/null and b/static/img/cards-spare/03-CLUBS.png differ
diff --git a/static/img/cards-spare/03-DIAMONDS.png b/static/img/cards-spare/03-DIAMONDS.png
new file mode 100644
index 00000000..51325497
Binary files /dev/null and b/static/img/cards-spare/03-DIAMONDS.png differ
diff --git a/static/img/cards-spare/03-HEARTS.png b/static/img/cards-spare/03-HEARTS.png
new file mode 100644
index 00000000..72a3bdaf
Binary files /dev/null and b/static/img/cards-spare/03-HEARTS.png differ
diff --git a/static/img/cards-spare/03-SPADES.png b/static/img/cards-spare/03-SPADES.png
new file mode 100644
index 00000000..c8d38f0c
Binary files /dev/null and b/static/img/cards-spare/03-SPADES.png differ
diff --git a/static/img/cards-spare/04-CLUBS.png b/static/img/cards-spare/04-CLUBS.png
new file mode 100644
index 00000000..cc72540b
Binary files /dev/null and b/static/img/cards-spare/04-CLUBS.png differ
diff --git a/static/img/cards-spare/04-DIAMONDS.png b/static/img/cards-spare/04-DIAMONDS.png
new file mode 100644
index 00000000..dd2418b5
Binary files /dev/null and b/static/img/cards-spare/04-DIAMONDS.png differ
diff --git a/static/img/cards-spare/04-HEARTS.png b/static/img/cards-spare/04-HEARTS.png
new file mode 100644
index 00000000..30d06de4
Binary files /dev/null and b/static/img/cards-spare/04-HEARTS.png differ
diff --git a/static/img/cards-spare/04-SPADES.png b/static/img/cards-spare/04-SPADES.png
new file mode 100644
index 00000000..2c93a215
Binary files /dev/null and b/static/img/cards-spare/04-SPADES.png differ
diff --git a/static/img/cards-spare/05-CLUBS.png b/static/img/cards-spare/05-CLUBS.png
new file mode 100644
index 00000000..451ac6a1
Binary files /dev/null and b/static/img/cards-spare/05-CLUBS.png differ
diff --git a/static/img/cards-spare/05-DIAMONDS.png b/static/img/cards-spare/05-DIAMONDS.png
new file mode 100644
index 00000000..4bf1de94
Binary files /dev/null and b/static/img/cards-spare/05-DIAMONDS.png differ
diff --git a/static/img/cards-spare/05-HEARTS.png b/static/img/cards-spare/05-HEARTS.png
new file mode 100644
index 00000000..a8a42363
Binary files /dev/null and b/static/img/cards-spare/05-HEARTS.png differ
diff --git a/static/img/cards-spare/05-SPADES.png b/static/img/cards-spare/05-SPADES.png
new file mode 100644
index 00000000..5cf3bcfc
Binary files /dev/null and b/static/img/cards-spare/05-SPADES.png differ
diff --git a/static/img/cards-spare/06-CLUBS.png b/static/img/cards-spare/06-CLUBS.png
new file mode 100644
index 00000000..9293ed3f
Binary files /dev/null and b/static/img/cards-spare/06-CLUBS.png differ
diff --git a/static/img/cards-spare/06-DIAMONDS.png b/static/img/cards-spare/06-DIAMONDS.png
new file mode 100644
index 00000000..b21a3eec
Binary files /dev/null and b/static/img/cards-spare/06-DIAMONDS.png differ
diff --git a/static/img/cards-spare/06-HEARTS.png b/static/img/cards-spare/06-HEARTS.png
new file mode 100644
index 00000000..5a26cbbb
Binary files /dev/null and b/static/img/cards-spare/06-HEARTS.png differ
diff --git a/static/img/cards-spare/06-SPADES.png b/static/img/cards-spare/06-SPADES.png
new file mode 100644
index 00000000..edc65b5b
Binary files /dev/null and b/static/img/cards-spare/06-SPADES.png differ
diff --git a/static/img/cards-spare/07-CLUBS.png b/static/img/cards-spare/07-CLUBS.png
new file mode 100644
index 00000000..aca12f21
Binary files /dev/null and b/static/img/cards-spare/07-CLUBS.png differ
diff --git a/static/img/cards-spare/07-DIAMONDS.png b/static/img/cards-spare/07-DIAMONDS.png
new file mode 100644
index 00000000..8d97797a
Binary files /dev/null and b/static/img/cards-spare/07-DIAMONDS.png differ
diff --git a/static/img/cards-spare/07-HEARTS.png b/static/img/cards-spare/07-HEARTS.png
new file mode 100644
index 00000000..1a99572a
Binary files /dev/null and b/static/img/cards-spare/07-HEARTS.png differ
diff --git a/static/img/cards-spare/07-SPADES.png b/static/img/cards-spare/07-SPADES.png
new file mode 100644
index 00000000..ee77ee96
Binary files /dev/null and b/static/img/cards-spare/07-SPADES.png differ
diff --git a/static/img/cards-spare/08-CLUBS.png b/static/img/cards-spare/08-CLUBS.png
new file mode 100644
index 00000000..539f3273
Binary files /dev/null and b/static/img/cards-spare/08-CLUBS.png differ
diff --git a/static/img/cards-spare/08-DIAMONDS.png b/static/img/cards-spare/08-DIAMONDS.png
new file mode 100644
index 00000000..48dbe15d
Binary files /dev/null and b/static/img/cards-spare/08-DIAMONDS.png differ
diff --git a/static/img/cards-spare/08-HEARTS.png b/static/img/cards-spare/08-HEARTS.png
new file mode 100644
index 00000000..6e274b26
Binary files /dev/null and b/static/img/cards-spare/08-HEARTS.png differ
diff --git a/static/img/cards-spare/08-SPADES.png b/static/img/cards-spare/08-SPADES.png
new file mode 100644
index 00000000..b7446cbb
Binary files /dev/null and b/static/img/cards-spare/08-SPADES.png differ
diff --git a/static/img/cards-spare/09-CLUBS.png b/static/img/cards-spare/09-CLUBS.png
new file mode 100644
index 00000000..2228ceca
Binary files /dev/null and b/static/img/cards-spare/09-CLUBS.png differ
diff --git a/static/img/cards-spare/09-DIAMONDS.png b/static/img/cards-spare/09-DIAMONDS.png
new file mode 100644
index 00000000..d5833b24
Binary files /dev/null and b/static/img/cards-spare/09-DIAMONDS.png differ
diff --git a/static/img/cards-spare/09-HEARTS.png b/static/img/cards-spare/09-HEARTS.png
new file mode 100644
index 00000000..f649c8d0
Binary files /dev/null and b/static/img/cards-spare/09-HEARTS.png differ
diff --git a/static/img/cards-spare/09-SPADES.png b/static/img/cards-spare/09-SPADES.png
new file mode 100644
index 00000000..273a1fe2
Binary files /dev/null and b/static/img/cards-spare/09-SPADES.png differ
diff --git a/static/img/cards-spare/10-CLUBS.png b/static/img/cards-spare/10-CLUBS.png
new file mode 100644
index 00000000..888cdd62
Binary files /dev/null and b/static/img/cards-spare/10-CLUBS.png differ
diff --git a/static/img/cards-spare/10-DIAMONDS.png b/static/img/cards-spare/10-DIAMONDS.png
new file mode 100644
index 00000000..2dcf08ec
Binary files /dev/null and b/static/img/cards-spare/10-DIAMONDS.png differ
diff --git a/static/img/cards-spare/10-HEARTS.png b/static/img/cards-spare/10-HEARTS.png
new file mode 100644
index 00000000..30f93d28
Binary files /dev/null and b/static/img/cards-spare/10-HEARTS.png differ
diff --git a/static/img/cards-spare/10-SPADES.png b/static/img/cards-spare/10-SPADES.png
new file mode 100644
index 00000000..66ea2f19
Binary files /dev/null and b/static/img/cards-spare/10-SPADES.png differ
diff --git a/static/img/cards-spare/11-CLUBS.png b/static/img/cards-spare/11-CLUBS.png
new file mode 100644
index 00000000..70c085bb
Binary files /dev/null and b/static/img/cards-spare/11-CLUBS.png differ
diff --git a/static/img/cards-spare/11-DIAMONDS.png b/static/img/cards-spare/11-DIAMONDS.png
new file mode 100644
index 00000000..2b987300
Binary files /dev/null and b/static/img/cards-spare/11-DIAMONDS.png differ
diff --git a/static/img/cards-spare/11-HEARTS.png b/static/img/cards-spare/11-HEARTS.png
new file mode 100644
index 00000000..99d1ef3b
Binary files /dev/null and b/static/img/cards-spare/11-HEARTS.png differ
diff --git a/static/img/cards-spare/11-SPADES.png b/static/img/cards-spare/11-SPADES.png
new file mode 100644
index 00000000..ca080355
Binary files /dev/null and b/static/img/cards-spare/11-SPADES.png differ
diff --git a/static/img/cards-spare/12-CLUBS.png b/static/img/cards-spare/12-CLUBS.png
new file mode 100644
index 00000000..fd9d3b52
Binary files /dev/null and b/static/img/cards-spare/12-CLUBS.png differ
diff --git a/static/img/cards-spare/12-DIAMONDS.png b/static/img/cards-spare/12-DIAMONDS.png
new file mode 100644
index 00000000..465d9627
Binary files /dev/null and b/static/img/cards-spare/12-DIAMONDS.png differ
diff --git a/static/img/cards-spare/12-HEARTS.png b/static/img/cards-spare/12-HEARTS.png
new file mode 100644
index 00000000..f0f7d791
Binary files /dev/null and b/static/img/cards-spare/12-HEARTS.png differ
diff --git a/static/img/cards-spare/12-SPADES.png b/static/img/cards-spare/12-SPADES.png
new file mode 100644
index 00000000..f8d5debb
Binary files /dev/null and b/static/img/cards-spare/12-SPADES.png differ
diff --git a/static/img/cards-spare/13-CLUBS.png b/static/img/cards-spare/13-CLUBS.png
new file mode 100644
index 00000000..cf1f723b
Binary files /dev/null and b/static/img/cards-spare/13-CLUBS.png differ
diff --git a/static/img/cards-spare/13-DIAMONDS.png b/static/img/cards-spare/13-DIAMONDS.png
new file mode 100644
index 00000000..17ba2c23
Binary files /dev/null and b/static/img/cards-spare/13-DIAMONDS.png differ
diff --git a/static/img/cards-spare/13-HEARTS.png b/static/img/cards-spare/13-HEARTS.png
new file mode 100644
index 00000000..7274ece8
Binary files /dev/null and b/static/img/cards-spare/13-HEARTS.png differ
diff --git a/static/img/cards-spare/13-SPADES.png b/static/img/cards-spare/13-SPADES.png
new file mode 100644
index 00000000..3072ea35
Binary files /dev/null and b/static/img/cards-spare/13-SPADES.png differ
diff --git a/static/img/cards-spare/rights.md b/static/img/cards-spare/rights.md
new file mode 100644
index 00000000..36c265cd
--- /dev/null
+++ b/static/img/cards-spare/rights.md
@@ -0,0 +1 @@
+With reference from https://github.com/jmk82/pokergame
diff --git a/static/img/cards/02-CLUBS.png b/static/img/cards/02-CLUBS.png
new file mode 100644
index 00000000..291ed975
Binary files /dev/null and b/static/img/cards/02-CLUBS.png differ
diff --git a/static/img/cards/02-DIAMONDS.png b/static/img/cards/02-DIAMONDS.png
new file mode 100644
index 00000000..4deee7cc
Binary files /dev/null and b/static/img/cards/02-DIAMONDS.png differ
diff --git a/static/img/cards/02-HEARTS.png b/static/img/cards/02-HEARTS.png
new file mode 100644
index 00000000..75a014f3
Binary files /dev/null and b/static/img/cards/02-HEARTS.png differ
diff --git a/static/img/cards/02-SPADES.png b/static/img/cards/02-SPADES.png
new file mode 100644
index 00000000..1ce0ffe8
Binary files /dev/null and b/static/img/cards/02-SPADES.png differ
diff --git a/static/img/cards/03-CLUBS.png b/static/img/cards/03-CLUBS.png
new file mode 100644
index 00000000..076ab318
Binary files /dev/null and b/static/img/cards/03-CLUBS.png differ
diff --git a/static/img/cards/03-DIAMONDS.png b/static/img/cards/03-DIAMONDS.png
new file mode 100644
index 00000000..8ee0b4b9
Binary files /dev/null and b/static/img/cards/03-DIAMONDS.png differ
diff --git a/static/img/cards/03-HEARTS.png b/static/img/cards/03-HEARTS.png
new file mode 100644
index 00000000..8e74673f
Binary files /dev/null and b/static/img/cards/03-HEARTS.png differ
diff --git a/static/img/cards/03-SPADES.png b/static/img/cards/03-SPADES.png
new file mode 100644
index 00000000..f9e06b4f
Binary files /dev/null and b/static/img/cards/03-SPADES.png differ
diff --git a/static/img/cards/04-CLUBS.png b/static/img/cards/04-CLUBS.png
new file mode 100644
index 00000000..8be9e089
Binary files /dev/null and b/static/img/cards/04-CLUBS.png differ
diff --git a/static/img/cards/04-DIAMONDS.png b/static/img/cards/04-DIAMONDS.png
new file mode 100644
index 00000000..70e82e83
Binary files /dev/null and b/static/img/cards/04-DIAMONDS.png differ
diff --git a/static/img/cards/04-HEARTS.png b/static/img/cards/04-HEARTS.png
new file mode 100644
index 00000000..ceecbfe0
Binary files /dev/null and b/static/img/cards/04-HEARTS.png differ
diff --git a/static/img/cards/04-SPADES.png b/static/img/cards/04-SPADES.png
new file mode 100644
index 00000000..95abe3e7
Binary files /dev/null and b/static/img/cards/04-SPADES.png differ
diff --git a/static/img/cards/05-CLUBS.png b/static/img/cards/05-CLUBS.png
new file mode 100644
index 00000000..bde97776
Binary files /dev/null and b/static/img/cards/05-CLUBS.png differ
diff --git a/static/img/cards/05-DIAMONDS.png b/static/img/cards/05-DIAMONDS.png
new file mode 100644
index 00000000..bb925255
Binary files /dev/null and b/static/img/cards/05-DIAMONDS.png differ
diff --git a/static/img/cards/05-HEARTS.png b/static/img/cards/05-HEARTS.png
new file mode 100644
index 00000000..d923456f
Binary files /dev/null and b/static/img/cards/05-HEARTS.png differ
diff --git a/static/img/cards/05-SPADES.png b/static/img/cards/05-SPADES.png
new file mode 100644
index 00000000..53a1aad2
Binary files /dev/null and b/static/img/cards/05-SPADES.png differ
diff --git a/static/img/cards/06-CLUBS.png b/static/img/cards/06-CLUBS.png
new file mode 100644
index 00000000..a9660a03
Binary files /dev/null and b/static/img/cards/06-CLUBS.png differ
diff --git a/static/img/cards/06-DIAMONDS.png b/static/img/cards/06-DIAMONDS.png
new file mode 100644
index 00000000..78a80ad0
Binary files /dev/null and b/static/img/cards/06-DIAMONDS.png differ
diff --git a/static/img/cards/06-HEARTS.png b/static/img/cards/06-HEARTS.png
new file mode 100644
index 00000000..361643ef
Binary files /dev/null and b/static/img/cards/06-HEARTS.png differ
diff --git a/static/img/cards/06-SPADES.png b/static/img/cards/06-SPADES.png
new file mode 100644
index 00000000..40242a71
Binary files /dev/null and b/static/img/cards/06-SPADES.png differ
diff --git a/static/img/cards/07-CLUBS.png b/static/img/cards/07-CLUBS.png
new file mode 100644
index 00000000..9d6b5455
Binary files /dev/null and b/static/img/cards/07-CLUBS.png differ
diff --git a/static/img/cards/07-DIAMONDS.png b/static/img/cards/07-DIAMONDS.png
new file mode 100644
index 00000000..6ad5f15b
Binary files /dev/null and b/static/img/cards/07-DIAMONDS.png differ
diff --git a/static/img/cards/07-HEARTS.png b/static/img/cards/07-HEARTS.png
new file mode 100644
index 00000000..19b89a2e
Binary files /dev/null and b/static/img/cards/07-HEARTS.png differ
diff --git a/static/img/cards/07-SPADES.png b/static/img/cards/07-SPADES.png
new file mode 100644
index 00000000..b9f1b93d
Binary files /dev/null and b/static/img/cards/07-SPADES.png differ
diff --git a/static/img/cards/08-CLUBS.png b/static/img/cards/08-CLUBS.png
new file mode 100644
index 00000000..cec743cb
Binary files /dev/null and b/static/img/cards/08-CLUBS.png differ
diff --git a/static/img/cards/08-DIAMONDS.png b/static/img/cards/08-DIAMONDS.png
new file mode 100644
index 00000000..ed129512
Binary files /dev/null and b/static/img/cards/08-DIAMONDS.png differ
diff --git a/static/img/cards/08-HEARTS.png b/static/img/cards/08-HEARTS.png
new file mode 100644
index 00000000..fb39723c
Binary files /dev/null and b/static/img/cards/08-HEARTS.png differ
diff --git a/static/img/cards/08-SPADES.png b/static/img/cards/08-SPADES.png
new file mode 100644
index 00000000..b6b3b381
Binary files /dev/null and b/static/img/cards/08-SPADES.png differ
diff --git a/static/img/cards/09-CLUBS.png b/static/img/cards/09-CLUBS.png
new file mode 100644
index 00000000..2174db58
Binary files /dev/null and b/static/img/cards/09-CLUBS.png differ
diff --git a/static/img/cards/09-DIAMONDS.png b/static/img/cards/09-DIAMONDS.png
new file mode 100644
index 00000000..0b933fb0
Binary files /dev/null and b/static/img/cards/09-DIAMONDS.png differ
diff --git a/static/img/cards/09-HEARTS.png b/static/img/cards/09-HEARTS.png
new file mode 100644
index 00000000..7b196d6d
Binary files /dev/null and b/static/img/cards/09-HEARTS.png differ
diff --git a/static/img/cards/09-SPADES.png b/static/img/cards/09-SPADES.png
new file mode 100644
index 00000000..3c3b5ffb
Binary files /dev/null and b/static/img/cards/09-SPADES.png differ
diff --git a/static/img/cards/10-CLUBS.png b/static/img/cards/10-CLUBS.png
new file mode 100644
index 00000000..18af741d
Binary files /dev/null and b/static/img/cards/10-CLUBS.png differ
diff --git a/static/img/cards/10-DIAMONDS.png b/static/img/cards/10-DIAMONDS.png
new file mode 100644
index 00000000..3bbc4e06
Binary files /dev/null and b/static/img/cards/10-DIAMONDS.png differ
diff --git a/static/img/cards/10-HEARTS.png b/static/img/cards/10-HEARTS.png
new file mode 100644
index 00000000..3eb83d72
Binary files /dev/null and b/static/img/cards/10-HEARTS.png differ
diff --git a/static/img/cards/10-SPADES.png b/static/img/cards/10-SPADES.png
new file mode 100644
index 00000000..0b3d2947
Binary files /dev/null and b/static/img/cards/10-SPADES.png differ
diff --git a/static/img/cards/11-CLUBS-V2.png b/static/img/cards/11-CLUBS-V2.png
new file mode 100644
index 00000000..8bad6105
Binary files /dev/null and b/static/img/cards/11-CLUBS-V2.png differ
diff --git a/static/img/cards/11-CLUBS.png b/static/img/cards/11-CLUBS.png
new file mode 100644
index 00000000..5e003be2
Binary files /dev/null and b/static/img/cards/11-CLUBS.png differ
diff --git a/static/img/cards/11-DIAMONDS-V2.png b/static/img/cards/11-DIAMONDS-V2.png
new file mode 100644
index 00000000..04947851
Binary files /dev/null and b/static/img/cards/11-DIAMONDS-V2.png differ
diff --git a/static/img/cards/11-DIAMONDS.png b/static/img/cards/11-DIAMONDS.png
new file mode 100644
index 00000000..131a9773
Binary files /dev/null and b/static/img/cards/11-DIAMONDS.png differ
diff --git a/static/img/cards/11-HEARTS-V2.png b/static/img/cards/11-HEARTS-V2.png
new file mode 100644
index 00000000..03cdfd47
Binary files /dev/null and b/static/img/cards/11-HEARTS-V2.png differ
diff --git a/static/img/cards/11-HEARTS.png b/static/img/cards/11-HEARTS.png
new file mode 100644
index 00000000..bf342bcb
Binary files /dev/null and b/static/img/cards/11-HEARTS.png differ
diff --git a/static/img/cards/11-SPADES-V2.png b/static/img/cards/11-SPADES-V2.png
new file mode 100644
index 00000000..734e4b17
Binary files /dev/null and b/static/img/cards/11-SPADES-V2.png differ
diff --git a/static/img/cards/11-SPADES.png b/static/img/cards/11-SPADES.png
new file mode 100644
index 00000000..f539c19c
Binary files /dev/null and b/static/img/cards/11-SPADES.png differ
diff --git a/static/img/cards/12-CLUBS-V2.png b/static/img/cards/12-CLUBS-V2.png
new file mode 100644
index 00000000..949839c1
Binary files /dev/null and b/static/img/cards/12-CLUBS-V2.png differ
diff --git a/static/img/cards/12-CLUBS.png b/static/img/cards/12-CLUBS.png
new file mode 100644
index 00000000..7be5f9a9
Binary files /dev/null and b/static/img/cards/12-CLUBS.png differ
diff --git a/static/img/cards/12-DIAMONDS-V2.png b/static/img/cards/12-DIAMONDS-V2.png
new file mode 100644
index 00000000..791b273d
Binary files /dev/null and b/static/img/cards/12-DIAMONDS-V2.png differ
diff --git a/static/img/cards/12-DIAMONDS.png b/static/img/cards/12-DIAMONDS.png
new file mode 100644
index 00000000..928f6501
Binary files /dev/null and b/static/img/cards/12-DIAMONDS.png differ
diff --git a/static/img/cards/12-HEARTS-V2.png b/static/img/cards/12-HEARTS-V2.png
new file mode 100644
index 00000000..c95e2df0
Binary files /dev/null and b/static/img/cards/12-HEARTS-V2.png differ
diff --git a/static/img/cards/12-HEARTS.png b/static/img/cards/12-HEARTS.png
new file mode 100644
index 00000000..21839e68
Binary files /dev/null and b/static/img/cards/12-HEARTS.png differ
diff --git a/static/img/cards/12-SPADES-V2.png b/static/img/cards/12-SPADES-V2.png
new file mode 100644
index 00000000..c6c69cac
Binary files /dev/null and b/static/img/cards/12-SPADES-V2.png differ
diff --git a/static/img/cards/12-SPADES.png b/static/img/cards/12-SPADES.png
new file mode 100644
index 00000000..7983d034
Binary files /dev/null and b/static/img/cards/12-SPADES.png differ
diff --git a/static/img/cards/13-CLUBS-v1.png b/static/img/cards/13-CLUBS-v1.png
new file mode 100644
index 00000000..ebde974a
Binary files /dev/null and b/static/img/cards/13-CLUBS-v1.png differ
diff --git a/static/img/cards/13-CLUBS.png b/static/img/cards/13-CLUBS.png
new file mode 100644
index 00000000..68e57747
Binary files /dev/null and b/static/img/cards/13-CLUBS.png differ
diff --git a/static/img/cards/13-DIAMONDS-V2.png b/static/img/cards/13-DIAMONDS-V2.png
new file mode 100644
index 00000000..4c9aaf20
Binary files /dev/null and b/static/img/cards/13-DIAMONDS-V2.png differ
diff --git a/static/img/cards/13-DIAMONDS.png b/static/img/cards/13-DIAMONDS.png
new file mode 100644
index 00000000..e21d6a0a
Binary files /dev/null and b/static/img/cards/13-DIAMONDS.png differ
diff --git a/static/img/cards/13-HEARTS-V2.png b/static/img/cards/13-HEARTS-V2.png
new file mode 100644
index 00000000..27d235a3
Binary files /dev/null and b/static/img/cards/13-HEARTS-V2.png differ
diff --git a/static/img/cards/13-HEARTS.png b/static/img/cards/13-HEARTS.png
new file mode 100644
index 00000000..1d3c468d
Binary files /dev/null and b/static/img/cards/13-HEARTS.png differ
diff --git a/static/img/cards/13-SPADES-V2.png b/static/img/cards/13-SPADES-V2.png
new file mode 100644
index 00000000..b7abc52a
Binary files /dev/null and b/static/img/cards/13-SPADES-V2.png differ
diff --git a/static/img/cards/13-SPADES.png b/static/img/cards/13-SPADES.png
new file mode 100644
index 00000000..2edbbc14
Binary files /dev/null and b/static/img/cards/13-SPADES.png differ
diff --git a/static/img/cards/14-CLUBS.png b/static/img/cards/14-CLUBS.png
new file mode 100644
index 00000000..42bf5ec9
Binary files /dev/null and b/static/img/cards/14-CLUBS.png differ
diff --git a/static/img/cards/14-DIAMONDS.png b/static/img/cards/14-DIAMONDS.png
new file mode 100644
index 00000000..79cd3b8a
Binary files /dev/null and b/static/img/cards/14-DIAMONDS.png differ
diff --git a/static/img/cards/14-HEARTS.png b/static/img/cards/14-HEARTS.png
new file mode 100644
index 00000000..b4221240
Binary files /dev/null and b/static/img/cards/14-HEARTS.png differ
diff --git a/static/img/cards/14-SPADES-V2.png b/static/img/cards/14-SPADES-V2.png
new file mode 100644
index 00000000..fbc3a2dc
Binary files /dev/null and b/static/img/cards/14-SPADES-V2.png differ
diff --git a/static/img/cards/14-SPADES.png b/static/img/cards/14-SPADES.png
new file mode 100644
index 00000000..103f56d1
Binary files /dev/null and b/static/img/cards/14-SPADES.png differ
diff --git a/static/img/cards/BACK.png b/static/img/cards/BACK.png
new file mode 100644
index 00000000..000b640b
Binary files /dev/null and b/static/img/cards/BACK.png differ
diff --git a/static/img/cards/JOKER-BLACK.png b/static/img/cards/JOKER-BLACK.png
new file mode 100644
index 00000000..000b640b
Binary files /dev/null and b/static/img/cards/JOKER-BLACK.png differ
diff --git a/static/img/cards/JOKER-RED.png b/static/img/cards/JOKER-RED.png
new file mode 100644
index 00000000..55b3ef9b
Binary files /dev/null and b/static/img/cards/JOKER-RED.png differ
diff --git a/static/img/circleOfCards.gif b/static/img/circleOfCards.gif
new file mode 100644
index 00000000..ee338743
Binary files /dev/null and b/static/img/circleOfCards.gif differ
diff --git a/styles/form-input.css b/styles/form-input.css
new file mode 100644
index 00000000..119df969
--- /dev/null
+++ b/styles/form-input.css
@@ -0,0 +1,86 @@
+input[type="range"] {
+ -webkit-appearance: none;
+ width: 100%;
+}
+input[type="range"]:focus {
+ outline: none;
+}
+input[type="range"]::-webkit-slider-runnable-track {
+ width: 100%;
+ height: 1px;
+ cursor: pointer;
+ animate: 0.2s;
+ box-shadow: 0px 0px 0px #000000;
+ background: #808000;
+ border-radius: 50px;
+ border: 1px solid #808000;
+}
+input[type="range"]::-webkit-slider-thumb {
+ box-shadow: 1px 1px 1px #000000;
+ border: 1px solid #000000;
+ height: 6px;
+ width: 6px;
+ border-radius: 50px;
+ background: #ffffff;
+ cursor: pointer;
+ -webkit-appearance: none;
+ margin-top: -3.5px;
+}
+input[type="range"]:focus::-webkit-slider-runnable-track {
+ background: #808000;
+}
+input[type="range"]::-moz-range-track {
+ width: 100%;
+ height: 1px;
+ cursor: pointer;
+ animate: 0.2s;
+ box-shadow: 0px 0px 0px #000000;
+ background: #808000;
+ border-radius: 50px;
+ border: 1px solid #808000;
+}
+input[type="range"]::-moz-range-thumb {
+ box-shadow: 1px 1px 1px #000000;
+ border: 1px solid #000000;
+ height: 6px;
+ width: 6px;
+ border-radius: 50px;
+ background: #ffffff;
+ cursor: pointer;
+}
+input[type="range"]::-ms-track {
+ width: 100%;
+ height: 1px;
+ cursor: pointer;
+ animate: 0.2s;
+ background: transparent;
+ border-color: transparent;
+ color: transparent;
+}
+input[type="range"]::-ms-fill-lower {
+ background: #808000;
+ border: 1px solid #808000;
+ border-radius: 100px;
+ box-shadow: 0px 0px 0px #000000;
+}
+input[type="range"]::-ms-fill-upper {
+ background: #808000;
+ border: 1px solid #808000;
+ border-radius: 100px;
+ box-shadow: 0px 0px 0px #000000;
+}
+input[type="range"]::-ms-thumb {
+ box-shadow: 1px 1px 1px #000000;
+ border: 1px solid #000000;
+ height: 6px;
+ width: 6px;
+ border-radius: 50px;
+ background: #ffffff;
+ cursor: pointer;
+}
+input[type="range"]:focus::-ms-fill-lower {
+ background: #808000;
+}
+input[type="range"]:focus::-ms-fill-upper {
+ background: #808000;
+}
diff --git a/styles/main.css b/styles/main.css
new file mode 100644
index 00000000..4f97cc2f
--- /dev/null
+++ b/styles/main.css
@@ -0,0 +1,222 @@
+body {
+ display: flex;
+ justify-content: center;
+}
+
+.img-loading-div {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.img-loading {
+ border-radius: 200px;
+ display: flex;
+}
+.poker-root {
+ margin-top: 10vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 80%;
+}
+
+.card-img {
+ display: flex;
+ height: 25vh;
+ aspect-ratio: 2 / 3;
+ border: 1px solid white;
+ box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
+ transition: 0.3s;
+}
+
+.poker-name-display {
+ display: flex;
+ min-height: 1rem;
+ background-color: olive;
+ margin-top: 1.5rem;
+ border-radius: 10px;
+ color: white;
+ max-width: fit-content;
+ justify-content: center;
+ align-items: center;
+ padding: 5px 20px;
+ white-space: nowrap;
+}
+
+.poker-name-banner {
+ display: flex;
+ margin-top: 10px;
+ border-radius: 10px;
+ color: olive;
+ justify-content: center;
+
+ text-align: center;
+}
+
+.poker-name-input {
+ display: flex;
+ font-size: 0.8rem;
+ margin: 0px;
+ padding: 0px;
+ border-radius: 5px;
+ transition-property: border-right;
+ transition-duration: 5000ms;
+ border-right: 1px solid black; /* added property */
+}
+
+.poker-game-wrapper-prob-checkboxes-desc {
+ width: fit-content;
+ display: flex;
+ white-space: nowrap;
+ margin: 0;
+ padding: 0;
+}
+
+.poker-game-wrapper-prob-checkboxes-group {
+ margin-top: 10px;
+ display: flex;
+ flex-direction: row;
+ width: fit-content;
+}
+.poker-game-config-button-start,
+.button-restart,
+.action-bet-button {
+ display: flex;
+ margin-top: 10px;
+ padding: 0px 10px;
+
+ background-color: white;
+ color: olive;
+ border: 1px solid grey;
+ border-radius: 3px;
+}
+
+.poker-game-config-button-start:hover,
+.button-restart:hover,
+.action-bet-button:hover {
+ background-color: olive;
+ color: white;
+}
+.poker-game-checkbox-label {
+ display: flex;
+ flex-direction: row;
+ margin: 0 0 0 10px;
+
+ padding: 0;
+ justify-content: center;
+ align-items: center;
+}
+.poker-game-config-checkbox {
+ accent-color: olive;
+}
+.poker-wrapper-game-modes {
+ display: flex;
+ margin: 2rem 0 0 0;
+}
+.poker-game-mode-stud-seven {
+ display: flex;
+ background-color: white;
+ border: 1px solid grey;
+ border-top-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+ text-align: center;
+
+ background: linear-gradient(to left, black 40%, firebrick 60%, black 80%);
+ background-size: 200% auto;
+
+ color: #000;
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+
+ animation: wipe 1s linear infinite;
+}
+
+@keyframes wipe {
+ to {
+ background-position: 200% center;
+ }
+}
+
+.poker-game-config-checkbox-desc {
+ display: flex;
+ white-space: nowrap;
+ margin: 0 0 0 10px;
+ padding: 0;
+}
+
+.card-holder {
+ display: flex;
+ height: 100%;
+ width: auto;
+}
+
+.poker-hand {
+ display: flex;
+ flex-direction: row;
+ margin-top: 30px;
+}
+
+.action-area {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 30px;
+}
+
+input[type="range"] {
+ -webkit-appearance: none;
+ color: olive;
+}
+
+.slider-bet {
+ display: flex;
+ margin: 0;
+ padding: 0;
+ max-width: 100px;
+}
+.moneys {
+ justify-content: center;
+ margin-top: 10px;
+}
+
+.money-- {
+ width: fit-content;
+}
+
+.in-game-header {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+}
+
+table {
+ display: flex;
+ flex-direction: column;
+}
+
+thead,
+tbody {
+ display: flex;
+ flex-direction: column;
+}
+
+tr {
+ display: flex;
+ flex-direction: row;
+}
+
+th,
+td {
+ display: flex;
+ white-space: nowrap;
+ padding: 1px 7px;
+ width: 300px;
+ text-align: center;
+ justify-content: center;
+ align-items: center;
+ border: 1px solid black;
+}