diff --git a/index.html b/index.html index 379f081..6ed6c10 100644 --- a/index.html +++ b/index.html @@ -26,7 +26,7 @@ - +
?
diff --git a/script.js b/script.js index b523d1d..981bc91 100644 --- a/script.js +++ b/script.js @@ -1,21 +1,44 @@ -var deck = {}; -var hand = {}; +var maxNumber = 8; // the maximum number on cards (cards always start at one) +var colors = [ "red", "blue", "yellow" ]; // the available numbers +var handSize = (colors.length * 2) - 1; // the maximum hand size +var seriesLength = colors.length; // the minimum required length of series +var pointsMultiplier = 10; // useless "big number feel" multiplier +var sameNumberExtra = 1; // extra points given for sets of same numbers +var sameColorExtra = 4; // extra points given for sets of the same color if they are in a series + +var autoDraw = false; // whether the script should automatically draw on start and on discard +var autoRecommend = true; // whether the script should automatically recommend on change + +var allCards = undefined; +var deck = undefined; +var hand = undefined; +var recommendations = undefined; var deckElement = document.getElementById('deck'); var handElement = document.getElementById('hand'); +var recommendElement = document.getElementById('result'); -var colors = [ "red", "blue", "yellow" ]; -var handSize = 5; -var maxNumber = 8; +reset(); -fillDeck(); -updateUI(); +function toggleAutoDraw(element) { + if (element.checked) { + autoDraw = true; + fillHand(); + } + else { + autoDraw = false; + } +} function cardToId(card) { return card.color + "_" + card.number.toString(); } -function cardToElement(card, forDeck = false) { +function idToCard(cardId) { + return allCards[cardId]; +} + +function cardToElement(card, forDeck = false, drawn = false) { var element = document.createElement("okey-card"); element.innerHTML = card.number.toString(); @@ -29,7 +52,7 @@ function cardToElement(card, forDeck = false) { element.setAttribute("onclick", "discard('" + cardToId(card) + "')"); } - if (forDeck && card.drawn) { + if (forDeck && drawn) { element.classList.add("drawn-" + card.color); } else { @@ -40,82 +63,309 @@ function cardToElement(card, forDeck = false) { return element.outerHTML; } -function isHandFull() { +function isHandFull(thisHand = null) { + if (thisHand == null) { + thisHand = hand; + } + return Object.entries(hand).length >= handSize; } -function fillDeck() { +function generateCards() { for (let color of colors) { for (let i = 1; i <= maxNumber; i++) { let card = { color: color, - number: i, - drawn: false + number: i }; + card.id = cardToId(card); - deck[cardToId(card)] = card; + allCards[card.id] = card; } } } -function discard(cardId) { - delete hand[cardId]; - updateUI(); +function fillDeck() { + for (let cardId of Object.keys(allCards)) { + deck.push(cardId); + } } -function drawCard(cardId) { - if (!deck[cardId].drawn && !isHandFull()) { - hand[cardId] = deck[cardId]; - deck[cardId].drawn = true; +function fillHand() { + let draws = handSize - hand.length; + for (let i = 0; i < draws; i++) { + let deckPos = Math.floor(Math.random()*deck.length); + drawCard(deck[deckPos]); + } +} + +function discard(cardId, thisHand = null) { + let handGiven = true; + if (thisHand == null) { + thisHand = hand; + handGiven = false; + } + + if (thisHand.indexOf(cardId) !== -1) { + thisHand.splice(thisHand.indexOf(cardId), 1); + } + + if (autoDraw) { + fillHand(); + } + + if (!handGiven) { updateUI(); } } +function drawCard(cardId, thisHand = null, thisDeck = null) { + let handGiven = true; + if (thisHand == null) { + thisHand = hand; + handGiven = false; + } + + if (thisDeck == null) { + thisDeck = deck; + handGiven = false; + } + + if (!isHandFull(thisHand) && thisDeck.includes(cardId)) { + thisDeck.splice(thisDeck.indexOf(cardId), 1); + thisHand.push(cardId); + } + + if (!handGiven) { + updateUI(); + } +} + +function recommend() { + recommendOfDepth(); + updateUI(true); +} + function getPatterns(thisHand = null) { if (thisHand == null) { thisHand = hand; } - let patterns = []; + let patterns = {}; - for (let i = 1; i <= maxNumber; i++) { - sameNumber = 0; - for (let card of Object.values(thisHand)) { - if (card.number == i) { - sameNumber++; + cardsByNumbers = []; + cardColorsByNumbers = []; + + // set up empty arrays for each number + for (let i = 0; i < maxNumber; i++) { + cardsByNumbers.push([]); + cardColorsByNumbers.push([]); + } + + // fill them with the current hand's cards + for (let cardId of thisHand) { + let card = idToCard(cardId); + cardColorsByNumbers[card.number - 1].push(card.color); + cardsByNumbers[card.number - 1].push(card); + } + + // check if there are "color" amount of any + for (let i = 0; i < maxNumber; i++) { + if (cardsByNumbers[i].length >= colors.length) { + let patternName = (i + 1).toString(); + for (let j = 1; j < colors.length; j++) { + patternName += "-" + (i + 1).toString(); } - } - - if (sameNumber >= colors.length) { - let patternName = i + "-" + i + "-" + i; - patterns[patternName] = (i + 1) * 10; + + // "i" is lagging behind, adjustment needed + let points = (i + 1 + sameNumberExtra) + patterns[patternName] = {}; + patterns[patternName].pattern = patternName; + patterns[patternName].points = points; } } - console.log(Object.keys(patterns)); + // check if there are series present + for (let i = 0; i <= (maxNumber - seriesLength); i++) { + let isSeries = true; + let theseColors = colors; + for (let numberOffset = 0; numberOffset < seriesLength; numberOffset++) { + if (cardColorsByNumbers[i + numberOffset] < 1) { + isSeries = false; + break; + } + + // intersect + theseColors = [theseColors, cardColorsByNumbers[i + numberOffset]].reduce((a, c) => a.filter(i => c.includes(i))); + } + + if (isSeries) { + let patternName = (i + 1).toString(); + for (let numberOffset = 1; numberOffset < seriesLength; numberOffset++) { + patternName += "-" + (i + numberOffset + 1).toString(); + } + + // "i" is lagging behind, adjustment needed + let points = (i + 1); + + // check if they are of the same color + if (theseColors.length > 0) { + points = ((i + 1) + sameColorExtra); + patternName += "*"; + } + + patterns[patternName] = {}; + patterns[patternName].pattern = patternName; + patterns[patternName].points = points; + } + } - patterns = patterns.sort((a,b) => (a.last_nom > b.last_nom) ? -1 : ((b.last_nom > a.last_nom) ? 1 : 0)); + let points = Object.values(patterns).map(pattern => pattern.points); + let maxPoints = Math.max(...points); - return Object.keys(patterns); + let selectedPatterns = Object.values(patterns).filter(pattern => pattern.points == maxPoints); + + return selectedPatterns; } -function updateUI() { +function getHandPoints(hand = null) { + let patterns = getPatterns(hand); + if (patterns.length > 0) { + return patterns[0].points; + } + + return 0; +} + +function recommendOfDepth(depth = 4) { + var t0 = performance.now(); + + recommendations = []; + recommendThrowaway(depth); + let current = getHandPoints(); + if (current > 0) { + let choice = { + burnedCard: "CASH OUT", + points: current, + chance: 1 + }; + recommendations.unshift(choice); + } + + var t1 = performance.now(); + console.log("Took " + (t1 - t0) + " milliseconds."); +} + +function recommendThrowaway(depth = 0, thisHand = null, thisDeck = null, chance = 1, startingFunction = true) { + if (thisHand == null) { + thisHand = hand; + } + + if (thisDeck == null) { + thisDeck = deck; + } + + if (Object.keys(thisHand).length < 1) { + return [0, "nothing", 0]; + } + + let decks = []; + let hands = []; + let choices = []; + + let loopDuration = Math.min(handSize, thisHand.length); + + for (let i = 0; i < loopDuration; i++) { + for (let deckIndex = 0; deckIndex < thisDeck.length; deckIndex++) { + let newHand = [...thisHand]; + let newDeck = [...thisDeck]; + + choices.push({ burnedCard: newHand[i], index: i }); + + newHand.splice(i, 1); + newHand.push(newDeck[deckIndex]); + newDeck.splice(deckIndex, 1); + + hands.push(newHand); + decks.push(newDeck); + } + } + + if (startingFunction) { + console.log(choices); + for (let choice of choices) { + let points = getHandPoints(hands[choice.index]); + if (points > 0) { + choice.points = points; + + let thisChance = 1; + if (thisDeck.length > 0) { + thisChance = chance * (1/thisDeck.length); + } + + choice.chance = thisChance + recommendations.push(choice); + } + } + } +} + +function getRecommendationText(rec) { + if (Object.keys(allCards).includes(rec.burnedCard)) { + return "Burn " + + rec.burnedCard.replace("_", " ") + + " and earn potentially " + + (rec.points * pointsMultiplier) + + " points. (" + + (rec.chance * 100).toFixed(2) + + "% chance)"; + } + return "Cash out and get " + + (rec.points * pointsMultiplier) + + " points instantly!"; +} + +function updateUI(skipAuto = false) { + if (autoRecommend && !skipAuto) { + recommend(); + } + recommendationArray = []; + for (let recommendation of recommendations) { + recommendationArray.push(getRecommendationText(recommendation)); + + } + recommendationArray = [...new Set(recommendationArray)]; + recommendElement.innerHTML = recommendationArray.join("
"); + handElement.innerHTML = ""; - for (let card of Object.values(hand)) { + for (let cardId of hand) { + let card = idToCard(cardId); handElement.innerHTML += cardToElement(card); } deckElement.innerHTML = ""; - for (let card of Object.values(deck)) { - deckElement.innerHTML += cardToElement(card, true); + for (let cardId of Object.keys(allCards)) { + let card = idToCard(cardId); + if (deck.includes(cardId)) { + deckElement.innerHTML += cardToElement(card, true); + } + else { + deckElement.innerHTML += cardToElement(card, true, true); + } } } function reset() { - deck = {}; - hand = {}; + allCards = {}; + deck = []; + hand = []; + recommendations = []; + generateCards(); fillDeck(); + if (autoDraw) { + fillHand(); + } updateUI(); } \ No newline at end of file