Skip to content

Commit 413fb78

Browse files
committed
Game stats
1 parent 5c9d8ac commit 413fb78

13 files changed

Lines changed: 122 additions & 31 deletions

File tree

src/app.scss

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,4 @@
55
display: grid;
66
place-items: center;
77
background-color: #282c34;
8-
9-
p {
10-
color: white;
11-
text-align: center;
12-
font-size: calc(18px + 2vmin);
13-
}
148
}

src/components/MatrixCanvas.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import React, { FC, memo, MutableRefObject, ReactElement, useEffect, useRef } fr
33
import { TetrisMatrix } from '@types';
44
import { clearCanvas, drawToCanvas } from '@utils/canvas';
55

6-
import './matrixCanvas.scss';
7-
86
interface MatrixCanvasProps {
97
id: string;
108
ctxForwardRef?: MutableRefObject<CanvasRenderingContext2D | null>;

src/components/Queue.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import MatrixCanvas from '@components/MatrixCanvas';
44
import { BLOCK_SIZE } from '@constants';
55
import { PlayerState } from '@types';
66

7-
import './queue.scss';
8-
97
interface QueueProps {
108
matrices: PlayerState[];
119
}

src/components/Stats.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React, { FC } from 'react';
2+
3+
import { GameStats } from '@types';
4+
5+
const Stats: FC<GameStats> = ({ level, lines, score }) => {
6+
return (
7+
<div id="tetris-game-stats">
8+
<div className="game-stat-section">
9+
<p className="game-stat-section-header">score</p>
10+
<p className="game-stat-section-value">{score}</p>
11+
</div>
12+
<div className="game-stat-section">
13+
<p className="game-stat-section-header">level</p>
14+
<p className="game-stat-section-value">{level}</p>
15+
</div>
16+
<div className="game-stat-section">
17+
<p className="game-stat-section-header">lines</p>
18+
<p className="game-stat-section-value">{lines}</p>
19+
</div>
20+
</div>
21+
);
22+
};
23+
24+
export default Stats;

src/components/Tetris.scss

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,43 @@
55
height: 100%;
66

77
&-container {
8+
display: grid;
9+
grid-template-columns: auto auto;
10+
grid-template-rows: auto auto auto;
11+
padding: 8px;
12+
border-radius: 8px;
13+
background-color: #42464e;
14+
}
15+
16+
&-queue {
817
display: flex;
18+
flex-direction: column;
19+
align-items: center;
20+
min-width: 175px;
921
}
22+
23+
&-game-stats {
24+
grid-column: 1 / 3;
25+
display: grid;
26+
grid-template-columns: 1fr 1fr 1fr;
27+
padding: 8px;
28+
text-align: center;
29+
color: #ffffffeb;
30+
31+
.game-stat-section {
32+
display: grid;
33+
34+
&-header {
35+
font-size: 24px;
36+
}
37+
38+
&-value {
39+
font-size: 32px;
40+
}
41+
}
42+
}
43+
}
44+
45+
.tetris-canvas {
46+
padding: 8px;
1047
}

