Skip to content

Commit 8ab4ee6

Browse files
Merge pull request #33 from SpaNb4/alesia/task-timer
Task timer and statistics
2 parents b4887b4 + 23f33f8 commit 8ab4ee6

13 files changed

+242
-9
lines changed

src/assets/scss/components/guess-a-number.scss src/assets/scss/components/guess_a_number.scss

+10-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
text-align: center;
4747
font-weight: bold;
4848
margin: auto;
49-
cursor: pointer;
49+
background-color: $color-mine-shaft;
50+
color: $color-pavlova;
5051

5152
&:hover {
5253
box-shadow: 0px 0px 5px $color-silver;
@@ -90,4 +91,12 @@
9091
box-shadow: 0px 0px 5px $color-silver;
9192
}
9293
}
94+
95+
#timer-guess-a-number {
96+
margin-left: 5px;
97+
}
98+
99+
#codeguess-a-number {
100+
margin-left: 10px;
101+
}
93102
}

src/assets/scss/components/room.scss

+11
Original file line numberDiff line numberDiff line change
@@ -1539,3 +1539,14 @@
15391539
}
15401540
}
15411541
}
1542+
1543+
#timer-snake {
1544+
@include resolution(big-mobile) {
1545+
font-size: 20px;
1546+
}
1547+
}
1548+
#timer-guess-a-number {
1549+
@include resolution(big-mobile) {
1550+
font-size: 20px;
1551+
}
1552+
}

src/assets/scss/main.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
@import './components//tetris';
1515
@import './components/hangman.scss';
1616
@import './components/snake.scss';
17-
@import './components/guess-a-number.scss';
17+
@import './components/guess_a_number.scss';
1818
@import './components/gem-puzzle.scss';
1919
@import './components/auth.scss';
2020

src/js/components/guess-a-number.ts src/js/components/guessanumber.ts

+37-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
import { getRandomInt, playAudio } from './utils.js';
22
import winSound from '../../assets/audio/guessanumber_win.mp3';
33
import uncorrectSound from '../../assets/audio/guessanumber_wrong_answer.mp3';
4+
import { GameTimer } from './timer.js';
5+
import { createTimerView } from './timer_view.js';
6+
import { getRoomState } from './room_state.js';
7+
import { definitionCodeWord } from './game-over.js';
48

59
let randomNumber: number = 0;
610
let numberOfGuesses: number = 0;
11+
const gameName = 'guess-a-number';
712
const userGuess: any = 'userGuess';
813
const statusArea: string = 'statusArea';
914
const historyList: string = 'historyList';
1015
const maxValue: number = 100;
16+
const maxGuesses: number = 9;
1117
const buttonArea: string = 'buttonArea';
18+
const codeWord: string = 'codeguess-a-number';
19+
const timerguessanumber: string = '#timer-guess-a-number';
20+
const stateTimer = new GameTimer(gameName, getRoomState());
21+
const secretWord = definitionCodeWord();
1222

1323
function writeMessage(elementId: any, message: string, appendMessage: any) {
1424
const elemToUpdate = document.getElementById(elementId);
@@ -19,11 +29,23 @@ function writeMessage(elementId: any, message: string, appendMessage: any) {
1929
}
2030
}
2131

32+
function setHiddenWordVisibility(visible) {
33+
document.getElementById(codeWord).innerHTML = (visible ? secretWord : '');
34+
}
35+
2236
function newGame() {
2337
randomNumber = getRandomInt(maxValue) + 1;
2438
numberOfGuesses = 0;
2539
writeMessage('historyList', '', '');
40+
const timerContainer = document.querySelector(timerguessanumber);
41+
timerContainer.innerHTML = '';
42+
createTimerView(timerContainer, stateTimer);
43+
stateTimer.gameOpened();
44+
document.getElementById(userGuess).removeAttribute('disabled');
45+
document.getElementById(buttonArea).removeAttribute('disabled');
2646
document.getElementById(userGuess).focus();
47+
const gameFinished = getRoomState().isGameFinished(gameName);
48+
setHiddenWordVisibility(gameFinished);
2749
}
2850

