Skip to content

Instantly share code, notes, and snippets.

@lttr
Last active December 27, 2017 20:23
Show Gist options
  • Save lttr/13cc84d1278d652976069dd252f95032 to your computer and use it in GitHub Desktop.
Save lttr/13cc84d1278d652976069dd252f95032 to your computer and use it in GitHub Desktop.
Risk game battle analysis

risk game

3 separate files for quick web start. HTML, CSS, JS.

Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Web starter</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<section class="turn">
<div class="units">
<input id="input-attacker" type="text" class="units__input">
<input id="input-defender" type="text" class="units__input">
</div>
<div class="controls">
<button id="mode" class="btn">Mode FAST</button>
<button id="start" class="btn">Start</button>
<button id="reset" class="btn">Reset</button>
</div>
<div class="dices-all">
<div class="dices-player">
<div id="attacker-first" class="dice dice--attacker one"></div>
<div id="attacker-second" class="dice dice--attacker two"></div>
<div id="attacker-third" class="dice dice--attacker three"></div>
</div>
<div class="dices-player">
<div id="defender-first" class="dice dice--defender four"></div>
<div id="defender-second" class="dice dice--defender six"></div>
</div>
</div>
<div class="messages">
<p id="message"></p>
</div>
</section>
<section class="results">
<table class="results-table">
<thead>
<tr>
<td>Strategy</td>
<td>Attackers</td>
<td>Defenders</td>
<td>Attackers won %</td>
<td>Defenders won %</td>
<td>Draws %</td>
</tr>
</thead>
<tbody id="results">
</tbody>
</table>
</section>
</main>
<script src="script.js"></script>
</body>
</html>

MIT License

