- A simple whack a mole game. Click the moles to get as many points as you can before the time runs out!
Demo: 👉 Click me
const holes = document.querySelectorAll('.hole');
const scoreBoard = document.querySelector('.score');
const moles = document.querySelectorAll('.mole');
let lastHole;
let timeUp = false;
let score = 0;
function randomTime(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
function randomHole(holes) {
const idx = Math.floor(Math.random() * holes.length);
const hole = holes[idx];
if (hole === lastHole) {
console.log('Ah nah thats the same one bud');
return randomHole(holes);
}
lastHole = hole;
return hole;
}
function peep() {
const time = randomTime(200, 1000);
const hole = randomHole(holes);
hole.classList.add('up');
setTimeout(() => {
hole.classList.remove('up');
if (!timeUp) peep();
}, time);
}
function startGame() {
scoreBoard.textContent = 0;
timeUp = false;
score = 0;
peep();
setTimeout(() => timeUp = true, 10000)
}
function bonk(e) {
if(!e.isTrusted) return; // cheater!
score++;
this.parentNode.classList.remove('up');
scoreBoard.textContent = score;
}
moles.forEach(mole => mole.addEventListener('click', bonk));
In the example solution, if the user clicks fast enough on the same mole, he can score more than one point. To fix this, I add a variable to track if the user clicks on the same mole that he has already clicked on.
let lastMole;
function bonk(e) {
if (!e.isTrusted || lastMole === this) return;
score++;
lastMole = this;
this.parentNode.classList.remove('up');
scoreCount.textContent = score;
}
I also used the timer function from the previous day to show the user how much time it's left in the game.
function timer(seconds) {
clearInterval(countdown);
const now = Date.now();
const then = now + seconds * 1000;
displayTimeLeft(seconds);
countdown = setInterval(() => {
const secondsLeft = Math.round((then - Date.now()) / 1000);
if (secondsLeft < 0) {
clearInterval(countdown);
scoreBoard.classList.add('final');
return;
}
displayTimeLeft(secondsLeft);
}, 1000);
}
First, we should create a function for choosing a random hole from all the holes on the page. We can achieve this easily by using Math.random()
.
Also, we want the moles to come out from a different hole every time, so we create a variable lastHole
to check if the random hole is the same as the last one. If so, the computer will randomly pick another hole.
function randomHole(holes) {
const idx = Math.floor(Math.random() * holes.length);
const hole = holes[idx];
if (hole === lastHole) {
console.log('Ah nah thats the same one bud');
return randomHole(holes);
}
lastHole = hole;
return hole;
}
To make the game more interesting, besides popping up from different holes, every mole pops up for a different duration too.
After getting a random duration and a random hole, we add the up
class to hole
to make the moles pop up on the page and use a setTimeout
to make it hidden again after a period of time. Since we want the moles keep popping up unless the time runs out, we can call peep()
again inside the callback.
function randomTime(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
function peep() {
const time = randomTime(200, 1000);
const hole = randomHole(holes);
hole.classList.add('up');
setTimeout(() => {
hole.classList.remove('up');
if (!timeUp) peep();
}, time);
}
There are some people who use programs to create fake clicks to cheat in this kind of game. To prevent this, we can use event.isTrusted
to check if the user is cheating.
According to MDN, the isTrusted
property returns true
when the event was generated by a user action, and false
when the event was created or modified by a script.
function bonk(e) {
if(!e.isTrusted) return;
score++;
this.parentNode.classList.remove('up');
scoreBoard.textContent = score;
}