├── images
├── Tetris Game.gif
├── blue_block.png
├── green_block.png
├── navy_block.png
├── peach_block.png
├── pink_block.png
├── purple_block.png
└── yellow_block.png
├── .github
└── FUNDING.yml
├── index.html
├── README.md
├── css
└── style.css
└── js
└── app.js
/images/Tetris Game.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/Tetris Game.gif
--------------------------------------------------------------------------------
/images/blue_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/blue_block.png
--------------------------------------------------------------------------------
/images/green_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/green_block.png
--------------------------------------------------------------------------------
/images/navy_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/navy_block.png
--------------------------------------------------------------------------------
/images/peach_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/peach_block.png
--------------------------------------------------------------------------------
/images/pink_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/pink_block.png
--------------------------------------------------------------------------------
/images/purple_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/purple_block.png
--------------------------------------------------------------------------------
/images/yellow_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kubowania/Tetris/HEAD/images/yellow_block.png
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: kubowania
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Tetris!
7 |
8 |
9 |
10 |
11 |
12 |
13 |
28 |
29 | Welcome to tetris
30 |
31 |
32 |
35 |
36 |
37 |
Your Score 0
38 |
41 |
Lines:0
42 |
43 | Start / Pause
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tetris
2 | A vanilla javascript game
3 |
4 | Watch my tutorial [here](https://www.youtube.com/watch?v=GWPGz9hrVMk)
5 |
6 | I have kept the styling at a bare miniumum for you to go wild and make it your own. Please tag me as I would LOVE to see your game!!!
7 |
8 | Tetris is a tile-matching puzzle game from the 80’s. Try to get your personal high score by moving each of the 5 randomly selected Tetromino shapes sideways and/or rotating by quarter-turns, so that they form a solid horizontal line without gaps. When such a line is formed, it disappears and any blocks above it fall down to fill the space. For each line you will receive 10 points.
9 |
10 | This was my first project from General Assembly's Software Engineering Immersive Course. It was also my first time building a project with JavaScript.
11 |
12 | 
13 |
14 |
15 |
16 |
17 | Brief
18 |
19 | * The game should stop if a Tetrimino fills the highest row of the game board
20 | * The player should be able to rotate each Tetrimino about its own axis
21 | * If a line is completed it should be removed and the pieces above should take its place
22 | * Render a grid-based game in the browser
23 | * Include separate HTML / CSS / JavaScript files
24 | * Use Javascript for DOM manipulation
25 | * Deploy your game online, using Github Pages, where the rest of the world can access it
26 | * Use semantic markup for HTML and CSS (adhere to best practices)
27 |
28 | Technologies Used
29 |
30 | * HTML5
31 | * CSS3
32 | * JavaScript
33 | * Git
34 | * GitHub
35 | * Google Fonts
36 |
37 | Features piece of code no.1
38 |
39 |
40 | ```
41 | //freeze the shape
42 | function freeze() {
43 | // if block has settled
44 | if(current.some(index => squares[currentPosition + index + width].classList.contains('block3') || squares[currentPosition + index + width].classList.contains('block2'))) {
45 | // make it block2
46 | current.forEach(index => squares[index + currentPosition].classList.add('block2'))
47 | // start a new tetromino falling
48 | random = nextRandom
49 | nextRandom = Math.floor(Math.random() * theTetrominoes.length)
50 | current = theTetrominoes[random][currentRotation]
51 | currentPosition = 4
52 | draw()
53 | displayShape()
54 | addScore()
55 | gameOver()
56 | }
57 | }
58 | freeze()
59 |
60 | ```
61 |
62 | Features piece of code no.2
63 |
64 |
65 | ```
66 | //Add score
67 | function addScore() {
68 | for (currentIndex = 0; currentIndex < 199;currentIndex += width) {
69 | const row = [currentIndex,currentIndex+1,currentIndex+2,currentIndex+3,currentIndex+4,currentIndex+5,currentIndex+6,currentIndex+7,currentIndex+8,currentIndex+9]
70 | if(row.every(index => squares[index].classList.contains('block2'))) {
71 | score += 10
72 | lines +=1
73 | scoreDisplay.innerHTML = score
74 | linesDisplay.innerHTML = lines
75 | row.forEach(index => {
76 | squares[index].style.backgroundImage = 'none'
77 | squares[index].classList.remove('block2') || squares[index].classList.remove('block')
78 |
79 | })
80 |
81 | ```
82 |
83 | ### MIT Licence
84 |
85 | Copyright (c) 2020 Ania Kubow
86 |
87 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
88 |
89 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
90 |
91 | *Translation: Ofcourse you can use this for you project! Just make sure to say where you got this from :)
92 |
93 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
94 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* default font size in browsers is 16px = 1em, we make
3 | things easier for us and make 10px our base size.
4 | We have 10/16 = 0.625 = 1rem as it is set on root element.
5 | So 1rem is now 10px throughout our stylesheet.*/
6 | font-size: 0.625em;
7 | }
8 |
9 | * {
10 | box-sizing: border-box;
11 | }
12 |
13 | body {
14 | font-family: "Montserrat", sans-serif;
15 | font-size: 1.6rem;
16 | margin: auto;
17 | max-width: 60rem;
18 | color: #d8edea;
19 | background: radial-gradient(
20 | circle,
21 | rgba(175, 196, 174, 1) 0%,
22 | rgba(104, 204, 191, 1) 89%,
23 | rgba(94, 191, 178, 1) 100%
24 | );
25 | }
26 |
27 | header {
28 | text-align: center;
29 | margin-top: 3rem;
30 | }
31 |
32 | div {
33 | height: 2rem;
34 | width: 2rem;
35 | }
36 |
37 | /* some utility classes */
38 | .t-ucase {
39 | text-transform: uppercase;
40 | }
41 |
42 | .t-big {
43 | font-size: 1.5em;
44 | }
45 |
46 | .t-wide {
47 | letter-spacing: 1.5rem;
48 | }
49 |
50 | .t-close {
51 | letter-spacing: 1rem;
52 | }
53 |
54 | .fw-300 {
55 | font-weight: 300;
56 | }
57 |
58 | .fw-400 {
59 | font-weight: 400;
60 | }
61 |
62 | .score-display {
63 | font-size: 5rem;
64 | color: rgb(133, 121, 107, 0.5);
65 | }
66 |
67 | .game-area {
68 | display: flex;
69 | justify-content: center;
70 | }
71 |
72 | .game {
73 | height: 0;
74 | width: 300px;
75 | }
76 |
77 | .grid {
78 | display: flex;
79 | flex-wrap: wrap;
80 | align-items: center;
81 | width: 20rem;
82 | height: 40rem;
83 | }
84 |
85 | .previous-shape {
86 | width: 10rem;
87 | padding-left: 2rem;
88 | margin-top: -5rem;
89 | }
90 |
91 | .previous-grid {
92 | display: flex;
93 | flex-wrap: wrap;
94 | width: 8rem;
95 | height: 8rem;
96 | }
97 |
98 | .block {
99 | background-image: url(../images/blue_block.png);
100 | }
101 |
102 | .block2 {
103 | background-image: url(../images/purple_block.png);
104 | }
105 |
106 | .block3 {
107 | background-image: url(../images/green_block.png);
108 | }
109 |
110 | .block4 {
111 | background-image: url(../images/navy_block.png);
112 | }
113 |
114 | .block5 {
115 | background-image: url(../images/pink_block.png);
116 | }
117 |
118 | .end {
119 | background-color: #d8edea;
120 | }
121 |
122 | .button {
123 | position: relative;
124 | width: 22rem;
125 | height: 2.2rem;
126 | text-align: center;
127 | color: #fff;
128 | letter-spacing: 1px;
129 | text-decoration: none;
130 | line-height: 23px;
131 | font-size: 10px;
132 | display: block;
133 | margin: 30px;
134 | text-shadow: -1px -1px 0 #a84155;
135 | background: #d25068;
136 | border: 1px solid #d25068;
137 | width: 12rem;
138 | background-image: linear-gradient(to bottom, #f66c7b, #d25068);
139 | border-radius: 5px;
140 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset,
141 | 0 -1px 0 rgba(255, 255, 255, 0.1) inset, 0 4px 0 #ad4257,
142 | 0 4px 2px rgba(0, 0, 0, 0.5);
143 | }
144 |
145 | .button:before {
146 | background: #f0f0f0;
147 | background-image: linear-gradient(#d0d0d0, #f0f0f0);
148 | border-radius: 5px;
149 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5) inset, 0 1px 0 #fff;
150 | position: absolute;
151 | content: "";
152 | left: -6px;
153 | right: -6px;
154 | top: -6px;
155 | bottom: -10px;
156 | z-index: -1;
157 | }
158 |
159 | .button:active {
160 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset,
161 | 0 -1px 0 rgba(255, 255, 255, 0.1) inset;
162 | top: 5px;
163 | }
164 |
165 | .button:active:before {
166 | top: -11px;
167 | bottom: -5px;
168 | content: "";
169 | }
170 |
171 | .button:hover {
172 | background: #f66c7b;
173 | background-image: linear-gradient(top, #d25068, #f66c7b);
174 | }
175 |
176 | .end {
177 | background-image: url(/Users/limit/development/Tetris/images/blue_block.png);
178 | }
179 |
180 | .display {
181 | display: flex;
182 | flex-direction: column;
183 | justify-content: space-between;
184 | align-items: center;
185 | text-align: center;
186 | margin-top: 1rem;
187 | width: 17.5rem;
188 | height: 25rem;
189 | background: #f0f0f0;
190 | background-image: linear-gradient(#d0d0d0, #f0f0f0);
191 | border-radius: 5px;
192 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5) inset, 0 1px 0 #fff;
193 | color: #85796b;
194 | }
195 |
196 | .score,
197 | .lines-display {
198 | padding-top: 1rem;
199 | font-size: 1.2rem;
200 | }
201 |
202 | /*menu*/
203 | .container {
204 | max-width: 600px;
205 | padding: 0 3rem;
206 | margin: auto;
207 | overflow: hidden;
208 | }
209 |
210 | .btn:hover {
211 | opacity: 0.7;
212 | }
213 |
214 | /* START of MENU STYLING */
215 | .menu-wrap {
216 | position: fixed;
217 | top: 0;
218 | left: 0;
219 | z-index: 1;
220 | }
221 |
222 | .menu-wrap .toggler {
223 | position: absolute;
224 | top: 0;
225 | left: 0;
226 | z-index: 2;
227 | width: 50px;
228 | height: 50px;
229 | opacity: 0;
230 | cursor: pointer;
231 | }
232 |
233 | .menu-wrap .hamburger {
234 | position: absolute;
235 | top: 0;
236 | left: 0;
237 | z-index: 1;
238 | display: flex;
239 | width: 40px;
240 | height: 40px;
241 | padding: 1rem;
242 | background: rgba(13, 110, 139, 0.75);
243 | align-items: center;
244 | justify-content: center;
245 | }
246 |
247 | /* Hamburger Line */
248 | .menu-wrap .hamburger > div {
249 | position: relative;
250 | display: flex;
251 | width: 150%;
252 | height: 2px;
253 | background: #fff;
254 | flex: none;
255 | align-items: center;
256 | justify-content: center;
257 | transition: all 0.4s ease;
258 | }
259 |
260 | /* Hamburger Lines - Top & Bottom */
261 | .menu-wrap .hamburger > div:before,
262 | .menu-wrap .hamburger > div:after {
263 | position: absolute;
264 | top: -7px;
265 | z-index: 1;
266 | width: 100%;
267 | height: 2px;
268 | background: inherit;
269 | content: "";
270 | }
271 |
272 | /* Moves Line Down */
273 | .menu-wrap .hamburger > div:after {
274 | top: 7px;
275 | }
276 |
277 | /* Toggler Animation */
278 | .menu-wrap .toggler:checked + .hamburger > div {
279 | transform: rotate(135deg);
280 | }
281 |
282 | /* Turns Lines Into X */
283 | .menu-wrap .toggler:checked + .hamburger > div:before,
284 | .menu-wrap .toggler:checked + .hamburger > div:after {
285 | top: 0;
286 | transform: rotate(90deg);
287 | }
288 |
289 | /* Rotate On Hover When Checked */
290 | .menu-wrap .toggler:checked:hover + .hamburger > div {
291 | transform: rotate(225deg);
292 | }
293 |
294 | .menu {
295 | display: flex;
296 | justify-content: center;
297 | position: fixed;
298 | z-index: 1;
299 | left: 0;
300 | top: 0;
301 | width: 100%;
302 | height: 100%;
303 | overflow: auto;
304 | background-color: rgba(24, 39, 51, 0.85);
305 | }
306 |
307 | .menu-content {
308 | text-align: center;
309 | width: 600px;
310 | align-items: center;
311 | margin-top: 230px;
312 | justify-content: center;
313 | width: 200vw;
314 | height: 200vh;
315 | border-radius: 50%;
316 | transition: all 0.8s ease;
317 | }
318 |
319 | .rules {
320 | font-size: 12px;
321 | transition: color 0.4s ease;
322 | }
323 |
324 | .key {
325 | color: #f8de7e;
326 | }
327 |
328 | .close {
329 | border-radius: 5px;
330 | color: rgba(24, 39, 51, 0.85);
331 | }
332 |
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', () => {
2 | // TODO: we can also get the grid size from user
3 | const GRID_WIDTH = 10
4 | const GRID_HEIGHT = 20
5 | const GRID_SIZE = GRID_WIDTH * GRID_HEIGHT
6 |
7 | // no need to type 200 divs :)
8 | const grid = createGrid();
9 | let squares = Array.from(grid.querySelectorAll('div'))
10 | const startBtn = document.querySelector('.button')
11 | const hamburgerBtn = document.querySelector('.toggler')
12 | const menu = document.querySelector('.menu')
13 | const span = document.getElementsByClassName('close')[0]
14 | const scoreDisplay = document.querySelector('.score-display')
15 | const linesDisplay = document.querySelector('.lines-score')
16 | let currentIndex = 0
17 | let currentRotation = 0
18 | const width = 10
19 | let score = 0
20 | let lines = 0
21 | let timerId
22 | let nextRandom = 0
23 | const colors = [
24 | 'url(images/blue_block.png)',
25 | 'url(images/pink_block.png)',
26 | 'url(images/purple_block.png)',
27 | 'url(images/peach_block.png)',
28 | 'url(images/yellow_block.png)'
29 | ]
30 |
31 |
32 | function createGrid() {
33 | // the main grid
34 | let grid = document.querySelector(".grid")
35 | for (let i = 0; i < GRID_SIZE; i++) {
36 | let gridElement = document.createElement("div")
37 | grid.appendChild(gridElement)
38 | }
39 |
40 | // set base of grid
41 | for (let i = 0; i < GRID_WIDTH; i++) {
42 | let gridElement = document.createElement("div")
43 | gridElement.setAttribute("class", "block3")
44 | grid.appendChild(gridElement)
45 | }
46 |
47 | let previousGrid = document.querySelector(".previous-grid")
48 | // Since 16 is the max grid size in which all the Tetrominoes
49 | // can fit in we create one here
50 | for (let i = 0; i < 16; i++) {
51 | let gridElement = document.createElement("div")
52 | previousGrid.appendChild(gridElement);
53 | }
54 | return grid;
55 | }
56 |
57 |
58 | //assign functions to keycodes
59 | function control(e) {
60 | if (e.keyCode === 39)
61 | moveright()
62 | else if (e.keyCode === 38)
63 | rotate()
64 | else if (e.keyCode === 37)
65 | moveleft()
66 | else if (e.keyCode === 40)
67 | moveDown()
68 | }
69 |
70 | // the classical behavior is to speed up the block if down button is kept pressed so doing that
71 | document.addEventListener('keydown', control)
72 |
73 | //The Tetrominoes
74 | const lTetromino = [
75 | [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, 2],
76 | [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2 + 2],
77 | [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, GRID_WIDTH * 2],
78 | [GRID_WIDTH, GRID_WIDTH * 2, GRID_WIDTH * 2 + 1, GRID_WIDTH * 2 + 2]
79 | ]
80 |
81 | const zTetromino = [
82 | [0, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1],
83 | [GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2, GRID_WIDTH * 2 + 1],
84 | [0, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1],
85 | [GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2, GRID_WIDTH * 2 + 1]
86 | ]
87 |
88 | const tTetromino = [
89 | [1, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2],
90 | [1, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2 + 1],
91 | [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2 + 1],
92 | [1, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1]
93 | ]
94 |
95 | const oTetromino = [
96 | [0, 1, GRID_WIDTH, GRID_WIDTH + 1],
97 | [0, 1, GRID_WIDTH, GRID_WIDTH + 1],
98 | [0, 1, GRID_WIDTH, GRID_WIDTH + 1],
99 | [0, 1, GRID_WIDTH, GRID_WIDTH + 1]
100 | ]
101 |
102 | const iTetromino = [
103 | [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, GRID_WIDTH * 3 + 1],
104 | [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH + 3],
105 | [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, GRID_WIDTH * 3 + 1],
106 | [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH + 3]
107 | ]
108 |
109 | const theTetrominoes = [lTetromino, zTetromino, tTetromino, oTetromino, iTetromino]
110 |
111 | //Randomly Select Tetromino
112 | let random = Math.floor(Math.random() * theTetrominoes.length)
113 | let current = theTetrominoes[random][currentRotation]
114 |
115 |
116 | //move the Tetromino moveDown
117 | let currentPosition = 4
118 | //draw the shape
119 | function draw() {
120 | current.forEach(index => {
121 | squares[currentPosition + index].classList.add('block')
122 | squares[currentPosition + index].style.backgroundImage = colors[random]
123 | })
124 | }
125 |
126 | //undraw the shape
127 | function undraw() {
128 | current.forEach(index => {
129 | squares[currentPosition + index].classList.remove('block')
130 | squares[currentPosition + index].style.backgroundImage = 'none'
131 | })
132 | }
133 |
134 | //move down on loop
135 | function moveDown() {
136 | undraw()
137 | currentPosition = currentPosition += width
138 | draw()
139 | freeze()
140 | }
141 |
142 | startBtn.addEventListener('click', () => {
143 | if (timerId) {
144 | clearInterval(timerId)
145 | timerId = null
146 | } else {
147 | draw()
148 | timerId = setInterval(moveDown, 1000)
149 | nextRandom = Math.floor(Math.random() * theTetrominoes.length)
150 | displayShape()
151 | }
152 | })
153 |
154 | //move left and prevent collisions with shapes moving left
155 | function moveright() {
156 | undraw()
157 | const isAtRightEdge = current.some(index => (currentPosition + index) % width === width - 1)
158 | if (!isAtRightEdge) currentPosition += 1
159 | if (current.some(index => squares[currentPosition + index].classList.contains('block2'))) {
160 | currentPosition -= 1
161 | }
162 | draw()
163 | }
164 |
165 | //move right and prevent collisions with shapes moving right
166 | function moveleft() {
167 | undraw()
168 | const isAtLeftEdge = current.some(index => (currentPosition + index) % width === 0)
169 | if (!isAtLeftEdge) currentPosition -= 1
170 | if (current.some(index => squares[currentPosition + index].classList.contains('block2'))) {
171 | currentPosition += 1
172 | }
173 | draw()
174 | }
175 |
176 | //freeze the shape
177 | function freeze() {
178 | // if block has settled
179 | if (current.some(index => squares[currentPosition + index + width].classList.contains('block3') || squares[currentPosition + index + width].classList.contains('block2'))) {
180 | // make it block2
181 | current.forEach(index => squares[index + currentPosition].classList.add('block2'))
182 | // start a new tetromino falling
183 | random = nextRandom
184 | nextRandom = Math.floor(Math.random() * theTetrominoes.length)
185 | current = theTetrominoes[random][currentRotation]
186 | currentPosition = 4
187 | draw()
188 | displayShape()
189 | addScore()
190 | gameOver()
191 | }
192 | }
193 | freeze()
194 |
195 | //Rotate the Tetromino
196 | function rotate() {
197 | undraw()
198 | currentRotation++
199 | if (currentRotation === current.length) {
200 | currentRotation = 0
201 | }
202 | current = theTetrominoes[random][currentRotation]
203 | draw()
204 | }
205 |
206 | //Game Over
207 | function gameOver() {
208 | if (current.some(index => squares[currentPosition + index].classList.contains('block2'))) {
209 | scoreDisplay.innerHTML = 'end'
210 | clearInterval(timerId)
211 | }
212 | }
213 |
214 | //show previous tetromino in scoreDisplay
215 | const displayWidth = 4
216 | const displaySquares = document.querySelectorAll('.previous-grid div')
217 | let displayIndex = 0
218 |
219 | const smallTetrominoes = [
220 | [1, displayWidth + 1, displayWidth * 2 + 1, 2], /* lTetromino */
221 | [0, displayWidth, displayWidth + 1, displayWidth * 2 + 1], /* zTetromino */
222 | [1, displayWidth, displayWidth + 1, displayWidth + 2], /* tTetromino */
223 | [0, 1, displayWidth, displayWidth + 1], /* oTetromino */
224 | [1, displayWidth + 1, displayWidth * 2 + 1, displayWidth * 3 + 1] /* iTetromino */
225 | ]
226 |
227 | function displayShape() {
228 | displaySquares.forEach(square => {
229 | square.classList.remove('block')
230 | square.style.backgroundImage = 'none'
231 | })
232 | smallTetrominoes[nextRandom].forEach(index => {
233 | displaySquares[displayIndex + index].classList.add('block')
234 | displaySquares[displayIndex + index].style.backgroundImage = colors[nextRandom]
235 | })
236 | }
237 |
238 | //Add score
239 | function addScore() {
240 | for (currentIndex = 0; currentIndex < GRID_SIZE; currentIndex += GRID_WIDTH) {
241 | const row = [currentIndex, currentIndex + 1, currentIndex + 2, currentIndex + 3, currentIndex + 4, currentIndex + 5, currentIndex + 6, currentIndex + 7, currentIndex + 8, currentIndex + 9]
242 | if (row.every(index => squares[index].classList.contains('block2'))) {
243 | score += 10
244 | lines += 1
245 | scoreDisplay.innerHTML = score
246 | linesDisplay.innerHTML = lines
247 | row.forEach(index => {
248 | squares[index].style.backgroundImage = 'none'
249 | squares[index].classList.remove('block2') || squares[index].classList.remove('block')
250 |
251 | })
252 | //splice array
253 | const squaresRemoved = squares.splice(currentIndex, width)
254 | squares = squaresRemoved.concat(squares)
255 | squares.forEach(cell => grid.appendChild(cell))
256 | }
257 | }
258 | }
259 |
260 | //Styling eventListeners
261 | hamburgerBtn.addEventListener('click', () => {
262 | menu.style.display = 'flex'
263 | })
264 | span.addEventListener('click', () => {
265 | menu.style.display = 'none'
266 | })
267 |
268 | })
--------------------------------------------------------------------------------