keeperballon commited on
Commit
c5bccdd
·
verified ·
1 Parent(s): e95f001

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +794 -19
index.html CHANGED
@@ -1,19 +1,794 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Modern Tetris</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --primary-color: #6c5ce7;
11
+ --secondary-color: #a29bfe;
12
+ --accent-color: #fd79a8;
13
+ --dark-color: #2d3436;
14
+ --light-color: #f9f9f9;
15
+ --shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
16
+ --border-radius: 10px;
17
+ }
18
+
19
+ * {
20
+ margin: 0;
21
+ padding: 0;
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ body {
26
+ font-family: 'Poppins', sans-serif;
27
+ background: linear-gradient(135deg, #dfe6e9, #b2bec3);
28
+ min-height: 100vh;
29
+ display: flex;
30
+ justify-content: center;
31
+ align-items: center;
32
+ padding: 20px;
33
+ color: var(--dark-color);
34
+ }
35
+
36
+ .container {
37
+ display: flex;
38
+ flex-direction: column;
39
+ align-items: center;
40
+ max-width: 1000px;
41
+ width: 100%;
42
+ background-color: rgba(255, 255, 255, 0.85);
43
+ backdrop-filter: blur(10px);
44
+ border-radius: var(--border-radius);
45
+ box-shadow: var(--shadow);
46
+ padding: 20px;
47
+ }
48
+
49
+ @media (min-width: 768px) {
50
+ .container {
51
+ flex-direction: row;
52
+ justify-content: center;
53
+ gap: 40px;
54
+ padding: 40px;
55
+ }
56
+ }
57
+
58
+ h1 {
59
+ font-size: 2.5rem;
60
+ margin-bottom: 20px;
61
+ color: var(--primary-color);
62
+ text-align: center;
63
+ width: 100%;
64
+ }
65
+
66
+ .game-container {
67
+ position: relative;
68
+ }
69
+
70
+ .tetris-board {
71
+ border: 2px solid var(--primary-color);
72
+ border-radius: var(--border-radius);
73
+ background-color: #fff;
74
+ box-shadow: var(--shadow);
75
+ display: grid;
76
+ grid-template-rows: repeat(20, 1fr);
77
+ grid-template-columns: repeat(10, 1fr);
78
+ gap: 1px;
79
+ width: 300px;
80
+ height: 600px;
81
+ overflow: hidden;
82
+ }
83
+
84
+ .cell {
85
+ background-color: #f5f5f5;
86
+ border-radius: 2px;
87
+ }
88
+
89
+ .side-panel {
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 20px;
93
+ }
94
+
95
+ .next-piece-container, .score-container, .level-container, .controls-container {
96
+ background-color: white;
97
+ border-radius: var(--border-radius);
98
+ padding: 15px;
99
+ box-shadow: var(--shadow);
100
+ width: 200px;
101
+ }
102
+
103
+ .next-piece-container h2, .score-container h2, .level-container h2, .controls-container h2 {
104
+ color: var(--primary-color);
105
+ font-size: 1.2rem;
106
+ margin-bottom: 10px;
107
+ text-align: center;
108
+ }
109
+
110
+ .next-piece-preview {
111
+ height: 100px;
112
+ display: grid;
113
+ grid-template-rows: repeat(4, 1fr);
114
+ grid-template-columns: repeat(4, 1fr);
115
+ gap: 1px;
116
+ margin: 0 auto;
117
+ }
118
+
119
+ .control-btn {
120
+ background-color: var(--primary-color);
121
+ color: white;
122
+ border: none;
123
+ border-radius: var(--border-radius);
124
+ padding: 10px 15px;
125
+ font-family: 'Poppins', sans-serif;
126
+ font-weight: 600;
127
+ cursor: pointer;
128
+ transition: all 0.3s ease;
129
+ display: block;
130
+ width: 100%;
131
+ margin-bottom: 10px;
132
+ box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1);
133
+ }
134
+
135
+ .control-btn:hover {
136
+ background-color: var(--secondary-color);
137
+ transform: translateY(-2px);
138
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.15);
139
+ }
140
+
141
+ .control-btn:active {
142
+ transform: translateY(0);
143
+ box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1);
144
+ }
145
+
146
+ .score, .level {
147
+ font-size: 1.5rem;
148
+ font-weight: 600;
149
+ color: var(--accent-color);
150
+ text-align: center;
151
+ }
152
+
153
+ .game-over-modal {
154
+ position: absolute;
155
+ top: 0;
156
+ left: 0;
157
+ width: 100%;
158
+ height: 100%;
159
+ background-color: rgba(0, 0, 0, 0.85);
160
+ display: flex;
161
+ flex-direction: column;
162
+ justify-content: center;
163
+ align-items: center;
164
+ border-radius: var(--border-radius);
165
+ opacity: 0;
166
+ pointer-events: none;
167
+ transition: opacity 0.3s ease;
168
+ }
169
+
170
+ .game-over-modal.active {
171
+ opacity: 1;
172
+ pointer-events: all;
173
+ }
174
+
175
+ .game-over-modal h2 {
176
+ color: white;
177
+ font-size: 2rem;
178
+ margin-bottom: 20px;
179
+ }
180
+
181
+ .game-over-modal p {
182
+ color: white;
183
+ font-size: 1.2rem;
184
+ margin-bottom: 30px;
185
+ }
186
+
187
+ .keyboard-controls {
188
+ margin-top: 15px;
189
+ font-size: 0.9rem;
190
+ color: #666;
191
+ }
192
+
193
+ .keyboard-controls p {
194
+ margin: 5px 0;
195
+ }
196
+
197
+ .tetromino {
198
+ border: 1px solid rgba(0, 0, 0, 0.2);
199
+ border-radius: 2px;
200
+ }
201
+
202
+ .I {
203
+ background-color: #00cec9;
204
+ }
205
+
206
+ .J {
207
+ background-color: #0984e3;
208
+ }
209
+
210
+ .L {
211
+ background-color: #e17055;
212
+ }
213
+
214
+ .O {
215
+ background-color: #fdcb6e;
216
+ }
217
+
218
+ .S {
219
+ background-color: #00b894;
220
+ }
221
+
222
+ .T {
223
+ background-color: #6c5ce7;
224
+ }
225
+
226
+ .Z {
227
+ background-color: #d63031;
228
+ }
229
+
230
+ @keyframes flash {
231
+ 0%, 100% {
232
+ filter: brightness(1);
233
+ }
234
+ 50% {
235
+ filter: brightness(1.5);
236
+ }
237
+ }
238
+
239
+ .flash-row {
240
+ animation: flash 0.2s 3;
241
+ }
242
+
243
+ @media (max-width: 767px) {
244
+ .tetris-board {
245
+ width: 280px;
246
+ height: 560px;
247
+ }
248
+
249
+ .side-panel {
250
+ margin-top: 20px;
251
+ flex-direction: row;
252
+ flex-wrap: wrap;
253
+ justify-content: center;
254
+ gap: 10px;
255
+ }
256
+
257
+ .next-piece-container, .score-container, .level-container, .controls-container {
258
+ width: calc(50% - 10px);
259
+ min-width: 130px;
260
+ }
261
+ }
262
+ </style>
263
+ </head>
264
+ <body>
265
+ <div class="container">
266
+ <div class="game-container">
267
+ <h1>Modern Tetris</h1>
268
+ <div class="tetris-board" id="tetris-board"></div>
269
+ <div class="game-over-modal" id="game-over-modal">
270
+ <h2>Game Over</h2>
271
+ <p>Your score: <span id="final-score">0</span></p>
272
+ <button class="control-btn" id="restart-btn">Play Again</button>
273
+ </div>
274
+ </div>
275
+ <div class="side-panel">
276
+ <div class="next-piece-container">
277
+ <h2>Next Piece</h2>
278
+ <div class="next-piece-preview" id="next-piece-preview"></div>
279
+ </div>
280
+ <div class="score-container">
281
+ <h2>Score</h2>
282
+ <div class="score" id="score">0</div>
283
+ </div>
284
+ <div class="level-container">
285
+ <h2>Level</h2>
286
+ <div class="level" id="level">1</div>
287
+ </div>
288
+ <div class="controls-container">
289
+ <h2>Controls</h2>
290
+ <button class="control-btn" id="start-btn">Start / Pause</button>
291
+ <div class="keyboard-controls">
292
+ <p>← → : Move left/right</p>
293
+ <p>↓ : Soft drop</p>
294
+ <p>↑ : Rotate</p>
295
+ <p>Space : Hard drop</p>
296
+ </div>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <script>
302
+ document.addEventListener('DOMContentLoaded', () => {
303
+ // Game constants
304
+ const BOARD_WIDTH = 10;
305
+ const BOARD_HEIGHT = 20;
306
+ const PREVIEW_SIZE = 4;
307
+
308
+ // Game elements
309
+ const tetrisBoard = document.getElementById('tetris-board');
310
+ const nextPiecePreview = document.getElementById('next-piece-preview');
311
+ const scoreElement = document.getElementById('score');
312
+ const levelElement = document.getElementById('level');
313
+ const startBtn = document.getElementById('start-btn');
314
+ const gameOverModal = document.getElementById('game-over-modal');
315
+ const finalScoreElement = document.getElementById('final-score');
316
+ const restartBtn = document.getElementById('restart-btn');
317
+
318
+ // Game state
319
+ let gameBoard = Array(BOARD_HEIGHT).fill().map(() => Array(BOARD_WIDTH).fill(0));
320
+ let score = 0;
321
+ let level = 1;
322
+ let linesCleared = 0;
323
+ let currentPiece = null;
324
+ let nextPiece = null;
325
+ let gameInterval = null;
326
+ let isPaused = false;
327
+ let isGameOver = false;
328
+
329
+ // Tetrominoes
330
+ const tetrominoes = {
331
+ I: {
332
+ shape: [
333
+ [0, 0, 0, 0],
334
+ [1, 1, 1, 1],
335
+ [0, 0, 0, 0],
336
+ [0, 0, 0, 0]
337
+ ],
338
+ color: 'I'
339
+ },
340
+ J: {
341
+ shape: [
342
+ [1, 0, 0],
343
+ [1, 1, 1],
344
+ [0, 0, 0]
345
+ ],
346
+ color: 'J'
347
+ },
348
+ L: {
349
+ shape: [
350
+ [0, 0, 1],
351
+ [1, 1, 1],
352
+ [0, 0, 0]
353
+ ],
354
+ color: 'L'
355
+ },
356
+ O: {
357
+ shape: [
358
+ [1, 1],
359
+ [1, 1]
360
+ ],
361
+ color: 'O'
362
+ },
363
+ S: {
364
+ shape: [
365
+ [0, 1, 1],
366
+ [1, 1, 0],
367
+ [0, 0, 0]
368
+ ],
369
+ color: 'S'
370
+ },
371
+ T: {
372
+ shape: [
373
+ [0, 1, 0],
374
+ [1, 1, 1],
375
+ [0, 0, 0]
376
+ ],
377
+ color: 'T'
378
+ },
379
+ Z: {
380
+ shape: [
381
+ [1, 1, 0],
382
+ [0, 1, 1],
383
+ [0, 0, 0]
384
+ ],
385
+ color: 'Z'
386
+ }
387
+ };
388
+
389
+ // Initialize game board
390
+ function initBoard() {
391
+ tetrisBoard.innerHTML = '';
392
+ nextPiecePreview.innerHTML = '';
393
+
394
+ // Create game board cells
395
+ for (let row = 0; row < BOARD_HEIGHT; row++) {
396
+ for (let col = 0; col < BOARD_WIDTH; col++) {
397
+ const cell = document.createElement('div');
398
+ cell.classList.add('cell');
399
+ cell.dataset.row = row;
400
+ cell.dataset.col = col;
401
+ tetrisBoard.appendChild(cell);
402
+ }
403
+ }
404
+
405
+ // Create next piece preview cells
406
+ for (let row = 0; row < PREVIEW_SIZE; row++) {
407
+ for (let col = 0; col < PREVIEW_SIZE; col++) {
408
+ const cell = document.createElement('div');
409
+ cell.classList.add('cell');
410
+ cell.dataset.row = row;
411
+ cell.dataset.col = col;
412
+ nextPiecePreview.appendChild(cell);
413
+ }
414
+ }
415
+ }
416
+
417
+ // Generate random tetromino
418
+ function getRandomTetromino() {
419
+ const tetrominoKeys = Object.keys(tetrominoes);
420
+ const randomKey = tetrominoKeys[Math.floor(Math.random() * tetrominoKeys.length)];
421
+ const tetromino = { ...tetrominoes[randomKey] };
422
+ return {
423
+ ...tetromino,
424
+ row: 0,
425
+ col: Math.floor((BOARD_WIDTH - tetromino.shape[0].length) / 2)
426
+ };
427
+ }
428
+
429
+ // Draw tetromino on the board
430
+ function drawTetromino() {
431
+ clearBoard();
432
+
433
+ // Draw settled pieces
434
+ for (let row = 0; row < BOARD_HEIGHT; row++) {
435
+ for (let col = 0; col < BOARD_WIDTH; col++) {
436
+ if (gameBoard[row][col]) {
437
+ const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
438
+ if (cell) {
439
+ cell.classList.add('tetromino', gameBoard[row][col]);
440
+ }
441
+ }
442
+ }
443
+ }
444
+
445
+ // Draw current piece
446
+ if (currentPiece) {
447
+ for (let row = 0; row < currentPiece.shape.length; row++) {
448
+ for (let col = 0; col < currentPiece.shape[row].length; col++) {
449
+ if (currentPiece.shape[row][col]) {
450
+ const boardRow = currentPiece.row + row;
451
+ const boardCol = currentPiece.col + col;
452
+ if (boardRow >= 0 && boardRow < BOARD_HEIGHT && boardCol >= 0 && boardCol < BOARD_WIDTH) {
453
+ const cell = document.querySelector(`.cell[data-row="${boardRow}"][data-col="${boardCol}"]`);
454
+ if (cell) {
455
+ cell.classList.add('tetromino', currentPiece.color);
456
+ }
457
+ }
458
+ }
459
+ }
460
+ }
461
+ }
462
+ }
463
+
464
+ // Draw next piece in preview
465
+ function drawNextPiece() {
466
+ // Clear the preview
467
+ const previewCells = nextPiecePreview.querySelectorAll('.cell');
468
+ previewCells.forEach(cell => {
469
+ cell.className = 'cell';
470
+ });
471
+
472
+ if (!nextPiece) return;
473
+
474
+ // Center the piece in the preview
475
+ const offsetRow = Math.floor((PREVIEW_SIZE - nextPiece.shape.length) / 2);
476
+ const offsetCol = Math.floor((PREVIEW_SIZE - nextPiece.shape[0].length) / 2);
477
+
478
+ for (let row = 0; row < nextPiece.shape.length; row++) {
479
+ for (let col = 0; col < nextPiece.shape[row].length; col++) {
480
+ if (nextPiece.shape[row][col]) {
481
+ const previewRow = offsetRow + row;
482
+ const previewCol = offsetCol + col;
483
+ if (previewRow >= 0 && previewRow < PREVIEW_SIZE && previewCol >= 0 && previewCol < PREVIEW_SIZE) {
484
+ const cell = nextPiecePreview.querySelector(`.cell[data-row="${previewRow}"][data-col="${previewCol}"]`);
485
+ if (cell) {
486
+ cell.classList.add('tetromino', nextPiece.color);
487
+ }
488
+ }
489
+ }
490
+ }
491
+ }
492
+ }
493
+
494
+ // Clear the visual board (not the data)
495
+ function clearBoard() {
496
+ const cells = tetrisBoard.querySelectorAll('.cell');
497
+ cells.forEach(cell => {
498
+ cell.className = 'cell';
499
+ });
500
+ }
501
+
502
+ // Check if a move is valid
503
+ function isValidMove(piece, rowOffset = 0, colOffset = 0) {
504
+ for (let row = 0; row < piece.shape.length; row++) {
505
+ for (let col = 0; col < piece.shape[row].length; col++) {
506
+ if (piece.shape[row][col]) {
507
+ const newRow = piece.row + row + rowOffset;
508
+ const newCol = piece.col + col + colOffset;
509
+
510
+ // Check if out of bounds or colliding with settled pieces
511
+ if (
512
+ newRow < 0 ||
513
+ newRow >= BOARD_HEIGHT ||
514
+ newCol < 0 ||
515
+ newCol >= BOARD_WIDTH ||
516
+ (newRow >= 0 && gameBoard[newRow][newCol])
517
+ ) {
518
+ return false;
519
+ }
520
+ }
521
+ }
522
+ }
523
+ return true;
524
+ }
525
+
526
+ // Check and clear completed lines
527
+ function checkLines() {
528
+ let linesComplete = 0;
529
+ let flashRows = [];
530
+
531
+ for (let row = BOARD_HEIGHT - 1; row >= 0; row--) {
532
+ if (gameBoard[row].every(cell => cell !== 0)) {
533
+ linesComplete++;
534
+ flashRows.push(row);
535
+ }
536
+ }
537
+
538
+ if (linesComplete > 0) {
539
+ // Flash the lines that will be cleared
540
+ flashRows.forEach(row => {
541
+ for (let col = 0; col < BOARD_WIDTH; col++) {
542
+ const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
543
+ if (cell) {
544
+ cell.classList.add('flash-row');
545
+ }
546
+ }
547
+ });
548
+
549
+ // Wait for animation to complete then clear lines
550
+ setTimeout(() => {
551
+ for (let row of flashRows) {
552
+ // Remove the row
553
+ gameBoard.splice(row, 1);
554
+ // Add a new empty row at the top
555
+ gameBoard.unshift(Array(BOARD_WIDTH).fill(0));
556
+ }
557
+
558
+ // Update score and level
559
+ updateScore(linesComplete);
560
+ linesCleared += linesComplete;
561
+ if (linesCleared >= level * 10) {
562
+ level++;
563
+ levelElement.textContent = level;
564
+ updateGameSpeed();
565
+ }
566
+
567
+ drawTetromino();
568
+ }, 600);
569
+ }
570
+ }
571
+
572
+ // Calculate score based on lines cleared
573
+ function updateScore(lines) {
574
+ const linePoints = [0, 40, 100, 300, 1200]; // Points for 0, 1, 2, 3, 4 lines
575
+ const points = linePoints[lines] * level;
576
+ score += points;
577
+ scoreElement.textContent = score;
578
+ }
579
+
580
+ // Update game speed based on level
581
+ function updateGameSpeed() {
582
+ if (gameInterval) {
583
+ clearInterval(gameInterval);
584
+ }
585
+ const speed = Math.max(100, 1000 - (level - 1) * 100); // Decrease interval as level increases
586
+ gameInterval = setInterval(moveDown, speed);
587
+ }
588
+
589
+ // Move the current piece down
590
+ function moveDown() {
591
+ if (isPaused || isGameOver || !currentPiece) return;
592
+
593
+ if (isValidMove(currentPiece, 1, 0)) {
594
+ currentPiece.row++;
595
+ drawTetromino();
596
+ } else {
597
+ // Lock piece in place
598
+ lockPiece();
599
+ // Check for completed lines
600
+ checkLines();
601
+ // Generate new piece
602
+ spawnPiece();
603
+ }
604
+ }
605
+
606
+ // Move the current piece left
607
+ function moveLeft() {
608
+ if (isPaused || isGameOver || !currentPiece) return;
609
+
610
+ if (isValidMove(currentPiece, 0, -1)) {
611
+ currentPiece.col--;
612
+ drawTetromino();
613
+ }
614
+ }
615
+
616
+ // Move the current piece right
617
+ function moveRight() {
618
+ if (isPaused || isGameOver || !currentPiece) return;
619
+
620
+ if (isValidMove(currentPiece, 0, 1)) {
621
+ currentPiece.col++;
622
+ drawTetromino();
623
+ }
624
+ }
625
+
626
+ // Rotate the current piece
627
+ function rotatePiece() {
628
+ if (isPaused || isGameOver || !currentPiece) return;
629
+
630
+ // Create a copy of the current piece
631
+ const rotatedPiece = { ...currentPiece };
632
+
633
+ // Create a new rotated shape matrix
634
+ const N = rotatedPiece.shape.length;
635
+ const rotatedShape = Array(N).fill().map(() => Array(N).fill(0));
636
+
637
+ // Perform the rotation (90 degrees clockwise)
638
+ for (let row = 0; row < N; row++) {
639
+ for (let col = 0; col < N; col++) {
640
+ rotatedShape[col][N - 1 - row] = rotatedPiece.shape[row][col];
641
+ }
642
+ }
643
+
644
+ rotatedPiece.shape = rotatedShape;
645
+
646
+ // Check if the rotation is valid
647
+ if (isValidMove(rotatedPiece)) {
648
+ currentPiece.shape = rotatedShape;
649
+ drawTetromino();
650
+ }
651
+ }
652
+
653
+ // Hard drop the current piece
654
+ function hardDrop() {
655
+ if (isPaused || isGameOver || !currentPiece) return;
656
+
657
+ while (isValidMove(currentPiece, 1, 0)) {
658
+ currentPiece.row++;
659
+ }
660
+
661
+ drawTetromino();
662
+ lockPiece();
663
+ checkLines();
664
+ spawnPiece();
665
+ }
666
+
667
+ // Lock the current piece in place
668
+ function lockPiece() {
669
+ for (let row = 0; row < currentPiece.shape.length; row++) {
670
+ for (let col = 0; col < currentPiece.shape[row].length; col++) {
671
+ if (currentPiece.shape[row][col]) {
672
+ const boardRow = currentPiece.row + row;
673
+ const boardCol = currentPiece.col + col;
674
+
675
+ if (boardRow >= 0 && boardRow < BOARD_HEIGHT && boardCol >= 0 && boardCol < BOARD_WIDTH) {
676
+ gameBoard[boardRow][boardCol] = currentPiece.color;
677
+ }
678
+ }
679
+ }
680
+ }
681
+ }
682
+
683
+ // Spawn a new piece
684
+ function spawnPiece() {
685
+ currentPiece = nextPiece || getRandomTetromino();
686
+ nextPiece = getRandomTetromino();
687
+
688
+ drawNextPiece();
689
+
690
+ // Check if game is over (collision on spawn)
691
+ if (!isValidMove(currentPiece)) {
692
+ gameOver();
693
+ }
694
+
695
+ drawTetromino();
696
+ }
697
+
698
+ // Game over
699
+ function gameOver() {
700
+ isGameOver = true;
701
+ clearInterval(gameInterval);
702
+ finalScoreElement.textContent = score;
703
+ gameOverModal.classList.add('active');
704
+ }
705
+
706
+ // Start or pause the game
707
+ function toggleGame() {
708
+ if (isGameOver) {
709
+ resetGame();
710
+ return;
711
+ }
712
+
713
+ if (isPaused) {
714
+ // Resume game
715
+ isPaused = false;
716
+ startBtn.textContent = 'Pause';
717
+ updateGameSpeed();
718
+ } else {
719
+ // Pause game
720
+ isPaused = true;
721
+ startBtn.textContent = 'Resume';
722
+ clearInterval(gameInterval);
723
+ }
724
+ }
725
+
726
+ // Reset the game
727
+ function resetGame() {
728
+ // Reset game state
729
+ gameBoard = Array(BOARD_HEIGHT).fill().map(() => Array(BOARD_WIDTH).fill(0));
730
+ score = 0;
731
+ level = 1;
732
+ linesCleared = 0;
733
+ isPaused = false;
734
+ isGameOver = false;
735
+
736
+ // Reset UI
737
+ scoreElement.textContent = score;
738
+ levelElement.textContent = level;
739
+ startBtn.textContent = 'Pause';
740
+ gameOverModal.classList.remove('active');
741
+
742
+ // Start new game
743
+ spawnPiece();
744
+ updateGameSpeed();
745
+ }
746
+
747
+ // Handle keyboard controls
748
+ function handleKeyPress(e) {
749
+ if (isGameOver) return;
750
+
751
+ switch(e.key) {
752
+ case 'ArrowLeft':
753
+ moveLeft();
754
+ break;
755
+ case 'ArrowRight':
756
+ moveRight();
757
+ break;
758
+ case 'ArrowDown':
759
+ moveDown();
760
+ break;
761
+ case 'ArrowUp':
762
+ rotatePiece();
763
+ break;
764
+ case ' ':
765
+ hardDrop();
766
+ break;
767
+ case 'p':
768
+ case 'P':
769
+ toggleGame();
770
+ break;
771
+ }
772
+ }
773
+
774
+ // Initialize the game
775
+ function init() {
776
+ initBoard();
777
+ spawnPiece();
778
+
779
+ // Event listeners
780
+ document.addEventListener('keydown', handleKeyPress);
781
+ startBtn.addEventListener('click', toggleGame);
782
+ restartBtn.addEventListener('click', resetGame);
783
+
784
+ // Start the game paused
785
+ isPaused = true;
786
+ startBtn.textContent = 'Start';
787
+ }
788
+
789
+ // Start the game
790
+ init();
791
+ });
792
+ </script>
793
+ </body>
794
+ </html>