src/components/Tetris.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { EventHandler, FC, KeyboardEvent, useCallback, useEffect, useRedu
22

33
import * as actionTypes from '@actions/actionTypes';
44
import MatrixCanvas from '@components/MatrixCanvas';
5+
import Stats from '@components/Stats';
56
import Queue from '@components/Queue';
67
import { BLOCK_SIZE, BOARD_HEIGHT, BOARD_WIDTH } from '@constants';
78
import { initialState, reducer } from '@reducers';
@@ -17,11 +18,11 @@ const Tetris: FC = () => {
1718
const requestRef = useRef(0);
1819
const lastTick = useRef(0);
1920
const [frameCount, setFrameCount] = useState(0);
20-
const [gameStart, setGameStart] = useState(false);
21+
const [gameStart, setGameStart] = useState(true);
2122
const [isPaused, setIsPaused] = useState(false);
2223
const [gameOver, setGameOver] = useState(false);
2324

24-
const { gameBoard, player, queue } = state;
25+
const { gameBoard, player, queue, stats } = state;
2526

2627
const _movePlayerDown = useCallback(() => {
2728
if (player.y === player.placeholder.y) {
@@ -47,8 +48,9 @@ const Tetris: FC = () => {
4748
if (gameStart && !isPaused && !gameOver) {
4849
requestRef.current = requestAnimationFrame(() => {
4950
const now = performance.now();
51+
const tickTime = 250 - 25 * stats.level;
5052

51-
if (now - 500 >= lastTick.current) {
53+
if (now - tickTime >= lastTick.current) {
5254
_movePlayerDown();
5355

5456
lastTick.current = now;
@@ -62,7 +64,7 @@ const Tetris: FC = () => {
6264
} else {
6365
return;
6466
}
65-
}, [_movePlayerDown, frameCount, gameOver, gameStart, isPaused]);
67+
}, [_movePlayerDown, frameCount, gameOver, gameStart, isPaused, stats.level]);
6668

6769
useEffect(() => {
6870
// Re-draw the game board and player position
@@ -72,7 +74,7 @@ const Tetris: FC = () => {
7274
drawInitialScreen(gameBoardCtxRef.current);
7375
} else if (gameOver) {
7476
console.log('game over');
75-
drawGameOverScreen(gameBoardCtxRef.current);
77+
drawGameOverScreen(gameBoardCtxRef.current, stats.score);
7678
} else if (isPaused) {
7779
console.log('paused');
7880
drawPauseScreen(gameBoardCtxRef.current);
@@ -194,6 +196,7 @@ const Tetris: FC = () => {
194196
width={BOARD_WIDTH * BLOCK_SIZE}
195197
/>
196198
<Queue matrices={queue} />
199+
<Stats {...stats} />
197200
</div>
198201
</div>
199202
);

src/components/matrixCanvas.scss

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/components/queue.scss

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/constants/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const QUEUE_LENGTH = 4;
77

88
export enum ColorCodes {
99
transparent = 0,
10-
white = 1,
10+
background = 1,
1111
lightBlue = 2,
1212
blue = 3,
1313
orange = 4,
@@ -19,7 +19,7 @@ export enum ColorCodes {
1919

2020
export const COLOR_MAP = {
2121
[ColorCodes.transparent]: '#ffffff00',
22-
[ColorCodes.white]: '#f0f0f0',
22+
[ColorCodes.background]: '#000000',
2323
[ColorCodes.lightBlue]: '#60d9f4',
2424
[ColorCodes.blue]: '#4051d3',
2525
[ColorCodes.orange]: '#e8b23f',

src/reducers/index.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
getNewPlayer,
1616
initializeBoard,
1717
initializeQueue,
18+
initializeStats,
1819
updateGameBoardWithPlayer,
1920
updatePlaceholder,
2021
} from '@utils';
@@ -36,11 +37,12 @@ export const initialState: TetrisState = {
3637
gameBoard: initializeBoard(),
3738
player: getNewPlayer(1),
3839
queue: initializeQueue(),
40+
stats: initializeStats(),
3941
tetrominoCount: QUEUE_LENGTH + 1,
4042
};
4143

4244
export const reducer = (state: TetrisState, action: TetrisActions): TetrisState => {
43-
const { gameBoard, player, queue, tetrominoCount } = state;
45+
const { gameBoard, player, queue, stats, tetrominoCount } = state;
4446

4547
switch (action.type) {
4648
case RESET_PLAYER: {
@@ -61,6 +63,10 @@ export const reducer = (state: TetrisState, action: TetrisActions): TetrisState
6163
...player,
6264
y: player.y + 1,
6365
},
66+
stats: {
67+
...stats,
68+
score: stats.score + 1,
69+
},
6470
};
6571
}
6672
case MOVE_PLAYER_LEFT: {
@@ -91,6 +97,7 @@ export const reducer = (state: TetrisState, action: TetrisActions): TetrisState
9197
const newGameBoard = updateGameBoardWithPlayer({ gameBoard, ...player });
9298

9399
return {
100+
...state,
94101
gameBoard: newGameBoard,
95102
player: {
96103
...queue[0],
@@ -102,14 +109,21 @@ export const reducer = (state: TetrisState, action: TetrisActions): TetrisState
102109
}
103110
case JUMP_TO_PLACEHOLDER: {
104111
const newGameBoard = updateGameBoardWithPlayer({ gameBoard, ...player, y: player.placeholder.y });
112+
const jumpDistance = player.placeholder.y - player.y;
113+
const pointsEarned = jumpDistance + jumpDistance * stats.level;
105114

106115
return {
116+
...state,
107117
gameBoard: newGameBoard,
108118
player: {
109119
...queue[0],
110120
placeholder: updatePlaceholder({ gameBoard: newGameBoard, ...queue[0] }),
111121
},
112122
queue: [...queue.slice(1), getNewPlayer(tetrominoCount + 1)],
123+
stats: {
124+
...stats,
125+
score: stats.score + pointsEarned,
126+
},
113127
tetrominoCount: tetrominoCount + 1,
114128
};
115129
}
@@ -126,6 +140,17 @@ export const reducer = (state: TetrisState, action: TetrisActions): TetrisState
126140
}
127141
case UPDATE_GAME_BOARD: {
128142
const newGameBoard = clearMatrixRows(gameBoard, action.payload);
143+
const numRowsCompleted = action.payload.length;
144+
const newLinesCount = stats.lines + numRowsCompleted;
145+
146+
const standardPoints = numRowsCompleted * 100;
147+
const levelBonus = stats.level * 500;
148+
149+
let pointsEarned = standardPoints + levelBonus;
150+
151+
if (numRowsCompleted === 4) {
152+
pointsEarned *= 2;
153+
}
129154

130155
return {
131156
...state,
@@ -134,6 +159,12 @@ export const reducer = (state: TetrisState, action: TetrisActions): TetrisState
134159
...player,
135160
placeholder: updatePlaceholder({ gameBoard: newGameBoard, ...player }),
136161
},
162+
stats: {
163+
...stats,
164+
level: Math.floor(newLinesCount / 10),
165+
lines: newLinesCount,
166+
score: stats.score + pointsEarned,
167+
},
137168
};
138169
}
139170
default: {

0 commit comments

Comments
 (0)