I'm working on a personal game project to hone my CSS, HTML, and vanilla JS skills. I would appreciate any feedback on my small dice game project.
The main logic of the game is run by this function:
getScore: function(results) {
let message = '';
if (results.toString() === WIN) {
message = `${WIN} You Win!`;
} else if (results.toString() === LOSS) {
message = `${LOSS} You Lose`;
} else if (results[0] === results[1] && results[1] === results[2]) {
message = `Trips! ${results[0]}`;
} else if (results[0] === results[1]) {
message = `You scored: ${results[2]}`;
} else if (results[1] === results[2]) {
message = `You scored: ${results[0]}`;
} else {
message = 'Roll again...';
}
return message;
},
Essentially a player rolls until two dice match, the outlier is the player's score. Special cases are: '1,2,3': instant loss '4,5,6': instant win If a player rolls all the same number, the opponent would have to roll three matching numbers that are higher than the first player's score i.e.('4,4,4' > '1,1,1'). The only roll that can beat'6,6,6' is '4,5,6'.
I still need to add the logic for playing against a human or NPC.
Here is the full code:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description" content="Ceelo: Three Dice" />
<title>Ceelo</title>
<link rel="stylesheet" href="styles.css" />
<link rel="stylesheet" href="dice.css">
<link rel="stylesheet" href="animations.css">
</head>
<body>
<div class="score">Let's Roll</div>
<div class="scene">
<div class="dice-display">
<div class="dice dice-one idle">
<div class="face face-front">
<div class="front-pip-1">⬤</div>
<div class="front-pip-2">⬤</div>
<div class="front-pip-3">⬤</div>
</div>
<div class="face face-back">
<div class="back-pip-1">⬤</div>
<div class="back-pip-2">⬤</div>
<div class="back-pip-3">⬤</div>
<div class="back-pip-4">⬤</div>
</div>
<div class="face face-right">
<div class="right-pip-1">⬤</div>
<div class="right-pip-2">⬤</div>
<div class="right-pip-3">⬤</div>
<div class="right-pip-4">⬤</div>
<div class="right-pip-5">⬤</div>
</div>
<div class="face face-left">
<div class="left-pip-1">⬤</div>
<div class="left-pip-2">⬤</div>
</div>
<div class="face face-top">
<div class="top-pip-1">⬤</div>
<div class="top-blank-1"></div>
<div class="top-blank-2"></div>
</div>
<div class="face face-bottom">
<div class="bottom-pip-1">⬤</div>
<div class="bottom-pip-2">⬤</div>
<div class="bottom-pip-3">⬤</div>
<div class="bottom-pip-4">⬤</div>
<div class="bottom-pip-5">⬤</div>
<div class="bottom-pip-6">⬤</div>
</div>
</div>
</div>
<div class="dice-display">
<div class="dice dice-two idle">
<div class="face face-front">
<div class="front-pip-1">⬤</div>
<div class="front-pip-2">⬤</div>
<div class="front-pip-3">⬤</div>
</div>
<div class="face face-back">
<div class="back-pip-1">⬤</div>
<div class="back-pip-2">⬤</div>
<div class="back-pip-3">⬤</div>
<div class="back-pip-4">⬤</div>
</div>
<div class="face face-right">
<div class="right-pip-1">⬤</div>
<div class="right-pip-2">⬤</div>
<div class="right-pip-3">⬤</div>
<div class="right-pip-4">⬤</div>
<div class="right-pip-5">⬤</div>
</div>
<div class="face face-left">
<div class="left-pip-1">⬤</div>
<div class="left-pip-2">⬤</div>
</div>
<div class="face face-top">
<div class="top-pip-1">⬤</div>
<div class="top-blank-1"></div>
<div class="top-blank-2"></div>
</div>
<div class="face face-bottom">
<div class="bottom-pip-1">⬤</div>
<div class="bottom-pip-2">⬤</div>
<div class="bottom-pip-3">⬤</div>
<div class="bottom-pip-4">⬤</div>
<div class="bottom-pip-5">⬤</div>
<div class="bottom-pip-6">⬤</div>
</div>
</div>
</div>
<div class="dice-display">
<div class="dice dice-three idle">
<div class="face face-front">
<div class="front-pip-1">⬤</div>
<div class="front-pip-2">⬤</div>
<div class="front-pip-3">⬤</div>
</div>
<div class="face face-back">
<div class="back-pip-1">⬤</div>
<div class="back-pip-2">⬤</div>
<div class="back-pip-3">⬤</div>
<div class="back-pip-4">⬤</div>
</div>
<div class="face face-right">
<div class="right-pip-1">⬤</div>
<div class="right-pip-2">⬤</div>
<div class="right-pip-3">⬤</div>
<div class="right-pip-4">⬤</div>
<div class="right-pip-5">⬤</div>
</div>
<div class="face face-left">
<div class="left-pip-1">⬤</div>
<div class="left-pip-2">⬤</div>
</div>
<div class="face face-top">
<div class="top-pip-1">⬤</div>
<div class="top-blank-1"></div>
<div class="top-blank-2"></div>
</div>
<div class="face face-bottom">
<div class="bottom-pip-1">⬤</div>
<div class="bottom-pip-2">⬤</div>
<div class="bottom-pip-3">⬤</div>
<div class="bottom-pip-4">⬤</div>
<div class="bottom-pip-5">⬤</div>
<div class="bottom-pip-6">⬤</div>
</div>
</div>
</div>
</div>
<div class="controls">
<button id="roll">Roll</button>
</div>
<script src="main.js"></script>
</body>
</html>
main.js
'using-strict';
const LOSS = '1,2,3';
const WIN = '4,5,6';
const INITIAL_TRANSFORM_STATE = 'idle'
var roll = function() {
let sides = 6;
return Math.floor(sides * Math.random()) + 1;
};
const view = {
roll: document.getElementById('roll'),
dice1: {o: document.querySelector('.dice-one'), state: INITIAL_TRANSFORM_STATE},
dice2: {o: document.querySelector('.dice-two'), state: INITIAL_TRANSFORM_STATE},
dice3: {o: document.querySelector('.dice-three'), state: INITIAL_TRANSFORM_STATE},
score: document.querySelector('.score'),
updateDice: function(results) {
handleDiceAnimation(this.dice1);
handleDiceAnimation(this.dice2);
handleDiceAnimation(this.dice3);
setTimeout(()=>{
showResult(this.dice1, results[0]);
showResult(this.dice2, results[1]);
showResult(this.dice3, results[2]);
},820)
},
updateScore: function(message) {
this.score.textContent = message;
},
};
const game = {
turn: function() {
let rollResult = [roll(), roll(), roll()];
let resultSorted = [...rollResult].sort(); // sort results and return new array
view.updateDice(rollResult);
setTimeout(()=>{
view.updateScore(this.getScore(resultSorted));
}, 1000);
},
getScore: function(results) {
let message = '';
if (results.toString() === WIN) {
message = `${WIN} You Win!`;
} else if (results.toString() === LOSS) {
message = `${LOSS} You Lose`;
} else if (results[0] === results[1] && results[1] === results[2]) {
message = `Trips! ${results[0]}`;
} else if (results[0] === results[1]) {
message = `You scored: ${results[2]}`;
} else if (results[1] === results[2]) {
message = `You scored: ${results[0]}`;
} else {
message = 'Roll again...';
}
return message;
},
};
view.roll.addEventListener('click', () => game.turn(), false);
var handleDiceAnimation = dice => {
if (dice.state === 'idle'){
dice.o.classList.remove('idle');
}
dice.o.classList.remove('spin');
void dice.o.offsetWidth;
dice.o.classList.add('spin');
}
var showResult = (dice, value) => {
dice.o.classList.remove(dice.state);
void dice.o.offsetWidth;
if (value === 1) {
dice.o.classList.add('show-top');
dice.state = 'show-top';
} else if (value === 2) {
dice.o.classList.add('show-left');
dice.state = 'show-left';
} else if (value === 3) {
dice.o.classList.add('show-front');
dice.state = 'show-front';
} else if (value === 4) {
dice.o.classList.add('show-back');
dice.state = 'show-back';
} else if (value === 5) {
dice.o.classList.add('show-right');
dice.state = 'show-right';
} else if (value === 6) {
dice.o.classList.add('show-bottom');
dice.state = 'show-bottom';
}
}
animations.css
.idle {
animation: idle linear infinite 6s;
}
@keyframes idle {
from {
transform: translateZ(-75px) rotateX(0deg) rotateY(0deg);
}
to {
transform: translateZ(-75px) rotateX(360deg) rotateY(360deg);
}
}
.spin {
animation: spin 0.8s linear 1;
}
@keyframes spin {
0% {
-webkit-transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 0deg);
transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 0deg);
}
50% {
-webkit-transform: translateZ(-75px) scale(1.4) rotate3d(-1, 1, 0, 180deg);
transform: translateZ(-75px) scale(1.4) rotate3d(-1, 1, 0, 180deg);
}
100% {
-webkit-transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 360deg);
transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 360deg);
}
}
.show-top { transform: translateZ(-75px) rotateX(-90deg); }
.show-front { transform: translateZ(-75px) rotateY( 0deg); }
.show-right { transform: translateZ(-75px) rotateY( -90deg); }
.show-back { transform: translateZ(-75px) rotateY(-180deg); }
.show-left { transform: translateZ(-75px) rotateY( 90deg); }
.show-top { transform: translateZ(-75px) rotateX( -90deg); }
.show-bottom { transform: translateZ(-75px) rotateX( 90deg); }
dice.css
.dice-display {
width: 150px;
height: 150px;
perspective: 450px;
}
.dice {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transform: translateZ(-75px);
transition: transform 0.4s;
}
.dice-one {
}
.dice-two {
}
.dice-three {
}
.face {
position: absolute;
width: 150px;
height: 150px;
display: grid;
grid-template-columns: 50px, 50px, 50px;
grid-template-rows: 50px, 50px, 50px;
border: 1px solid hsla(0, 100%, 50%, 0.2);
color: whitesmoke;
font-size: 24pt;
}
/* 3 */
.face-front {
background: hsla(0, 100%, 50%, 0.6);
transform: rotateY(0deg) translateZ(75px);
}
.front-pip-1 {
grid-column: 3 / 4;
grid-row: 1 / 2;
place-self: center;
}
.front-pip-2 {
grid-column: 2 / 3;
grid-row: 2 / 3;
place-self: center;
}
.front-pip-3 {
grid-column: 1 / 2;
grid-row: 3 / 4;
place-self: center;
}
/* 4 */
.face-back {
background: hsla(0, 100%, 50%, 0.6);
transform: rotateY(180deg) translateZ(75px);
}
.back-pip-1 {
grid-column: 1 / 2;
grid-row: 1 / 2;
place-self: center;
}
.back-pip-2 {
grid-column: 3 / 4;
grid-row: 1 / 2;
place-self: center;
}
.back-pip-3 {
grid-column: 1 / 2;
grid-row: 3 / 4;
place-self: center;
}
.back-pip-4 {
grid-column: 3 / 4;
grid-row: 3 / 4;
place-self: center;
}
/* 5 */
.face-right {
background: hsla(0, 100%, 50%, 0.6);
transform: rotateY(90deg) translateZ(75px);
}
.right-pip-1 {
grid-column: 1 / 2;
grid-row: 1 / 2;
place-self: center;
}
.right-pip-2 {
grid-column: 3 / 4;
grid-row: 1 / 2;
place-self: center;
}
.right-pip-3 {
grid-column: 2 / 3;
grid-row: 2 / 3;
place-self: center;
}
.right-pip-4 {
grid-column: 1 / 2;
grid-row: 3 / 4;
place-self: center;
}
.right-pip-5 {
grid-column: 3 / 4;
grid-row: 3 / 4;
place-self: center;
}
/* 2 */
.face-left {
background: hsla(0, 100%, 50%, 0.6);
transform: rotateY(-90deg) translateZ(75px);
}
.left-pip-1 {
grid-column: 3 / 4;
grid-row: 1 / 2;
place-self: center;
}
.left-pip-2 {
grid-column: 1 / 2;
grid-row: 3 / 4;
place-self: center;
}
/* 1 */
.face-top {
background: hsla(0, 100%, 50%, 0.6);
transform: rotateX(90deg) translateZ(75px);
}
.top-pip-1 {
grid-column: 2 / 3;
grid-row: 2 / 3;
place-self: center;
}
.top-blank-1 {
grid-column: 1 / 4;
grid-row: 1 / 2;
place-self: center;
}
.top-blank-2 {
grid-column: 1 / 4;
grid-row: 3 / 4;
place-self: center;
}
/* 6 */
.face-bottom {
background: hsla(0, 100%, 50%, 0.6);
transform: rotateX(-90deg) translateZ(75px);
}
.bottom-pip-1 {
grid-column: 1 / 2;
grid-row: 1 / 2;
place-self: center;
}
.bottom-pip-2 {
grid-column: 1 / 2;
grid-row: 2 / 3;
place-self: center;
}
.bottom-pip-3 {
grid-column: 1 / 2;
grid-row: 3 / 4;
place-self: center;
}
.bottom-pip-4 {
grid-column: 3 / 4;
grid-row: 1 / 2;
place-self: center;
}
.bottom-pip-5 {
grid-column: 3 / 4;
grid-row: 2 / 3;
place-self: center;
}
.bottom-pip-6 {
grid-column: 3 / 4;
grid-row: 3 / 4;
place-self: center;
}
styles.css
* {
box-sizing: border-box;
}
body {
background: repeating-linear-gradient(#071a1e 0%, #274249) fixed;
display: grid;
grid-template-columns: 20% auto 20%;
grid-template-rows: 25% auto 25%;
}
.score {
grid-column: 2 / 3;
grid-row: 1 / 2;
place-self: center;
color: gainsboro;
font-size: 30pt;
margin-bottom: 15px;
}
.scene {
display: flex;
justify-content: space-evenly;
grid-column: 2 / 3;
grid-row: 2 / 3;
}
.controls {
grid-column: 2 / 3;
grid-row: 3 / 4;
place-self: center;
}
#roll {
margin-top: 50px;
}
```