diff --git a/DOM.js b/DOM.js new file mode 100644 index 00000000..59cebf7e --- /dev/null +++ b/DOM.js @@ -0,0 +1,232 @@ +let playerPoints = 100; +let betAmount = 0; +let dealCardsTurn = true; +let swapTurn = false; +const cardChangePosition = []; + +const createCard = (cardInfo) => { + const card = document.createElement("div"); + card.classList.add("card"); + + const name = document.createElement("div"); + name.classList.add("name", cardInfo.colour); + name.innerText = cardInfo.displayName; + + const suit = document.createElement("div"); + suit.classList.add("suit", cardInfo.colour); + suit.innerText = cardInfo.suitSymbol; + + const cardDetailsTop = document.createElement("div"); + cardDetailsTop.classList.add("card-details-top"); + cardDetailsTop.appendChild(name); + cardDetailsTop.appendChild(suit); + + const cardDetailsBottom = cardDetailsTop.cloneNode(true); + cardDetailsBottom.classList.add("card-details-bottom"); + + card.appendChild(cardDetailsTop); + card.appendChild(cardDetailsBottom); + + return card; +}; + +const buildCardElements = (hand) => { + const cardContainer = document.querySelector(".card-container"); + cardContainer.innerHTML = ""; + // cardContainer.classList.add("card-container"); + + for (let i = 0; i < hand.length; i++) { + const card = createCard(hand[i]); + card.addEventListener("click", (event) => { + cardClick(hand, event.currentTarget, i); + }); + + cardContainer.appendChild(card); + } + + // document.body.appendChild(cardContainer); +}; + +const replaceCardElements = (hand) => { + const originalCardContainer = document.querySelector(".card-container"); + originalCardContainer.innerHTML = ""; + + const newCardContainer = document.createElement("div"); + newCardContainer.classList.add("card-container"); + + for (let i = 0; i < hand.length; i++) { + const card = createCard(hand[i]); + card.addEventListener("click", (event) => { + cardClick(hand, event.currentTarget, i); + }); + + newCardContainer.appendChild(card); + } + + originalCardContainer.innerHTML = newCardContainer.innerHTML; +}; + +const cardClick = (hand, cardElement, cardPosition) => { + console.log("PLAYER CLICKED CARD", hand[cardPosition]); + + if (swapTurn) { + const arrayPosition = cardChangePosition.indexOf(cardPosition); + + if (arrayPosition === -1) { + cardChangePosition.push(cardPosition); + cardElement.classList.add("clicked-card"); + console.log(cardChangePosition); + return; + } else { + cardChangePosition.splice(arrayPosition, 1); + cardElement.classList.remove("clicked-card"); + console.log(cardChangePosition); + return; + } + } +}; + +const dealButtonClick = () => { + console.log("DEAL BUTTON CLICKED"); + + const currentPoints = document.querySelector(".player-points"); + const feedback = document.querySelector(".feedback-message"); + const input = document.querySelector(".bet-input"); + + if (dealCardsTurn) { + betAmount = Number(input.value); + input.value = ""; + console.log(betAmount); + + if (betAmount <= 0) { + feedback.classList.add("opaque"); + feedback.innerText = "Please input a valid bet amount"; + + currentPoints.innerHTML = `Player Points: ${playerPoints}
Bet Amount: 0`; + return; + } else if (betAmount > playerPoints) { + feedback.classList.add("opaque"); + feedback.innerText = "Insufficient points balance"; + + currentPoints.innerHTML = `Player Points: ${playerPoints}
Bet Amount: 0`; + return; + } + feedback.classList.remove("opaque"); + playerPoints -= betAmount; + currentPoints.innerHTML = `Player Points: ${playerPoints}
Bet Amount: ${betAmount}`; + + // Create shuffled deck + deck = shuffleCards(makeDeck()); + + // Create hand array of 5 cards + hand = []; + for (let i = 0; i < 5; i += 1) { + hand.push(deck.pop()); + } + + buildCardElements(hand); + + dealCardsTurn = false; + swapTurn = true; + input.disabled = true; + return; + } else if (swapTurn) { + for (let i = 0; i < cardChangePosition.length; i++) { + hand[cardChangePosition[i]] = deck.pop(); + } + + replaceCardElements(hand); + + const handFeedbackArray = calcHandScore(hand, betAmount); + const handFeedback = handFeedbackArray[0]; + const winnings = handFeedbackArray[1]; + + feedback.innerText = `${handFeedback}`; + feedback.classList.add("opaque"); + + if (winnings != 0) { + playerPoints = playerPoints + winnings + betAmount; + currentPoints.innerHTML = `Player Points: ${playerPoints}
Bet Amount: 0`; + } + + // Reset array + cardChangePosition.splice(0, cardChangePosition.length); + + dealCardsTurn = true; + swapTurn = false; + input.disabled = false; + return; + } +}; + +const resetButtonClick = () => { + console.log("RESET BUTTON CLICKED"); + + if (dealCardsTurn) { + console.log("RESET WORKED"); + playerPoints = 100; + + const currentPoints = document.querySelector(".player-points"); + const feedback = document.querySelector(".feedback-message"); + + feedback.classList.add("opaque"); + feedback.innerText = "Player points has been reset"; + + currentPoints.innerHTML = `Player Points: ${playerPoints}
Bet Amount: 0`; + } else { + return; + } +}; + +const buildPointsTracker = () => { + const pointsHeader = document.createElement("p"); + pointsHeader.classList.add("player-points"); + pointsHeader.innerHTML = `Player Points: ${playerPoints}
Bet Amount: 0`; + + document.body.appendChild(pointsHeader); +}; + +const buildPlayerInterface = () => { + const playerResponseGroup = document.createElement("div"); + playerResponseGroup.classList.add("response-container"); + + const betInput = document.createElement("input"); + betInput.setAttribute("type", "number"); + betInput.setAttribute("placeholder", "Input your bet here"); + betInput.classList.add("bet-input"); + playerResponseGroup.appendChild(betInput); + + const dealButton = document.createElement("button"); + dealButton.classList.add("deal-button"); + dealButton.innerText = "Deal"; + dealButton.addEventListener("click", dealButtonClick); + playerResponseGroup.appendChild(dealButton); + + const resetButton = document.createElement("button"); + resetButton.classList.add("reset-button"); + resetButton.innerText = "Reset"; + resetButton.addEventListener("click", resetButtonClick); + playerResponseGroup.appendChild(resetButton); + + document.body.appendChild(playerResponseGroup); +}; + +const buildFeedbackMessage = () => { + const feedbackMessage = document.createElement("p"); + feedbackMessage.classList.add("feedback-message"); + feedbackMessage.innerHTML = `Placeholder`; + + document.body.appendChild(feedbackMessage); +}; + +initGame = () => { + buildPointsTracker(); + buildPlayerInterface(); + buildFeedbackMessage(); + + const cardContainer = document.createElement("div"); + cardContainer.classList.add("card-container"); + document.body.appendChild(cardContainer); +}; + +initGame(); diff --git a/functions.js b/functions.js new file mode 100644 index 00000000..976f04d0 --- /dev/null +++ b/functions.js @@ -0,0 +1,272 @@ +let deck; +let hand; + +// Get a random index ranging from 0 (inclusive) to max (exclusive). +const getRandomIndex = (max) => Math.floor(Math.random() * max); + +// Shuffle an array of cards +const shuffleCards = (cards) => { + // Loop over the card deck array once + for (let currentIndex = 0; currentIndex < cards.length; currentIndex += 1) { + // Select a random index in the deck + const randomIndex = getRandomIndex(cards.length); + // Select the card that corresponds to randomIndex + const randomCard = cards[randomIndex]; + // Select the card that corresponds to currentIndex + const currentCard = cards[currentIndex]; + // Swap positions of randomCard and currentCard in the deck + cards[currentIndex] = randomCard; + cards[randomIndex] = currentCard; + } + // Return the shuffled deck + return cards; +}; + +const makeDeck = () => { + // Initialise an empty deck array + const newDeck = []; + // Initialise an array of the 4 suits in our deck. We will loop over this array. + const suits = ["hearts", "diamonds", "clubs", "spades"]; + const suitsSymbol = ["♥", "♦", "♣", "♠"]; + + // Loop over the suits array + for (let suitIndex = 0; suitIndex < suits.length; suitIndex += 1) { + // Store the current suit in a variable + const currentSuit = suits[suitIndex]; + const currentSuitSymbol = suitsSymbol[suitIndex]; + let currentColour; + + if (currentSuit == "hearts" || currentSuit == "diamonds") { + currentColour = "red"; + } else { + currentColour = "black"; + } + + // Loop from 1 to 13 to create all cards for a given suit + // Notice rankCounter starts at 1 and not 0, and ends at 13 and not 12. + // This is an example of a loop without an array. + for (let rankCounter = 1; rankCounter <= 13; rankCounter += 1) { + // By default, the card name is the same as rankCounter + let cardName = `${rankCounter}`; + let shortName = `${rankCounter}`; + // If rank is 1, 11, 12, or 13, set cardName to the ace or face card's name + if (cardName === "1") { + cardName = "Ace"; + shortName = "A"; + } else if (cardName === "11") { + cardName = "Jack"; + shortName = "J"; + } else if (cardName === "12") { + cardName = "Queen"; + shortName = "Q"; + } else if (cardName === "13") { + cardName = "King"; + shortName = "K"; + } + + // Create a new card with the current name, suit, and rank + const card = { + name: cardName, + suit: currentSuit, + suitSymbol: currentSuitSymbol, + rank: rankCounter, + displayName: shortName, + colour: currentColour, + }; + + // Add the new card to the deck + newDeck.push(card); + } + } + + // Return the completed card deck + return newDeck; +}; + +/** + * A function that takes in card hand & calculates the number of points based on that hand + * Checks for the categories ranking first, then checks for higher card if previous check is a draw + * In a draw situation, suits don't matter, higher card does (Ace highest) + * To check for straights, check every card tally equals to 1, sum of ranks is a mulitple of 5, diff of max & min is 4 (accomodate for Ace) + * To check for flush, suit tally equals to 5 + * To check for straight flush, combine checks for straights & flush + * --To check for 3 or 4 of a kind, card tally equals to 3 or 4 + * TO check for full house, 4 or 3 of a kind, double or single pair, check card tally occurence pattern + * @param a {number} category points + * @param b {number} high card points + * @return {number} a & b mulitply with each other + */ + +const getTallyValues = (tally) => { + const tallyObjectValues = Object.values(tally); + const descendingTallyObjectValues = tallyObjectValues.sort((a, b) => b - a); + return descendingTallyObjectValues; +}; + +const getTallyKeys = (tally) => { + const tallyObjectKeys = Object.keys(tally); + const descendingTallyObjectKeys = tallyObjectKeys.sort((a, b) => b - a); + return descendingTallyObjectKeys; +}; + +const checkFlush = (cardSuitTally) => { + const cardSuitTallyValues = getTallyValues(cardSuitTally); + if (cardSuitTallyValues[0] === 5) { + return true; + } else { + return false; + } +}; + +const checkStraight = (cardRankTally, checkAce) => { + const cardRankTallyValues = getTallyValues(cardRankTally).map((x) => + Number(x) + ); + let outputvalue = false; + const rankFrequency = [...new Set(cardRankTallyValues)]; + + if (rankFrequency.length != 1 && rankFrequency[0] != 1) { + return outputvalue; + } + + const cardRankTallyKeys = getTallyKeys(cardRankTally).map((x) => Number(x)); + let rankSum = cardRankTallyKeys.reduce((a, b) => a + b); + let rankMaxMinDiff = + Math.max(...cardRankTallyKeys) - Math.min(...cardRankTallyKeys); + + if (rankSum % 5 === 0 && rankMaxMinDiff === 4) { + outputvalue = true; + } else if (checkAce === true) { + // Ace rank changes from 1 to 14 + rankSum += 13; + // Diff between initial Ace rank & King = 12 + // Diff between modified Ace rank & 10 = 4 + rankMaxMinDiff -= 8; + + if (rankSum === 60 && rankMaxMinDiff === 4) { + outputvalue = true; + } + } + + return outputvalue; +}; + +// Placed after check straight & flush +const checkOtherCondition = (cardRankTally) => { + let outputvalue = "Something went wrong"; + const cardRankTallyValues = getTallyValues(cardRankTally); + + // cardRankTallyValues.length will never be less than 2 + if (cardRankTallyValues[0] === 1) { + outputvalue = "High Card"; + } else if (cardRankTallyValues[0] === 2 && cardRankTallyValues[1] === 2) { + outputvalue = "2 Pairs"; + } else if (cardRankTallyValues[0] === 2) { + outputvalue = "1 Pair"; + } else if (cardRankTallyValues[0] === 3 && cardRankTallyValues[1] === 2) { + outputvalue = "Full House"; + } else if (cardRankTallyValues[0] === 3) { + outputvalue = "3 of a Kind"; + } else if (cardRankTallyValues[0] === 4) { + outputvalue = "4 of a Kind"; + } + + return outputvalue; +}; + +const checkHighCard = () => {}; + +const calcHandScore = (hand, betAmount) => { + let outputvalue; + let cardRankTally = {}; + let cardSuitTally = {}; + let checkAce = false; + let highCardCounter = 0; + let winnings = 0; + + for (let i = 0; i < hand.length; i++) { + let cardRank = hand[i].rank; + let cardSuit = hand[i].suit; + + if (cardRank === 1) { + checkAce = true; + highCardCounter += 1; + } else if (cardRank >= 11) { + highCardCounter += 1; + } + + // Tallying card ranks & suits + if (cardRank in cardRankTally) { + cardRankTally[cardRank] += 1; + } else { + cardRankTally[cardRank] = 1; + } + + if (cardSuit in cardSuitTally) { + cardSuitTally[cardSuit] += 1; + } else { + cardSuitTally[cardSuit] = 1; + } + } + + console.log(cardRankTally); + console.log(cardSuitTally); + + // const cardRankTallyValues = getTallyValues(cardRankTally); + // const cardSuitTallyValues = getTallyValues(cardSuitTally); + + // console.log(cardRankTallyValues); + // console.log(cardSuitTallyValues); + + const otherCheck = checkOtherCondition(cardRankTally); + const straightHand = checkStraight(cardRankTally, checkAce); + const flushHand = checkFlush(cardSuitTally); + + if (straightHand && flushHand) { + console.log("straight flush"); + winnings = betAmount * 50; + outputvalue = `You won ${winnings} points with a Straight Flush!!`; + } else if (straightHand) { + console.log("straight"); + winnings = betAmount * 4; + outputvalue = `You won ${winnings} points with a Straight`; + } else if (flushHand) { + console.log("flush"); + winnings = betAmount * 6; + outputvalue = `You won ${winnings} points with a Flush`; + } else if (otherCheck === "High Card" && highCardCounter > 0) { + console.log("high"); + winnings = betAmount * 1; + outputvalue = `You won ${winnings} points with a High Card (Jack or higher)`; + console.log("High Card Counter: " + highCardCounter); + } else if (otherCheck === "High Card") { + console.log("all other"); + outputvalue = `You didn't win anything 😢`; + } else if (otherCheck === "2 Pairs") { + console.log("2 pairs"); + winnings = betAmount * 2; + outputvalue = `You won ${winnings} points with 2 Pairs`; + } else if (otherCheck === "1 Pair") { + console.log("1 pair"); + winnings = betAmount * 1; + outputvalue = `You won ${winnings} points with a Pair`; + } else if (otherCheck === "Full House") { + console.log("full house"); + winnings = betAmount * 9; + outputvalue = `You won ${winnings} points with a Full House`; + } else if (otherCheck === "3 of a Kind") { + console.log("3 of a Kind"); + winnings = betAmount * 3; + outputvalue = `You won ${winnings} points with 3 of a kind`; + } else if (otherCheck === "4 of a Kind") { + console.log("4 of a Kind"); + winnings = betAmount * 25; + outputvalue = `You won ${winnings} points with 4 of a kind!`; + } + + return [outputvalue, winnings]; +}; + +// Refer to testHands.js to whats the current hand +// calcHandScore returns the number of points a given hand earns. +const pointsForHand = calcHandScore(playerHand); diff --git a/index.html b/index.html new file mode 100644 index 00000000..2fc354bf --- /dev/null +++ b/index.html @@ -0,0 +1,37 @@ + + + + Poker + + + + + + + +

Poker

+

Welcome to the game of Poker!

+
    +
  1. Declare the amount of points to bet
  2. +
  3. Click on the "Deal" button to deal the cards
  4. +
  5. + You may select the card(s) that you want to swap with another card +
  6. +
  7. + Once done, click on the "Deal" button again to confirm your selection +
  8. +
  9. Points are awarded based on your hand
  10. +
+ + + + + + diff --git a/styles.css b/styles.css new file mode 100644 index 00000000..29cd87f9 --- /dev/null +++ b/styles.css @@ -0,0 +1,109 @@ +body { + background-color: pink; + font-family: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", + "Lucida Sans", Arial, sans-serif; +} + +h1 { + text-align: center; + font-size: 4 5rem; + font-family: Brush Script MT; + margin: 0%; +} + +h3 { + text-align: center; + margin-top: 0%; +} + +.instructions { + display: table; + margin: 0 auto; +} + +ol { + /* list-style: none; */ + padding-left: 25px; +} + +li { + margin-bottom: 3px; +} + +.player-points { + font-weight: bolder; + text-align: center; +} + +.response-container { + display: flex; + justify-content: center; +} + +.feedback-message { + opacity: 0; + font-weight: bold; + text-align: center; +} + +.opaque { + opacity: 1; +} + +/* .bet-input { + display: inline-block; + width: 40%; +} + +.deal-button { + display: inline-block; + height: 50%; +} */ + +.card-container { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; +} + +.card { + padding: 5px; + margin: 10px; + background-color: white; + display: flex; + height: 140px; + width: 85px; + /* vertical-align: top; */ + border-radius: 5px; + text-align: center; + justify-content: space-between; +} + +.clicked-card { + position: relative; + background-color: gray; +} + +.suit { + margin: -5px; + font-size: 20px; +} + +.name { + margin: 0px; + font-size: 20px; + font-weight: bold; + font-family: sans-serif; +} + +.red { + color: red; +} + +.card-details-bottom { + transform: rotate(180deg); +} + +.disable-click { + pointer-events: none; +} diff --git a/testHands.js b/testHands.js new file mode 100644 index 00000000..86ab66c1 --- /dev/null +++ b/testHands.js @@ -0,0 +1,98 @@ +// // Straight hand +// const playerHand = [ +// { rank: 2, suit: "hearts", name: "2" }, +// { rank: 3, suit: "diamonds", name: "3" }, +// { rank: 5, suit: "spades", name: "5" }, +// { rank: 4, suit: "spades", name: "4" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // Flush hand +// const playerHand = [ +// { rank: 2, suit: "hearts", name: "2" }, +// { rank: 3, suit: "hearts", name: "3" }, +// { rank: 5, suit: "hearts", name: "5" }, +// { rank: 10, suit: "hearts", name: "4" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // Straight flush hand +// const playerHand = [ +// { rank: 10, suit: "hearts", name: "10" }, +// { rank: 11, suit: "hearts", name: "J" }, +// { rank: 12, suit: "hearts", name: "Q" }, +// { rank: 13, suit: "hearts", name: "K" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // Full house +// const playerHand = [ +// { rank: 10, suit: "hearts", name: "10" }, +// { rank: 10, suit: "spades", name: "10" }, +// { rank: 10, suit: "clubs", name: "10" }, +// { rank: 13, suit: "clubs", name: "K" }, +// { rank: 13, suit: "hearts", name: "K" }, +// ]; + +// // 4 of a kind +// const playerHand = [ +// { rank: 2, suit: "hearts", name: "2" }, +// { rank: 2, suit: "diamonds", name: "2" }, +// { rank: 2, suit: "spades", name: "2" }, +// { rank: 2, suit: "clubs", name: "2" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // 3 of a kind +// const playerHand = [ +// { rank: 3, suit: "hearts", name: "3" }, +// { rank: 2, suit: "diamonds", name: "2" }, +// { rank: 2, suit: "spades", name: "2" }, +// { rank: 2, suit: "clubs", name: "2" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // 2 pairs +// const playerHand = [ +// { rank: 2, suit: "hearts", name: "2" }, +// { rank: 2, suit: "diamonds", name: "2" }, +// { rank: 4, suit: "spades", name: "4" }, +// { rank: 4, suit: "clubs", name: "4" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // 1 pair +// const playerHand = [ +// { rank: 2, suit: "hearts", name: "2" }, +// { rank: 2, suit: "diamonds", name: "2" }, +// { rank: 5, suit: "spades", name: "5" }, +// { rank: 8, suit: "clubs", name: "8" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // High card +// const playerHand = [ +// { rank: 10, suit: "hearts", name: "10" }, +// { rank: 8, suit: "diamonds", name: "8" }, +// { rank: 6, suit: "spades", name: "6" }, +// { rank: 5, suit: "clubs", name: "5" }, +// { rank: 1, suit: "hearts", name: "Ace" }, +// ]; + +// // All other +// const playerHand = [ +// { rank: 10, suit: "hearts", name: "10" }, +// { rank: 8, suit: "diamonds", name: "8" }, +// { rank: 6, suit: "spades", name: "6" }, +// { rank: 5, suit: "clubs", name: "5" }, +// { rank: 2, suit: "hearts", name: "2" }, +// ]; + +// Random +const playerHand = [ + { rank: 12, suit: "clubs", name: "Q" }, + { rank: 7, suit: "diamonds", name: "7" }, + { rank: 13, suit: "diamonds", name: "K" }, + { rank: 9, suit: "diamonds", name: "9" }, + { rank: 1, suit: "clubs", name: "Ace" }, +];