Copyright (c) 2017 Lukáš Trumm

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
const defaultMessage = `Start the fight!`
const defaultUnits = {
attacker: 8,
defender: 6
}
const defaultInterval = 1000
const defaultRolls = {
attacker: [1, 2, 3],
defender: [4, 5]
}
const strategies = ['NoStrategy', 'FiveAndSixIsTooMuch',
'SixIsTooMuch', 'FirstTwoMoreThenNine']
const inputElements = {
attacker: document.querySelector("#input-attacker"),
defender: document.querySelector("#input-defender")
}
const startButton = document.querySelector("#start")
const resetButton = document.querySelector("#reset")
const modeButton = document.querySelector("#mode")
const attackerFirst = document.querySelector("#attacker-first")
const attackerSecond = document.querySelector("#attacker-second")
const attackerThird = document.querySelector("#attacker-third")
const defenderFirst = document.querySelector("#defender-first")
const defenderSecond = document.querySelector("#defender-second")
const dicesElements = {
attacker: [attackerFirst, attackerSecond, attackerThird],
defender: [defenderFirst, defenderSecond]
}
const rollClasses = ['one', 'two', 'three', 'four', 'five', 'six']
const messageElement = document.querySelector("#message")
const resultsElement = document.querySelector("#results")
let timeout = undefined
let mode = 'FAST'
window.onload = function() {
reset()
}
function reset() {
clearTimeout(timeout)
updateUnits(inputElements, defaultUnits)
startButton.addEventListener('click', start)
resetButton.addEventListener('click', reset)
modeButton.addEventListener('click', toggleMode)
updateDices(dicesElements, defaultRolls, rollClasses)
updateMessage(messageElement)
}
function toggleMode() {
if (mode === 'FAST') {
mode = 'SLOW'
} else if (mode === 'SLOW') {
mode = 'FAST'
}
modeButton.textContent = `Mode ${mode}`
}
function updateUnits(inputs, units) {
inputs.attacker.value = units.attacker
inputs.defender.value = units.defender
}
function updateDices(dices, rolls, rollClasses) {
Object.entries(dices).forEach(([player, dicesElements]) => {
dicesElements.forEach((diceElement, index) => {
const number = rolls[player][index]
if (number) {
diceElement.style.display = 'block'
diceElement.classList.remove(...rollClasses)
const randomRollClass = rollClasses[number - 1]
diceElement.classList.add(randomRollClass)
} else {
diceElement.style.display = 'none'
}
})
})
}
function updateMessage(messageElement, result) {
if (result === undefined) {
messageElement.textContent = defaultMessage
} else {
messageElement.textContent = `
Attacker lost ${result.attackerLost} units.
Defender lost ${result.defenderLost} units.
`
}
}
function loadUnits(inputs) {
return {
attacker: Number(inputs.attacker.value),
defender: Number(inputs.defender.value)
}
}
function updateResults(units, results, strategy) {
attackerPercent = Math.round(results.attacker / results.counter * 1000) / 10
defenderPercent = Math.round(results.defender / results.counter * 1000) / 10
drawPercent = Math.round(results.draw / results.counter * 1000) / 10
let highlight = ""
if (units.attacker === units.defender) {
highlight = "highlight"
}
const resultsTemplate = `
<tr class="${highlight}">
<td>${strategy}</td>
<td>${units.attacker}</td>
<td>${units.defender}</td>
<td>${attackerPercent}</td>
<td>${defenderPercent}</td>
<td>${drawPercent}</td>
</tr>
`
resultsElement.insertAdjacentHTML('beforeend', resultsTemplate)
}
function start() {
let units = loadUnits(inputElements)
let counter = 1
if (mode === 'SLOW') {
console.log('START')
console.log(doBattle(units, counter))
} else if (mode === 'FAST') {
const repeats = 10000
const maxUnits = 15
for (let attackers = 2; attackers <= maxUnits; attackers++) {
// for (let defenders = attackers - 1; defenders <= attackers + 1; defenders++) {
for (let defenders = attackers; defenders <= attackers; defenders++) {
strategies.forEach(strategy => {
setTimeout(() => {
units = {
attacker: attackers,
defender: defenders
}
const results = {
attacker: 0,
defender: 0,
draw: 0,
counter: 0
}
for (let index = 0; index < repeats; index++) {
counter = 1
const result = doBattle(units, counter, index, strategy)
results[result] += 1
results.counter++
}
console.log(units)
console.log(results)
updateResults(units, results, strategy)
updateUnits(inputElements, units)
updateDices(dicesElements, generateRolls(units), rollClasses)
}, 1000)
})
}
}
}
}
function doBattle(units, counter, index, strategy) {
if (units.defender <= 0 || units.attacker <= 1) {
if (mode === 'SLOW') {
clearTimeout(timeout)
}
if (units.attacker < units.defender) {
return 'defender'
} else if (units.attacker === units.defender) {
return 'draw'
} else {
return 'attacker'
}
}
const stats = {
counter: counter,
attackerUnits: units.attacker,
defenderUnits: units.defender
}
const rolls = generateRolls(units, strategy)
const result = resolveWinner(rolls)
units = {
attacker: units.attacker - result.attackerLost,
defender: units.defender - result.defenderLost
}
if (mode === 'SLOW') {
updateUnits(inputElements, units)
updateDices(dicesElements, rolls, rollClasses)
updateMessage(messageElement, result)
}
stats.attackerLost = result.attackerLost
stats.defenderLost = result.defenderLost
if (mode === 'SLOW') {
console.log(stats)
timeout = setTimeout(doBattle, defaultInterval,
units, counter + 1)
} else if (mode === 'FAST') {
return doBattle(units, counter + 1)
}
}
function resolveWinner(rolls) {
const sortedRolls = {
attacker: rolls.attacker.sort().reverse(),
defender: rolls.defender.sort().reverse()
}
const result = {
attackerLost: 0,
defenderLost: 0,
}
let index = 0
while (index < sortedRolls.attacker.length
&& index < sortedRolls.defender.length) {
const attack = sortedRolls.attacker[index]
const defend = sortedRolls.defender[index]
if (attack > defend) {
result.defenderLost++;
} else {
result.attackerLost++;
}
index++;
}
return result
}
function generateRolls(units, strategy) {
let attackerDicesNumber = 3
let defenderDicesNumber = 2
if (units.attacker === 3) {
attackerDicesNumber = 2
} else if (units.attacker === 2) {
attackerDicesNumber = 1
}
if (units.defender === 1) {
defenderDicesNumber = 1
}
const attacker = randomRollArray(attackerDicesNumber)
if (strategy === 'SixIsTooMuch') {
if (attacker.includes(6) || attacker.includes(5)) {
defenderDicesNumber = 1
}
}
if (strategy === 'FiveAndSixIsTooMuch') {
if (attacker.includes(5) || attacker.includes(6)) {
defenderDicesNumber = 1
}
}
if (strategy === 'FirstTwoMoreThenNine') {
if ((attacker[0] + attacker[1]) >= 9) {
defenderDicesNumber = 1
}
}
return {
attacker,
defender: randomRollArray(defenderDicesNumber)
}
}
function randomRollArray(length, max = 6) {
return Array.from(new Array(length)).map(() => {
return Math.floor(Math.random() * max) + 1
})
}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
/* Variables */
:root {
--color-attacker: rgb(255, 55, 55);
--color-defender: rgb(53, 84, 255);
}
/* Turn */
.turn {
display: grid;
grid-template-columns: 10% 1fr 1fr 1fr 1.5fr 10%;
grid-template-areas:
". units controls dices messages ."
". results results results results ."
;
grid-column-gap: 1em;
justify-items: center;
align-items: center;
justify-content: space-around;
}
/* Units */
.units {
grid-area: units;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.units__input {
font-size: 1.7em;
text-align: center;
width: 2em;
height: 2em;
margin: 1em;
}
/* Controls */
.controls {
grid-area: controls;
display: flex;
flex-direction: column;
}
.btn {
font-size: 1em;
margin-top: 1em;
background-color: white;
border: 1px solid grey;
border-radius: 0.5em;
padding: 0.5em;
}
.btn:hover {
cursor: pointer;
background-color: lightgrey;
}
.btn:focus {
outline-style: unset;
}
/* Dices */
.dices-all {
grid-area: dices;
display: flex;
flex-direction: column;
justify-content: center;
}
.dices-player {
display: flex;
flex-direction: row;
justify-content: center;
}
.dice {
width: 4em;
height: 4em;
margin: 0.5em;
border-radius: 0.7em;
background-size: 2.4em;
background-position: center center;
background-repeat: no-repeat;
}
.dice--attacker {
background-color: var(--color-attacker);
}
.dice--defender {
background-color: var(--color-defender);
}
.one {
background-image: url("./one.svg");
}
.two {
background-image: url("./two.svg");
}
.three {
background-image: url("./three.svg");
}
.four {
background-image: url("./four.svg");
}
.five {
background-image: url("./five.svg");
}
.six {
background-image: url("./six.svg");
}
/* Messages */
.messages {
grid-area: messages;
}
/* Results */
.results {
grid-area: results;
}
.results-table {
border-collapse: collapse;
border: 1px solid grey;
width: 70%;
margin: 2em auto;
}
.results-table thead {
background-color: lightgrey;
}
.results-table tr:hover {
background-color: lightgrey !important;
}
.results-table tr.highlight {
background-color: rgb(226, 226, 226);
}
.results-table td {
padding: 0.4em;
}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment