Skip to content

Commit 15ea784

Browse files
author
tomgillard
committed
Completing javascript30 day 30 in own index and scripts files. Expanded initial game to include countdown, hiscore getting and setting as well as difficulty modes and game over popup
1 parent 21833be commit 15ea784

File tree

3 files changed

+331
-2
lines changed

3 files changed

+331
-2
lines changed

30 - Whack A Mole/index.html

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Whack A Mole!</title>
6+
<link href="https://fonts.googleapis.com/css?family=Share+Tech+Mono" rel="stylesheet">
7+
<link href='https://fonts.googleapis.com/css?family=Amatic+SC:400,700' rel='stylesheet'>
8+
<link rel="stylesheet" href="style.css">
9+
</head>
10+
<body>
11+
12+
<header>
13+
<div class="controls">
14+
<div class="ctrl">
15+
<button class="btn btn-start">New Game</button>
16+
<span class="timer">00:00</span>
17+
</div>
18+
<div class="ctrl">
19+
Difficulty:
20+
<select class="game-mode">
21+
<option value="easy">EASY</option>
22+
<option value="hard">HARD</option>
23+
<option value="expert">EXPERT</option>
24+
</select>
25+
</div>
26+
<div class="ctrl">
27+
Hi-score: <span class="hi-score"></span>
28+
<span class="reset">Reset</span>
29+
</div>
30+
</div>
31+
</header>
32+
33+
<main>
34+
<h1>Whack-a-mole! <span class="score">0</span></h1>
35+
36+
<div class="game">
37+
<div class="hole hole1">
38+
<div class="mole"></div>
39+
</div>
40+
<div class="hole hole2">
41+
<div class="mole"></div>
42+
</div>
43+
<div class="hole hole3">
44+
<div class="mole"></div>
45+
</div>
46+
<div class="hole hole4">
47+
<div class="mole"></div>
48+
</div>
49+
<div class="hole hole5">
50+
<div class="mole"></div>
51+
</div>
52+
<div class="hole hole6">
53+
<div class="mole"></div>
54+
</div>
55+
</div>
56+
57+
</main>
58+
59+
<section class="game-over-panel">
60+
<h1>GAME OVER!</h1>
61+
<p>We hope you had fun.</p>
62+
</section>
63+
64+
<script src="scripts.js"></script>
65+
</body>
66+
</html>

30 - Whack A Mole/scripts.js

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
const holes = document.querySelectorAll('.hole');
2+
const scoreBoard = document.querySelector('.score');
3+
const moles = document.querySelectorAll('.mole');
4+
const gameOverPanel = document.querySelector('.game-over-panel');
5+
6+
const button = document.querySelector('.btn-start');
7+
const timerDisplay = document.querySelector('.timer');
8+
const hiScoreDisplay = document.querySelector('.hi-score');
9+
const scoreReset = document.querySelector('.reset');
10+
11+
const gameMode = document.querySelector('.game-mode');
12+
13+
// get & set the hiscore value in the UI
14+
let currentHiScore = localStorage.getItem('hiScore') || 0;
15+
hiScoreDisplay.textContent = currentHiScore;
16+
17+
// set the difficulty of the game
18+
let difficulty = { min: 500, max: 1000 };
19+
20+
// setup the rest of the game
21+
const gameDuration = 10000; // 10 seconds
22+
let countdown;
23+
let lastHole;
24+
let timeUp = false;
25+
let score = 0;
26+
27+
const randomTime = function randomTime(min, max) {
28+
return Math.round(Math.random() * (max - min) + min);
29+
};
30+
31+
const randomHole = function randomHole(holes) {
32+
const idx = Math.floor(Math.random() * holes.length);
33+
const hole = holes[idx];
34+
35+
if (hole === lastHole) {
36+
// same hole, run it again!
37+
return randomHole(holes);
38+
}
39+
40+
lastHole = hole;
41+
return hole;
42+
43+
};
44+
45+
// Show the moles!
46+
const peep = function peep() {
47+
const time = randomTime(difficulty.min, difficulty.max);
48+
const hole = randomHole(holes);
49+
50+
hole.classList.add('up');
51+
52+
setTimeout(() => {
53+
hole.classList.remove('up');
54+
if (!timeUp) peep();
55+
}, time);
56+
};
57+
58+
// Whack the moles!
59+
const bonk = function bonk(e) {
60+
if (!e.isTrusted) return; // stop cheating!
61+
this.parentNode.classList.remove('up');
62+
score++;
63+
scoreBoard.textContent = score;
64+
};
65+
66+
// start and show the remaining game duration
67+
const startCountdown = function startCountdown() {
68+
clearInterval(countdown); // clear exiting timers
69+
const now = Date.now();
70+
const then = now + gameDuration;
71+
72+
displayTimeLeft(gameDuration / 1000);
73+
74+
countdown = setInterval(() => {
75+
const secondsLeft = Math.round((then - Date.now()) / 1000);
76+
77+
if (secondsLeft < 0) {
78+
clearInterval(countdown);
79+
return;
80+
}
81+
82+
displayTimeLeft(secondsLeft);
83+
84+
}, 1000);
85+
};
86+
87+
const displayTimeLeft = function displayTimeLeft(seconds) {
88+
let secondsRemaining = seconds;
89+
90+
const minutes = Math.floor(secondsRemaining / 60);
91+
secondsRemaining = secondsRemaining % 60;
92+
const displayMins = minutes < 10 ? `0${minutes}` : minutes;
93+
const displaySeconds = secondsRemaining < 10 ? `0${secondsRemaining}` : secondsRemaining;
94+
const display = `${displayMins}:${displaySeconds}`;
95+
96+
timerDisplay.textContent = display;
97+
};
98+
99+
const getHiScore = function getHiScore() {
100+
return localStorage.getItem('hiScore');
101+
};
102+
103+
// Save hi-score to localStorage
104+
const saveHiScore = function saveHiScore() {
105+
if (score > parseInt(currentHiScore)) {
106+
localStorage.setItem('hiScore', JSON.stringify(score));
107+
hiScoreDisplay.textContent = score;
108+
hiScoreDisplay.classList.add('new-score');
109+
}
110+
};
111+
112+
const resetHiScore = function resetHiScore(){
113+
localStorage.setItem('hiScore', JSON.stringify(0));
114+
hiScoreDisplay.textContent = 0;
115+
currentHiScore = 0;
116+
};
117+
118+
// Set the game difficulty (Easy, Hard, Expert)
119+
const toggleDifficulty = function toggleDifficulty() {
120+
// easy mode range is min: 300, max: 1000
121+
// hard mode range is min: 150, max: 800
122+
// expert mode range is min: 100, max: 500
123+
switch (this.value) {
124+
case 'easy':
125+
difficulty.min = 500;
126+
difficulty.max = 1000;
127+
break;
128+
case 'hard':
129+
difficulty.min = 200;
130+
difficulty.max = 800;
131+
break;
132+
case 'expert':
133+
difficulty.min = 100;
134+
difficulty.max = 500;
135+
break;
136+
default:
137+
difficulty.min = 500;
138+
difficulty.max = 1000;
139+
}
140+
};
141+
142+
// Start a new game
143+
const startGame = function startGame() {
144+
gameOverPanel.classList.remove('active');
145+
hiScoreDisplay.classList.remove('new-score');
146+
button.setAttribute('disabled', 'disabled');
147+
scoreBoard.textContent = 0;
148+
timeUp = false;
149+
score = 0;
150+
currentHiScore = getHiScore();
151+
peep();
152+
startCountdown();
153+
setTimeout(() => {
154+
endGame();
155+
}, gameDuration);
156+
};
157+
158+
// Update somethings when game ends after set duration
159+
const endGame = function endGame() {
160+
// TIME'S UP!!!
161+
timeUp = true;
162+
// save the score
163+
saveHiScore();
164+
// re-enable the New Game button
165+
button.removeAttribute('disabled');
166+
// show "Game over" message;
167+
gameOverPanel.classList.add('active');
168+
};
169+
170+
button.addEventListener('click', startGame);
171+
gameMode.addEventListener('change', toggleDifficulty);
172+
scoreReset.addEventListener('click', resetHiScore);
173+
174+
moles.forEach(mole => mole.addEventListener('click', bonk));

30 - Whack A Mole/style.css

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
html {
22
box-sizing: border-box;
3-
font-size: 10px;
3+
font-size: 16px;
44
background: #ffc600;
55
}
66

@@ -11,16 +11,105 @@ html {
1111
body {
1212
padding: 0;
1313
margin:0;
14+
font-family: sans-serif;
15+
}
16+
17+
main {
18+
position: relative;
1419
font-family: 'Amatic SC', cursive;
1520
}
1621

1722
h1 {
1823
text-align: center;
19-
font-size: 10rem;
24+
font-size: 6rem;
2025
line-height:1;
2126
margin-bottom: 0;
2227
}
2328

29+
.controls,
30+
.modes {
31+
display: flex;
32+
justify-content: center;
33+
align-content: space-between;
34+
align-items: center;
35+
}
36+
37+
.controls > div,
38+
.modes > div {
39+
flex: 1 0 auto;
40+
text-align: center;
41+
}
42+
43+
.controls {
44+
padding: 10px 0;
45+
font-family: 'Share Tech Mono', monospace;
46+
background-color: #222;
47+
color: #fff;
48+
}
49+
50+
.hi-score.new-score {
51+
-webkit-animation: 1s linear 0s pulse;
52+
animation: 1s linear 0s pulse;
53+
}
54+
55+
@-webkit-keyframes pulse { 0% { transition: scale(0.75); } 50% { transition: scale(1.2); } 100% { transition: scale(1); } }
56+
@keyframes pulse { 0% { transition: scale(0.75); } 50% { transition: scale(1.2); } 100% { transition: scale(1); } }
57+
58+
.reset {
59+
display: inline-block;
60+
margin-left: 5px;
61+
font-family: Arial, sans-serif;
62+
font-size: 0.65rem;
63+
cursor: pointer;
64+
}
65+
66+
.btn {
67+
padding: 5px 10px;
68+
border: 0;
69+
background-color: #fff;
70+
}
71+
72+
.btn[disabled="disabled"] {
73+
background-color: rgba(255, 255, 255, 0.8);
74+
}
75+
76+
.timer {
77+
display: inline-block;
78+
margin-left: 10px;
79+
}
80+
81+
.game-over-panel {
82+
position: absolute;
83+
top: 50%;
84+
left: 50%;
85+
padding: 3rem;
86+
border-radius:1rem;
87+
text-transform: uppercase;
88+
text-align: center;
89+
font-weight: bold;
90+
background-color: rgba(255, 255, 255, 0.85);
91+
opacity: 0;
92+
visibility: hidden;
93+
transform: translate(-50%, -50%);
94+
transition: opacity 200ms ease;
95+
will-change: opacity, visbility;
96+
z-index: 2;
97+
}
98+
99+
.game-over-panel.active {
100+
opacity: 1;
101+
visibility: visible;
102+
}
103+
104+
.game-over-panel h1 {
105+
color: red;
106+
margin: 0;
107+
}
108+
109+
.game-over-panel p {
110+
font-size: 2rem;
111+
}
112+
24113
.score {
25114
background:rgba(255,255,255,0.2);
26115
padding:0 3rem;

0 commit comments

Comments
 (0)