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; +}