diff options
Diffstat (limited to 'public/js/bowling.js')
| -rw-r--r-- | public/js/bowling.js | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/public/js/bowling.js b/public/js/bowling.js new file mode 100644 index 0000000..e65bf13 --- /dev/null +++ b/public/js/bowling.js @@ -0,0 +1,402 @@ +document.addEventListener("DOMContentLoaded", function() { + var graph = document.getElementById("graph") + var stats = document.getElementById("stats") + var scoreboard = document.getElementById("scoreboard") + + function generateEmptyGame() { + var frames = [] + for (var i = 0; i < 9; i++) { + frames.push({ + throws: [null, null], + score: null, + }) + } + + frames.push({ + throws: [null, null, null], + score: null, + }) + + return { + frames: frames, + currentFrame: 0, + isComplete: false, + } + } + + function scoreGame(game) { + var thisFrameScore + for (var i = 0; i < 10; i++) { + if (game.frames[i].throws[0] === null) { + break + } + + if (i === 9) { + var firstThrow = game.frames[i].throws[0] || 0 + var secondThrow = game.frames[i].throws[1] || 0 + var thirdThrow = game.frames[i].throws[2] || 0 + + thisFrameScore = firstThrow + secondThrow + thirdThrow + } else { + // Fetch the next two throws in case we get a mark + var firstBonusThrow = game.frames[i + 1].throws[0] || 0 + var secondBonusThrow + if (firstBonusThrow === 10) { + // If we're in the 9th frame, pick the second throw of + // the next frame as the 10th frame can have multiple + // strikes + if (i === 8) { + secondBonusThrow = game.frames[i + 1].throws[1] || 0 + } else { + secondBonusThrow = game.frames[i + 2].throws[0] || 0 + } + } else { + secondBonusThrow = game.frames[i + 1].throws[1] || 0 + } + + var firstThrow = game.frames[i].throws[0] || 0 + var secondThrow = game.frames[i].throws[1] || 0 + + if (firstThrow === 10) { + thisFrameScore = 10 + firstBonusThrow + secondBonusThrow + } else if (firstThrow + secondThrow === 10) { + thisFrameScore = 10 + firstBonusThrow + } else { + thisFrameScore = firstThrow + secondThrow + } + } + + if (i === 0) { + game.frames[i].score = thisFrameScore + } else { + game.frames[i].score = game.frames[i - 1].score + thisFrameScore + } + } + } + + function generateThrow(game) { + var isNewFrame = game.frames[game.currentFrame].throws[0] === null + + if (isNewFrame) { + var firstThrow = Math.floor(Math.random() * 11) + game.frames[game.currentFrame].throws[0] = firstThrow + + if (firstThrow === 10 && game.currentFrame !== 9) { + game.currentFrame++ + } + } else { + if (game.currentFrame !== 9) { + var lastThrow = game.frames[game.currentFrame].throws[0] + var nextThrow = Math.floor(Math.random() * (10 - lastThrow + 1)) + + game.frames[game.currentFrame].throws[1] = nextThrow + + if (game.currentFrame !== 9) { + game.currentFrame++ + } + } else { + var firstThrow = game.frames[game.currentFrame].throws[0] + var secondThrow = game.frames[game.currentFrame].throws[1] + + if (firstThrow === 10 && secondThrow === null) { + var secondThrow = Math.floor(Math.random() * 11) + game.frames[game.currentFrame].throws[1] = secondThrow + } else if (secondThrow === null) { + var secondThrow = Math.floor(Math.random() * (10 - firstThrow + 1)) + + game.frames[game.currentFrame].throws[1] = secondThrow + + if (firstThrow + secondThrow !== 10) { + game.isComplete = true + } + } else if (firstThrow === 10 && secondThrow === 10) { + var thirdThrow = Math.floor(Math.random() * 11) + game.frames[game.currentFrame].throws[2] = thirdThrow + game.isComplete = true + } else if (firstThrow === 10) { + var thirdThrow = Math.floor(Math.random() * (10 - secondThrow + 1)) + + game.frames[game.currentFrame].throws[2] = thirdThrow + game.isComplete = true + } else if (firstThrow + secondThrow === 10) { + var thirdThrow = Math.floor(Math.random() * 11) + game.frames[game.currentFrame].throws[2] = thirdThrow + game.isComplete = true + } + } + } + } + + function renderScoreBoard(game) { + var frame + scoreboard.innerHTML = "" + + var firstHalf = document.createElement("div") + firstHalf.classList.add("scoreboard-half") + + var secondHalf = document.createElement("div") + secondHalf.classList.add("scoreboard-half") + + for (var i = 0; i < 5; i++) { + frame = game.frames[i] + + firstHalf.appendChild(renderFrame(frame.score, frame.throws, i === 9)) + } + + var mobileSpacer = document.createElement("div") + mobileSpacer.classList.add("mobile-spacer") + firstHalf.appendChild(mobileSpacer) + + for (var i = 5; i < 10; i++) { + frame = game.frames[i] + + secondHalf.appendChild(renderFrame(frame.score, frame.throws, i === 9)) + } + + scoreboard.appendChild(firstHalf) + scoreboard.appendChild(secondHalf) + } + + function throwToString(points) { + if (points === null) { + return " " + } else if (points === 10) { + return "X" + } else if (points === 0) { + return "-" + } else { + return points + "" + } + } + + // Not the 10th though! + function frameToString(throws) { + if (throws[0] === 10) { + return ["X", ""] + } else if (throws[0] + throws[1] === 10) { + return [throwToString(throws[0]), "/"] + } else { + return throws.map(throwToString) + } + } + + function renderFrame(score, throws, isTenth) { + var contents + if (isTenth) { + // 10th frame has weird logic + if (throws === undefined) { + contents = ["", "", ""] + + // Two strikes = XX[third throw] + } else if (throws[0] === 10 && throws[1] === 10) { + contents = [ + throwToString(10), + throwToString(10), + throwToString(throws[2]), + ] + + // X[normal frame] + } else if (throws[0] === 10) { + contents = [throwToString(10)].concat(frameToString(throws.slice(1))) + + // [normal frame][bonus throw] + } else if (throws[0] + throws[1] === 10) { + contents = frameToString(throws.slice(0, 2)).concat([ + throwToString(throws[2]), + ]) + } else { + contents = frameToString(throws) + } + + // Make contents 3 items long... lol + if (contents.length < 3) { + contents.push("") + } + + if (contents.length < 3) { + contents.push("") + } + } else { + contents = frameToString(throws) + + if (contents.length < 2) { + contents.push("") + } + } + + var frame = document.createElement("div") + frame.classList.add("frame") + + contents.forEach(function(formattedThrow) { + var square = document.createElement("div") + square.classList.add("square") + square.innerHTML = formattedThrow + + frame.appendChild(square) + }) + + var scoreDiv = document.createElement("div") + scoreDiv.classList.add("score") + scoreDiv.innerHTML = score + + frame.appendChild(scoreDiv) + + return frame + } + + function renderGraph(graphDiv) { + graphDiv.innerHTML = "" + + var distribution = [] + for (var i = 0; i <= 300; i++) { + distribution[i] = scores[i] || 0 + } + var highestPopulation = Math.max.apply(null, distribution) + + var bar + for (var i = 0; i <= 300; i++) { + bar = document.createElement("div") + bar.classList.add("bar") + bar.style.height = ((scores[i] || 0) / highestPopulation) * 100 + "%" + graphDiv.appendChild(bar) + } + } + + function renderStats(statsDiv) { + var distribution = [] + for (var i = 0; i <= 300; i++) { + distribution[i] = scores[i] || 0 + } + + var numGames = distribution.reduce(function(a, b) { + return a + b + }) + + var allGames = [] + var totalPins = 0 + var minScore, maxScore + + var mode = 0, + modeIndex + + for (var i = 0; i <= 300; i++) { + for (var j = 0; j < distribution[i]; j++) { + allGames.push(i) + totalPins += i + } + + if (distribution[i] > 0) { + maxScore = i + + if (minScore === undefined) { + minScore = i + } + } + + if (distribution[i] > mode) { + mode = distribution[i] + modeIndex = i + } + } + + var mean = Math.floor((totalPins / numGames) * 100) / 100 + + // TODO: Odd/even + var median + if (allGames.length % 2 === 1) { + median = allGames[Math.floor(allGames.length / 2)] + } else { + median = + (allGames[Math.floor(allGames.length / 2)] + + allGames[Math.floor(allGames.length / 2) - 1]) / + 2 + } + + statsDiv.innerHTML = + "<div>" + + [ + '<span class="stats-label">Games:</span><span> ' + numGames + "</span>", + '<span class="stats-label">Min:</span><span> ' + minScore + "</span>", + '<span class="stats-label">Max:</span><span> ' + maxScore + "</span>", + '<span class="stats-label">Mean:</span><span> ' + mean + "</span>", + '<span class="stats-label">Median:</span><span> ' + median + "</span>", + '<span class="stats-label">Mode:</span><span> ' + + modeIndex + + " (" + + mode + + " games)</span>", + ].join("</div><div>") + + "</div>" + } + + var scores = [] + var game = generateEmptyGame() + renderScoreBoard(game) + + var throwButton = document.getElementById("throw") + var gameButton = document.getElementById("game") + var manyGamesButton = document.getElementById("many-games") + + function handleThrowClick(e) { + e.preventDefault() + + if (game.isComplete) { + game = generateEmptyGame() + } + + generateThrow(game) + scoreGame(game) + renderScoreBoard(game) + + if (game.isComplete) { + var score = game.frames[9].score + scores[score] = (scores[score] || 0) + 1 + renderGraph(graph) + renderStats(stats) + } + } + + function simulateGame() { + game = generateEmptyGame() + while (!game.isComplete) { + generateThrow(game) + } + + scoreGame(game) + renderScoreBoard(game) + + var score = game.frames[9].score + scores[score] = (scores[score] || 0) + 1 + renderGraph(graph) + } + + function handleGameClick(e) { + e.preventDefault() + simulateGame() + renderStats(stats) + } + + function handleManyClick(e) { + e.preventDefault() + var simulations = 0 + + ;(function run() { + simulations++ + simulateGame() + renderStats(stats) + + if (simulations < 100) { + setTimeout(run, 5) + } + })() + } + + throwButton.addEventListener("click", handleThrowClick) + throwButton.addEventListener("touchend", handleThrowClick) + + gameButton.addEventListener("click", handleGameClick) + gameButton.addEventListener("touchend", handleGameClick) + + manyGamesButton.addEventListener("click", handleManyClick) + manyGamesButton.addEventListener("touchend", handleManyClick) +}) |