2951
function guessInRange(guess: number) {
@@ -40,12 +62,16 @@ function userGuessed() {
4062
} else {
4163
numberOfGuesses += 1;
4264

43-
if (Number(userGuessednumber) === randomNumber) {
65+
if (Number(userGuessednumber) === randomNumber && numberOfGuesses < maxGuesses) {
4466
// Got it
4567
writeMessage(statusArea, `<p style='color:rgb(245, 0, 6)'><span>You got me in</span> ${numberOfGuesses} <span>guesses</span>, <span>I was thinking</span> ${randomNumber}. <span>You won</span>!</p>`, '');
4668
const audioWin = new Audio(winSound);
4769
playAudio(audioWin);
48-
newGame();
70+
stateTimer.gameFinished();
71+
numberOfGuesses = 0;
72+
document.getElementById(userGuess).setAttribute('disabled', 'disabled');
73+
document.getElementById(buttonArea).setAttribute('disabled', 'disabled');
74+
setHiddenWordVisibility(true);
4975
} else if (Number(userGuessednumber) < randomNumber) {
5076
// User needs to guess higher
5177
writeMessage(statusArea, `<p><span>You need to guess higher than</span> ${userGuessednumber}, <span>try again</span>...</p>`, '');
@@ -59,8 +85,15 @@ function userGuessed() {
5985
const audioUncorrect = new Audio(uncorrectSound);
6086
playAudio(audioUncorrect);
6187
}
62-
}
6388

89+
if (numberOfGuesses >= maxGuesses) {
90+
writeMessage(statusArea, '<p><span>Game over! Try new game.</span><span>Please enter a number</span> 1-100 <span>and press the Guess button</span></p>', '');
91+
document.getElementById(historyList).innerHTML = '';
92+
randomNumber = getRandomInt(maxValue) + 1;
93+
numberOfGuesses = 0;
94+
writeMessage('historyList', '', '');
95+
}
96+
}
6497
document.getElementById(userGuess).value = '';
6598
document.getElementById(userGuess).focus();
6699
}
@@ -77,6 +110,7 @@ function resetGame() {
77110
document.getElementById(statusArea).innerHTML = '<p><span>Please enter a number</span> 1-100 <span>and press the Guess button</span>.</p>';
78111
document.getElementById(userGuess).value = '';
79112
document.getElementById(userGuess).focus();
113+
document.getElementById(userGuess).setAttribute('active', 'active');
80114
}
81115

82116
document.getElementById(buttonArea).addEventListener('click', () => {

src/js/components/header.js

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { state, volumeRange, keyboardSwitch } from './state';
22
import { gamearea } from './keyboard';
33
import locizify from 'locizify';
4+
import { getRoomState } from './room_state';
45

56
const restartButton = document.querySelector('#menu-restart-button');
67
const loginButton = document.querySelector('#menu-login-button');
@@ -49,6 +50,7 @@ function onLogoutClick() {
4950
loginButton.classList.remove(HIDE);
5051
registerButton.classList.remove(HIDE);
5152
localStorage.removeItem(USER);
53+
getRoomState().setUser(null);
5254
}
5355
});
5456
}
@@ -84,6 +86,7 @@ loginForm.addEventListener('submit', function (e) {
8486
saveButton.classList.remove(HIDE);
8587

8688
localStorage.setItem(USER, res.success);
89+
getRoomState().setUser(this.login_email.value);
8790
}
8891
});
8992
});
@@ -196,3 +199,12 @@ function headerInit() {
196199
}
197200

198201
document.addEventListener('DOMContentLoaded', headerInit);
202+
203+
function loadRoomState() {
204+
const currentUser = localStorage.getItem(USER);
205+
if (currentUser !== null) {
206+
getRoomState().setUser(currentUser);
207+
}
208+
}
209+
210+
loadRoomState();

src/js/components/room.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { state } from './state';
22
import { memoryGame } from './memory';
33
import { simonGame } from './simon';
4-
import { guessAnumberGame } from './guess-a-number.ts';
4+
import { guessAnumberGame } from './guessanumber.ts';
55
import { gameTicTacToe, closeGameTicTacToe } from './tic-tac-toe';
66
import { startTetris, KeyDown, stopTetris } from './tetris';
77
import { snakeGame } from './snake';

src/js/components/room_state.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function buildStorageKey(email) {
2+
return `states_${email}`;
3+
}
4+
5+
function storeStates(email, states) {
6+
localStorage.setItem(buildStorageKey(email), JSON.stringify(states));
7+
}
8+
9+
function loadStates(email) {
10+
const states = JSON.parse(localStorage.getItem(
11+
buildStorageKey(email),
12+
));
13+
if (states === null) {
14+
return {};
15+
}
16+
return states;
17+
}
18+
class RoomState {
19+
constructor() {
20+
this.states = {};
21+
this.userEmail = null;
22+
}
23+
24+
setUser(email) {
25+
this.states = {};
26+
this.userEmail = email;
27+
if (this.userEmail !== null) {
28+
this.states = loadStates(this.userEmail);
29+
}
30+
}
31+
32+
saveTime(gameName, time) {
33+
this.states[gameName] = time;
34+
if (this.userEmail !== null) {
35+
storeStates(this.userEmail, this.states);
36+
}
37+
}
38+
39+
getAllGames() {
40+
return { ...this.states };
41+
}
42+
43+
isGameFinished(gameName) {
44+
return (gameName in this.states);
45+
}
46+
}
47+
48+
const roomState = new RoomState();
49+
50+
function getRoomState() {
51+
return roomState;
52+
}
53+
54+
export { getRoomState };

src/js/components/snake.js

+22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { getRandomIntInclusive, playAudio } from './utils';
2+
import { createTimerView } from './timer_view';
3+
import { GameTimer } from './timer';
4+
import { getRoomState } from './room_state';
25
import winSound from '../../assets/audio/snake-game-win.mp3';
36
import overSound from '../../assets/audio/snake-game-over.mp3';
7+
import { definitionCodeWord } from './game-over';
8+
49

