1playingFieldSize = [10, 24]
2playingFieldScale = 30
3sidebarPadding = 32;
4speed = 200;
5
6
7// System Variables
8pieces = ["straight", "l1", "l2", "square", "s1", "s2", "t"]
9
10piecesDefinition = {
11 "straight": [
12 [
13 [-2, 0],
14 [-1, 0],
15 [0, 0],
16 [1, 0]
17 ],
18 [
19 [0, -2],
20 [0, -1],
21 [0, 0],
22 [0, 1]
23 ],
24 [
25 [-2, 1],
26 [-1, 1],
27 [0, 1],
28 [1, 1]
29 ],
30 [
31 [-1, -2],
32 [-1, -1],
33 [-1, 0],
34 [-1, 1]
35 ]
36 ],
37 "l1": [
38 [
39 [-1, -1],
40 [-1, 0],
41 [0, 0],
42 [1, 0]
43 ],
44 [
45 [1, -1],
46 [0, -1],
47 [0, 0],
48 [0, 1]
49 ],
50 [
51 [-1, 0],
52 [0, 0],
53 [1, 0],
54 [1, 1]
55 ],
56 [
57 [0, -1],
58 [0, 0],
59 [0, 1],
60 [-1, 1]
61 ]
62 ],
63 "l2": [
64 [
65 [1, -1],
66 [-1, 0],
67 [0, 0],
68 [1, 0]
69 ],
70 [
71 [1, 1],
72 [0, -1],
73 [0, 0],
74 [0, 1]
75 ],
76 [
77 [-1, 0],
78 [0, 0],
79 [1, 0],
80 [-1, 1]
81 ],
82 [
83 [0, -1],
84 [0, 0],
85 [0, 1],
86 [-1, -1]
87 ]
88 ],
89 "square": [
90 [
91 [-1, -1],
92 [-1, 0],
93 [0, 0],
94 [0, -1]
95 ],
96 [
97 [-1, -1],
98 [-1, 0],
99 [0, 0],
100 [0, -1]
101 ],
102 [
103 [-1, -1],
104 [-1, 0],
105 [0, 0],
106 [0, -1]
107 ],
108 [
109 [-1, -1],
110 [-1, 0],
111 [0, 0],
112 [0, -1]
113 ]
114 ],
115 "s1": [
116 [
117 [-1, 0],
118 [0, 0],
119 [0, -1],
120 [1, -1]
121 ],
122 [
123 [0, -1],
124 [0, 0],
125 [1, 0],
126 [1, 1]
127 ],
128 [
129 [-1, 1],
130 [0, 1],
131 [0, 0],
132 [1, 0]
133 ],
134 [
135 [-1, -1],
136 [-1, 0],
137 [0, 0],
138 [0, 1]
139 ]
140 ],
141 "s2": [
142 [
143 [-1, -1],
144 [0, -1],
145 [0, 0],
146 [1, 0]
147 ],
148 [
149 [1, -1],
150 [1, 0],
151 [0, 0],
152 [0, 1]
153 ],
154 [
155 [-1, 0],
156 [0, 0],
157 [0, 1],
158 [1, 1]
159 ],
160 [
161 [-1, 1],
162 [-1, 0],
163 [0, 0],
164 [0, -1]
165 ]
166 ],
167 "t": [
168 [
169 [-1, 0],
170 [0, 0],
171 [0, -1],
172 [1, 0]
173 ],
174 [
175 [0, -1],
176 [0, 0],
177 [1, 0],
178 [0, 1]
179 ],
180 [
181 [-1, 0],
182 [0, 0],
183 [0, 1],
184 [1, 0]
185 ],
186 [
187 [0, -1],
188 [0, 0],
189 [-1, 0],
190 [0, 1]
191 ]
192 ]
193}
194
195var playingField = [];
196var currentPiece;
197var nextPiece;
198var score = 0;
199var lines = 0;
200var gameOverState = false;
201var pausedState = false;
202
203// Function Definition
204function generatePlayingField() {
205 for (var i = 0; i < playingFieldSize[0]; i++) {
206 currentRowInPlayingFieldGeneration = []
207 for (var j = 0; j < playingFieldSize[1]; j++) {
208 currentRowInPlayingFieldGeneration.push(0)
209 }
210 playingField.push(currentRowInPlayingFieldGeneration)
211 }
212}
213
214function drawPixel(posX, posY) {
215 currentPixel = document.createElement("div");
216 currentPixel.className = "pixel"
217 currentPixel.style.left = playingFieldScale * posX + "px"
218 currentPixel.style.top = playingFieldScale * posY + "px"
219 $("#playing-field")[0].append(currentPixel)
220}
221
222function drawPixelInPreview(posX, posY, bounds) {
223 currentPixel = document.createElement("div");
224 currentPixel.className = "pixel"
225 currentPixel.style.left = playingFieldScale * posX + playingFieldScale * 1.5 + 32 + "px"
226 currentPixel.style.top = playingFieldScale * posY + playingFieldScale * 1.5 + 32 + "px"
227 $("#next-piece-preview")[0].append(currentPixel)
228}
229
230function drawPlayingField() {
231 // Clear playing field for render
232 $("#playing-field")[0].innerHTML = "";
233 // Render currently placed pieces
234 for (var i = 0; i < playingFieldSize[0]; i++) {
235 for (var j = 0; j < playingFieldSize[1]; j++) {
236 if (playingField[i][j] == 1) {
237 drawPixel(i, j)
238 }
239 }
240 }
241 // Render current piece
242 for (pixels in piecesDefinition[currentPiece.type][currentPiece.rotation]) {
243 drawPixel(currentPiece.position[0] + piecesDefinition[currentPiece.type][currentPiece.rotation][pixels][0], currentPiece.position[1] + piecesDefinition[currentPiece.type][currentPiece.rotation][pixels][1])
244 }
245}
246
247function updateNextPiecePreview() {
248 $("#next-piece-preview")[0].innerHTML = "";
249 for (pixels in piecesDefinition[nextPiece.type][nextPiece.rotation]) {
250 drawPixelInPreview(piecesDefinition[nextPiece.type][nextPiece.rotation][pixels][0], piecesDefinition[nextPiece.type][nextPiece.rotation][pixels][1])
251 }
252}
253
254function generateCurrentPiece() {
255 var pieceType = pieces[Math.floor(Math.random() * pieces.length)];
256 var piecePosition = [Math.floor(playingFieldSize[0] / 2), 2]
257 var pieceRotation = 0;
258 if (nextPiece == undefined) {
259 currentPiece = {
260 "type": pieceType,
261 "position": piecePosition,
262 "rotation": pieceRotation
263 }
264 var pieceType = pieces[Math.floor(Math.random() * pieces.length)];
265 var piecePosition = [Math.floor(playingFieldSize[0] / 2), 2]
266 var pieceRotation = 0;
267 nextPiece = {
268 "type": pieceType,
269 "position": piecePosition,
270 "rotation": pieceRotation
271 }
272 } else {
273 currentPiece = nextPiece
274 nextPiece = {
275 "type": pieceType,
276 "position": piecePosition,
277 "rotation": pieceRotation
278 }
279 }
280 updateNextPiecePreview();
281}
282
283function checkCollisions(directionX, directionY) {
284 collisionState = false;
285 for (pixels in piecesDefinition[currentPiece.type][currentPiece.rotation]) {
286 calculatedX = currentPiece.position[0] + piecesDefinition[currentPiece.type][currentPiece.rotation][pixels][0] + directionX;
287 calculatedY = currentPiece.position[1] + piecesDefinition[currentPiece.type][currentPiece.rotation][pixels][1] + directionY;
288 // Max Border
289 if (calculatedX > playingFieldSize[0] - 1) {
290 collisionState = true
291 return collisionState;
292 }
293 if (calculatedY > playingFieldSize[1] - 1) {
294 collisionState = true
295 return collisionState;
296 }
297 // Min Border
298 if (calculatedX < 0) {
299 collisionState = true
300 return collisionState;
301 }
302 if (calculatedY < 0) {
303 collisionState = true
304 return collisionState;
305 }
306
307 if (playingField[calculatedX][calculatedY] == 1) {
308 collisionState = true;
309 return collisionState;
310 }
311 }
312 return collisionState;
313}
314
315function checkCollisionsRotation(rotationDirection) {
316 collisionState = false;
317 for (pixels in piecesDefinition[currentPiece.type][currentPiece.rotation]) {
318 calculatedRotation = currentPiece.rotation + rotationDirection;
319 if (calculatedRotation > 3) {
320 calculatedRotation = 0;
321 }
322 if (calculatedRotation < 0) {
323 calculatedRotation = 3;
324 }
325 calculatedX = currentPiece.position[0] + piecesDefinition[currentPiece.type][calculatedRotation][pixels][0];
326 calculatedY = currentPiece.position[1] + piecesDefinition[currentPiece.type][calculatedRotation][pixels][1];
327 // Max Border
328 if (calculatedX > playingFieldSize[0] - 1) {
329 collisionState = true
330 return collisionState;
331 }
332 if (calculatedY > playingFieldSize[1] - 1) {
333 collisionState = true
334 return collisionState;
335 }
336 // Min Border
337 if (calculatedX < 0) {
338 collisionState = true
339 return collisionState;
340 }
341 if (calculatedY < 0) {
342 collisionState = true
343 return collisionState;
344 }
345
346 if (playingField[calculatedX][calculatedY] == 1) {
347 collisionState = true;
348 return collisionState;
349 }
350 }
351 return collisionState;
352}
353
354function physicsTick() {
355 if (!checkCollisions(0, 1)) {
356 currentPiece.position[1]++;
357 } else {
358 for (pixels in piecesDefinition[currentPiece.type][currentPiece.rotation]) {
359 calculatedX = currentPiece.position[0] + piecesDefinition[currentPiece.type][currentPiece.rotation][pixels][0];
360 calculatedY = currentPiece.position[1] + piecesDefinition[currentPiece.type][currentPiece.rotation][pixels][1];
361 playingField[calculatedX][calculatedY] = 1;
362 }
363 if (!gameOverState) {
364 checkLines();
365 generateCurrentPiece();
366 }
367 if (checkCollisions(0, 0)) {
368 gameOver();
369 }
370 }
371
372}
373
374function checkLines() {
375 linesWhereBreaksAre = []
376 for (var j = 0; j < playingFieldSize[1]; j++) {
377 uninterrupted = true;
378 for (var i = 0; i < playingFieldSize[0]; i++) {
379 if (playingField[i][j] == 0) {
380 uninterrupted = false;
381 }
382 }
383 if (uninterrupted) {
384 linesWhereBreaksAre.push(j);
385 }
386 }
387 for (var k = 0; k < linesWhereBreaksAre.length; k++) {
388 lineBreak = linesWhereBreaksAre[k]
389 for (var j = 0; j < lineBreak; j++) {
390 for (var i = 0; i < playingFieldSize[0]; i++) {
391 playingField[i][lineBreak - j] = playingField[i][lineBreak - 1 - j]
392 }
393 }
394 }
395 lines += linesWhereBreaksAre.length;
396 switch (linesWhereBreaksAre.length) {
397 case 1:
398 score += 40
399 break;
400 case 2:
401 score += 100
402 break;
403 case 3:
404 score += 300
405 break;
406 case 4:
407 score += 1200
408 break;
409 }
410 updateScore()
411}
412
413function updateScore() {
414 $("#score")[0].innerHTML = "Score: " + score + "<br>" + "Lines: " + lines;
415}
416
417function restart() {
418 console.log("Restarting")
419 score = 0
420 lines = 0
421 playingField = [];
422 generatePlayingField();
423 generateCurrentPiece();
424 drawPlayingField();
425 updateScore()
426 $("#gameover-screen")[0].remove();
427 gameOverState = false;
428}
429
430function gameOver() {
431 if (!gameOverState) {
432 gameOverScreen = document.createElement("div");
433 gameOverScreen.id = "gameover-screen"
434 gameOverScreen.innerHTML = "Game Over !<br>"
435 gameOverScreen.innerHTML += "Score: " + score + "<br>"
436 gameOverScreen.innerHTML += "Lines: " + lines + "<br>"
437 restartButton = document.createElement("button");
438 restartButton.onclick = restart;
439 restartButton.id = "gameover-restart-button"
440 restartButton.innerHTML = "Restart"
441 gameOverScreen.append(restartButton)
442 $("body")[0].append(gameOverScreen)
443 gameOverState = true;
444 }
445}
446
447// Define Controls
448document.addEventListener('keydown', (event) => {
449 if (!pausedState) {
450 var name = event.key;
451 var code = event.code;
452 switch (String(code)) {
453 case "ArrowLeft":
454 if (!checkCollisions(-1, 0)) {
455 currentPiece.position[0]--;
456 drawPlayingField();
457 }
458 break;
459 case "ArrowRight":
460 if (!checkCollisions(1, 0)) {
461 currentPiece.position[0]++;
462 drawPlayingField();
463 }
464 break;
465 case "ArrowDown":
466 physicsTick();
467 drawPlayingField();
468 break;
469 case "KeyZ":
470 if (!checkCollisionsRotation(-1)) {
471 currentPiece.rotation--;
472 if (currentPiece.rotation < 0) {
473 currentPiece.rotation = 3;
474 }
475 drawPlayingField();
476 }
477 break;
478 case "KeyX":
479 if (!checkCollisionsRotation(1)) {
480 currentPiece.rotation++;
481 if (currentPiece.rotation > 3) {
482 currentPiece.rotation = 0;
483 }
484 drawPlayingField();
485 }
486 break;
487 }
488 }
489}, false);
490
491function gameLoop() {
492 physicsTick();
493 drawPlayingField();
494}
495
496function togglePause() {
497 if (pausedState) {
498 gameLoopInterval = setInterval(gameLoop, speed);
499 pausedState = false;
500 } else {
501 clearInterval(gameLoopInterval);
502 pausedState = true;
503 }
504}
505
506// Code Start
507$("#playing-field")[0].style.width = playingFieldScale * playingFieldSize[0] + "px";
508$("#playing-field")[0].style.height = playingFieldScale * playingFieldSize[1] + "px";
509$("#sidebar")[0].style.width = playingFieldScale * playingFieldSize[0] - sidebarPadding * 2 + "px";
510$("#sidebar")[0].style.padding = sidebarPadding + "px";
511$("#sidebar")[0].style.left = "calc(50% + " + $("#playing-field")[0].style.width + ")";
512$("#sidebar")[0].style.height = playingFieldScale * playingFieldSize[1] - sidebarPadding * 2 + "px";
513
514$("#next-piece-preview")[0].style.width = playingFieldScale * 4 + 32 + "px";
515$("#next-piece-preview")[0].style.height = playingFieldScale * 4 + 32 + "px";
516
517generatePlayingField();
518generateCurrentPiece();
519drawPlayingField();
520updateScore()
521
522
523
524
525var gameLoopInterval = setInterval(gameLoop, speed);