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);