510
const display = [];
611
let viewPort;
@@ -23,6 +28,11 @@ const white = 'rgb(250, 250, 250)';
2328
const codGray = 'rgb(25, 25, 25)';
2429
const snakeLength = 'snakeLength';
2530
const displayId = 'display';
31+
const gameName = 'snake';
32+
const codeWord = 'codesnake';
33+
const timerSnake = '#timer-snake';
34+
const stateTimer = new GameTimer(gameName, getRoomState());
35+
const secretWord = definitionCodeWord();
2636

2737
const rectStyles = [
2838
{
@@ -193,6 +203,10 @@ function setTouchEvents(container) {
193203
});
194204
}
195205

206+
function setHiddenWordVisibility(visible) {
207+
document.getElementById(codeWord).innerHTML = (visible ? secretWord : '');
208+
}
209+
196210
function gameOver(endType) {
197211
clearInterval(gameTimer);
198212
gameStatus = 'stopped';
@@ -204,8 +218,10 @@ function gameOver(endType) {
204218
function gameWin(endType) {
205219
gameStatus = 'win';
206220
document.querySelector(message).textContent = 'You won!';
221+
stateTimer.gameFinished();
207222
const audioWin = new Audio(winSound);
208223
playAudio(audioWin);
224+
setHiddenWordVisibility(true);
209225
}
210226

211227
function setSnakeOnDisplay() {
@@ -323,6 +339,12 @@ function openSnakeGame() {
323339
viewPort = document.getElementById(displayId);
324340
initGame(viewPort);
325341
startGame(statRender);
342+
const timerContainer = document.querySelector(timerSnake);
343+
timerContainer.innerHTML = '';
344+
createTimerView(timerContainer, stateTimer);
345+
stateTimer.gameOpened();
346+
const gameFinished = getRoomState().isGameFinished(gameName);
347+
setHiddenWordVisibility(gameFinished);
326348
}
327349

328350
function closeSnakeGame() {

src/js/components/statistics_view.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { getRoomState } from './room_state';
2+
3+
function createStatistics() {
4+
const roomState = getRoomState();
5+
const statistics = document.querySelector('#game-statistics');
6+
document.querySelector('#menu-stats-button').addEventListener('click', () => {
7+
const result = [];
8+
const allGames = roomState.getAllGames();
9+
for (const [name, time] of Object.entries(allGames)) {
10+
result.push(`${name} - ${addZero(Math.floor(time/60))}:${addZero(time % 60)}</br>`);
11+
}
12+
statistics.innerHTML = result.join('');
13+
});
14+
}
15+
16+
function addZero(n) {
17+
return (parseInt(n, 10) < 10 ? '0' : '') + n;
18+
}
19+
20+
createStatistics();

src/js/components/timer.js

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const millisecondsCoefficient = 1000;
2+
3+
function nowMilliseconds() {
4+
const date = new Date();
5+
return date.getTime();
6+
}
7+
8+
class GameTimer {
9+
constructor(gameName, roomState) {
10+
this.roomState = roomState;
11+
this.gameName = gameName;
12+
this.finishTime = null;
13+
this.gameOpened();
14+
}
15+
16+
gameOpened() {
17+
this.startTime = nowMilliseconds();
18+
this.finishTime = null;
19+
}
20+
21+
gameFinished() {
22+
this.finishTime = this.getTimeDiffSeconds();
23+
this.roomState.saveTime(this.gameName, this.finishTime);
24+
}
25+
26+
getTimeSeconds() {
27+
if (this.finishTime === null) {
28+
return this.getTimeDiffSeconds();
29+
}
30+
return this.finishTime;
31+
}
32+
33+
getTimeDiffSeconds() {
34+
return Math.round((nowMilliseconds() - this.startTime) / millisecondsCoefficient);
35+
}
36+
}
37+
38+
export { GameTimer };

src/js/components/timer_view.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const updateInterval = 1000;
2+
3+
function createTimerView(container, timer) {
4+
const time = document.createElement('div');
5+
time.classList.add('time');
6+
time.innerHTML = `<i class="material-icons">alarm</i>
7+
<span class='minutes'>00</span>
8+
<span class='separator'>:</span>
9+
<span class='seconds'>00</span>`;
10+
container.append(time);
11+
const seconds = time.querySelector('.seconds');
12+
const minutes = time.querySelector('.minutes');
13+
14+
function pad(val) {
15+
return val > 9 ? val : `0${val}`;
16+
}
17+
setInterval(() => {
18+
let currentTime = timer.getTimeSeconds();
19+
seconds.innerHTML = pad(currentTime % 60);
20+
minutes.innerHTML = pad(parseInt(currentTime / 60, 10));
21+
}, updateInterval);
22+
}
23+
24+
export { createTimerView };

src/js/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import './components/tetris';
1919
import './components/snake';
2020
import './components/languages';
2121
import './components/game-over';
22-
import './components/guess-a-number';
22+
import './components/guessanumber';
2323
import './components/share';
2424
import './components/fakes';
25+
import './components/statistics_view';
26+
import './components/timer';
27+
import './components/timer_view';
28+
import './components/room_state';

0 commit comments

Comments
 (0)