nara-simba commited on
Commit
d36a31e
·
verified ·
1 Parent(s): 4413285

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +738 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Tetris Experiment Deepsite
3
- emoji: 📚
4
- colorFrom: yellow
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: tetris-experiment-deepsite
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,738 @@
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 Game</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
13
+ }
14
+
15
+ body {
16
+ background: linear-gradient(135deg, #1a1a2e, #16213e);
17
+ color: #fff;
18
+ min-height: 100vh;
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ padding: 20px;
23
+ }
24
+
25
+ h1 {
26
+ margin: 20px 0;
27
+ text-align: center;
28
+ color: #fff;
29
+ text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
30
+ font-size: 2.5rem;
31
+ }
32
+
33
+ .game-container {
34
+ display: flex;
35
+ flex-wrap: wrap;
36
+ justify-content: center;
37
+ gap: 20px;
38
+ max-width: 1000px;
39
+ width: 100%;
40
+ }
41
+
42
+ .game-board {
43
+ border: 4px solid #00ffff;
44
+ border-radius: 5px;
45
+ box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
46
+ background-color: rgba(0, 0, 0, 0.7);
47
+ }
48
+
49
+ .info-panel {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: 20px;
53
+ background-color: rgba(0, 0, 0, 0.7);
54
+ padding: 20px;
55
+ border-radius: 10px;
56
+ border: 2px solid #00ffff;
57
+ box-shadow: 0 0 15px rgba(0, 255, 255, 0.2);
58
+ min-width: 200px;
59
+ }
60
+
61
+ .next-piece {
62
+ text-align: center;
63
+ padding: 10px;
64
+ background-color: rgba(0, 0, 0, 0.5);
65
+ border-radius: 5px;
66
+ border: 1px solid #00ffff;
67
+ }
68
+
69
+ .score-display {
70
+ text-align: center;
71
+ padding: 15px;
72
+ background-color: rgba(0, 0, 0, 0.5);
73
+ border-radius: 5px;
74
+ border: 1px solid #00ffff;
75
+ }
76
+
77
+ .score-display h3 {
78
+ margin-bottom: 10px;
79
+ color: #00ffff;
80
+ }
81
+
82
+ .score-value {
83
+ font-size: 2rem;
84
+ font-weight: bold;
85
+ color: #fff;
86
+ }
87
+
88
+ .level-display {
89
+ text-align: center;
90
+ padding: 15px;
91
+ background-color: rgba(0, 0, 0, 0.5);
92
+ border-radius: 5px;
93
+ border: 1px solid #00ffff;
94
+ }
95
+
96
+ .level-display h3 {
97
+ margin-bottom: 10px;
98
+ color: #00ffff;
99
+ }
100
+
101
+ .level-value {
102
+ font-size: 2rem;
103
+ font-weight: bold;
104
+ color: #fff;
105
+ }
106
+
107
+ .controls {
108
+ margin-top: 10px;
109
+ text-align: center;
110
+ }
111
+
112
+ .controls h3 {
113
+ margin-bottom: 10px;
114
+ color: #00ffff;
115
+ }
116
+
117
+ .control-key {
118
+ display: inline-block;
119
+ margin: 5px;
120
+ padding: 8px 12px;
121
+ background-color: rgba(0, 255, 255, 0.2);
122
+ border: 1px solid #00ffff;
123
+ border-radius: 5px;
124
+ font-family: monospace;
125
+ font-weight: bold;
126
+ }
127
+
128
+ .game-over {
129
+ position: absolute;
130
+ top: 0;
131
+ left: 0;
132
+ width: 100%;
133
+ height: 100%;
134
+ background-color: rgba(0, 0, 0, 0.8);
135
+ display: flex;
136
+ flex-direction: column;
137
+ justify-content: center;
138
+ align-items: center;
139
+ z-index: 10;
140
+ display: none;
141
+ }
142
+
143
+ .game-over h2 {
144
+ font-size: 3rem;
145
+ color: #ff0000;
146
+ margin-bottom: 20px;
147
+ text-shadow: 0 0 10px rgba(255, 0, 0, 0.5);
148
+ }
149
+
150
+ .restart-btn {
151
+ padding: 15px 30px;
152
+ background-color: #00ffff;
153
+ color: #000;
154
+ border: none;
155
+ border-radius: 5px;
156
+ font-size: 1.2rem;
157
+ font-weight: bold;
158
+ cursor: pointer;
159
+ transition: all 0.3s;
160
+ }
161
+
162
+ .restart-btn:hover {
163
+ background-color: #fff;
164
+ box-shadow: 0 0 15px rgba(0, 255, 255, 0.8);
165
+ }
166
+
167
+ @media (max-width: 768px) {
168
+ .game-container {
169
+ flex-direction: column;
170
+ align-items: center;
171
+ }
172
+
173
+ .game-board {
174
+ width: 300px;
175
+ height: 600px;
176
+ }
177
+
178
+ .info-panel {
179
+ width: 300px;
180
+ flex-direction: row;
181
+ justify-content: space-between;
182
+ flex-wrap: wrap;
183
+ }
184
+
185
+ .next-piece, .score-display, .level-display {
186
+ flex: 1;
187
+ min-width: 120px;
188
+ }
189
+ }
190
+
191
+ @media (max-width: 400px) {
192
+ .game-board {
193
+ width: 250px;
194
+ height: 500px;
195
+ }
196
+
197
+ .info-panel {
198
+ width: 250px;
199
+ }
200
+ }
201
+ </style>
202
+ </head>
203
+ <body>
204
+ <h1>MODERN TETRIS</h1>
205
+
206
+ <div class="game-container">
207
+ <canvas id="board" class="game-board" width="300" height="600"></canvas>
208
+
209
+ <div class="info-panel">
210
+ <div class="next-piece">
211
+ <h3>Next Piece</h3>
212
+ <canvas id="next" width="150" height="150"></canvas>
213
+ </div>
214
+
215
+ <div class="score-display">
216
+ <h3>Score</h3>
217
+ <div class="score-value" id="score">0</div>
218
+ </div>
219
+
220
+ <div class="level-display">
221
+ <h3>Level</h3>
222
+ <div class="level-value" id="level">1</div>
223
+ </div>
224
+
225
+ <div class="controls">
226
+ <h3>Controls</h3>
227
+ <div>
228
+ <span class="control-key">←</span>
229
+ <span class="control-key">→</span> Move<br>
230
+ <span class="control-key">↑</span> Rotate<br>
231
+ <span class="control-key">↓</span> Soft Drop<br>
232
+ <span class="control-key">Space</span> Hard Drop<br>
233
+ <span class="control-key">P</span> Pause
234
+ </div>
235
+ </div>
236
+ </div>
237
+ </div>
238
+
239
+ <div class="game-over" id="gameOver">
240
+ <h2>GAME OVER</h2>
241
+ <div class="final-score">Score: <span id="finalScore">0</span></div>
242
+ <button class="restart-btn" id="restartBtn">Play Again</button>
243
+ </div>
244
+
245
+ <script>
246
+ document.addEventListener('DOMContentLoaded', () => {
247
+ const canvas = document.getElementById('board');
248
+ const ctx = canvas.getContext('2d');
249
+
250
+ const nextCanvas = document.getElementById('next');
251
+ const nextCtx = nextCanvas.getContext('2d');
252
+
253
+ const scoreElement = document.getElementById('score');
254
+ const levelElement = document.getElementById('level');
255
+ const finalScoreElement = document.getElementById('finalScore');
256
+ const gameOverElement = document.getElementById('gameOver');
257
+ const restartBtn = document.getElementById('restartBtn');
258
+
259
+ // Calculate size of each block
260
+ const blockSize = canvas.width / COLS;
261
+
262
+ // Game variables
263
+ let score = 0;
264
+ let level = 1;
265
+ let gameSpeed = 800; // Initial speed in ms
266
+ let gameOver = false;
267
+ let isPaused = false;
268
+ let dropInterval;
269
+
270
+ // Current piece
271
+ let currentPiece = null;
272
+ let nextPiece = null;
273
+
274
+ // Board - 2D array (COLS x ROWS)
275
+ let board = createBoard();
276
+
277
+ // Initialize the game
278
+ function init() {
279
+ board = createBoard();
280
+ score = 0;
281
+ level = 1;
282
+ gameSpeed = 800;
283
+ gameOver = false;
284
+ scoreElement.textContent = score;
285
+ levelElement.textContent = level;
286
+ gameOverElement.style.display = 'none';
287
+
288
+ // Generate first pieces
289
+ nextPiece = generatePiece();
290
+ newPiece();
291
+
292
+ // Start game loop
293
+ if (dropInterval) clearInterval(dropInterval);
294
+ dropInterval = setInterval(dropPiece, gameSpeed);
295
+
296
+ // Draw initial state
297
+ draw();
298
+ drawNext();
299
+ }
300
+
301
+ // Create empty board
302
+ function createBoard() {
303
+ return Array.from(Array(ROWS), () => Array(COLS).fill(0));
304
+ }
305
+
306
+ // Draw the board and current piece
307
+ function draw() {
308
+ // Clear the canvas
309
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
310
+
311
+ // Draw the board
312
+ for (let r = 0; r < ROWS; r++) {
313
+ for (let c = 0; c < COLS; c++) {
314
+ if (board[r][c]) {
315
+ drawBlock(ctx, c, r, board[r][c]);
316
+ }
317
+ }
318
+ }
319
+
320
+ // Draw the current piece
321
+ if (currentPiece) {
322
+ for (let r = 0; r < currentPiece.tetromino.length; r++) {
323
+ for (let c = 0; c < currentPiece.tetromino[r].length; c++) {
324
+ if (currentPiece.tetromino[r][c]) {
325
+ drawBlock(
326
+ ctx,
327
+ currentPiece.x + c,
328
+ currentPiece.y + r,
329
+ currentPiece.color
330
+ );
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ // Draw the next piece
338
+ function drawNext() {
339
+ nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
340
+
341
+ if (nextPiece) {
342
+ // Center the piece in the next canvas
343
+ const offsetX = (nextCanvas.width / blockSize - nextPiece.tetromino[0].length) / 2;
344
+ const offsetY = (nextCanvas.height / blockSize - nextPiece.tetromino.length) / 2;
345
+
346
+ for (let r = 0; r < nextPiece.tetromino.length; r++) {
347
+ for (let c = 0; c < nextPiece.tetromino[r].length; c++) {
348
+ if (nextPiece.tetromino[r][c]) {
349
+ drawBlock(
350
+ nextCtx,
351
+ offsetX + c,
352
+ offsetY + r,
353
+ nextPiece.color,
354
+ true
355
+ );
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ // Draw a single block
363
+ function drawBlock(context, x, y, color, isNext = false) {
364
+ const size = isNext ? blockSize * 0.8 : blockSize;
365
+ const offset = isNext ? blockSize * 0.1 : 0;
366
+
367
+ context.fillStyle = color;
368
+ context.fillRect(x * blockSize + offset, y * blockSize + offset, size, size);
369
+
370
+ // Add 3D effect
371
+ context.strokeStyle = 'rgba(255, 255, 255, 0.3)';
372
+ context.lineWidth = 2;
373
+ context.strokeRect(x * blockSize + offset, y * blockSize + offset, size, size);
374
+
375
+ // Add highlight effect
376
+ context.fillStyle = 'rgba(255, 255, 255, 0.1)';
377
+ context.fillRect(x * blockSize + offset + 2, y * blockSize + offset + 2, size * 0.4, size * 0.4);
378
+ }
379
+
380
+ // Generate a new random piece
381
+ function generatePiece() {
382
+ const randomIndex = Math.floor(Math.random() * PIECES.length);
383
+ const piece = JSON.parse(JSON.stringify(PIECES[randomIndex]));
384
+
385
+ // Adjust position to spawn at top center
386
+ piece.x = Math.floor(COLS / 2) - Math.floor(piece.tetromino[0].length / 2);
387
+ piece.y = 0;
388
+
389
+ return piece;
390
+ }
391
+
392
+ // Create new piece for play
393
+ function newPiece() {
394
+ currentPiece = nextPiece;
395
+ nextPiece = generatePiece();
396
+ drawNext();
397
+
398
+ // Check for collision immediately (game over condition)
399
+ if (isColliding()) {
400
+ endGame();
401
+ }
402
+ }
403
+
404
+ // Move piece down
405
+ function dropPiece() {
406
+ if (!gameOver && !isPaused) {
407
+ if (isColliding(0, 1)) {
408
+ lockPiece();
409
+ clearLines();
410
+ newPiece();
411
+ } else {
412
+ currentPiece.y++;
413
+ draw();
414
+ }
415
+ }
416
+ }
417
+
418
+ // Move piece left
419
+ function moveLeft() {
420
+ if (!isColliding(-1, 0)) {
421
+ currentPiece.x--;
422
+ draw();
423
+ }
424
+ }
425
+
426
+ // Move piece right
427
+ function moveRight() {
428
+ if (!isColliding(1, 0)) {
429
+ currentPiece.x++;
430
+ draw();
431
+ }
432
+ }
433
+
434
+ // Rotate piece
435
+ function rotate() {
436
+ const originalTetromino = currentPiece.tetromino;
437
+ const originalRotation = currentPiece.rotation;
438
+
439
+ // Try current rotation
440
+ currentPiece.tetromino = rotateMatrix(currentPiece.tetromino);
441
+ currentPiece.rotation = (currentPiece.rotation + 1) % 4;
442
+
443
+ // If rotation causes collision, try wall kicks
444
+ if (isColliding()) {
445
+ // Try moving left
446
+ currentPiece.x--;
447
+ if (isColliding()) {
448
+ currentPiece.x += 2; // Try moving right
449
+ if (isColliding()) {
450
+ currentPiece.x--; // Restore x position
451
+ currentPiece.tetromino = originalTetromino;
452
+ currentPiece.rotation = originalRotation;
453
+ }
454
+ }
455
+ }
456
+
457
+ draw();
458
+ }
459
+
460
+ // Rotate matrix 90 degrees
461
+ function rotateMatrix(matrix) {
462
+ const N = matrix.length;
463
+ const rotated = Array.from(Array(matrix[0].length), () => Array(N).fill(0));
464
+
465
+ for (let r = 0; r < N; r++) {
466
+ for (let c = 0; c < matrix[r].length; c++) {
467
+ rotated[c][N - 1 - r] = matrix[r][c];
468
+ }
469
+ }
470
+
471
+ return rotated;
472
+ }
473
+
474
+ // Check for collisions
475
+ function isColliding(offsetX = 0, offsetY = 0) {
476
+ for (let r = 0; r < currentPiece.tetromino.length; r++) {
477
+ for (let c = 0; c < currentPiece.tetromino[r].length; c++) {
478
+ // Skip empty blocks
479
+ if (!currentPiece.tetromino[r][c]) continue;
480
+
481
+ const newX = currentPiece.x + c + offsetX;
482
+ const newY = currentPiece.y + r + offsetY;
483
+
484
+ // Check walls and floor
485
+ if (newX < 0 || newX >= COLS || newY >= ROWS) {
486
+ return true;
487
+ }
488
+
489
+ // Check for collision with existing blocks
490
+ if (newY >= 0 && board[newY][newX]) {
491
+ return true;
492
+ }
493
+ }
494
+ }
495
+
496
+ return false;
497
+ }
498
+
499
+ // Lock piece in place
500
+ function lockPiece() {
501
+ for (let r = 0; r < currentPiece.tetromino.length; r++) {
502
+ for (let c = 0; c < currentPiece.tetromino[r].length; c++) {
503
+ if (currentPiece.tetromino[r][c]) {
504
+ const y = currentPiece.y + r;
505
+ const x = currentPiece.x + c;
506
+
507
+ if (y >= 0) { // Don't draw above the board
508
+ board[y][x] = currentPiece.color;
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+
515
+ // Check for completed lines and clear them
516
+ function clearLines() {
517
+ let linesCleared = 0;
518
+
519
+ for (let r = ROWS - 1; r >= 0; r--) {
520
+ // Check if line is complete
521
+ if (board[r].every(cell => cell !== 0)) {
522
+ // Remove the line
523
+ board.splice(r, 1);
524
+ // Add new empty line at top
525
+ board.unshift(Array(COLS).fill(0));
526
+ linesCleared++;
527
+ r++; // Check the same row again (it's now the next row after splice)
528
+ }
529
+ }
530
+
531
+ if (linesCleared > 0) {
532
+ // Update score
533
+ const points = [0, 40, 100, 300, 1200]; // Points for 0, 1, 2, 3, 4 lines
534
+ score += points[linesCleared] * level;
535
+ scoreElement.textContent = score;
536
+
537
+ // Check for level up (every 10 lines)
538
+ const newLevel = Math.floor(score / 1000) + 1;
539
+ if (newLevel > level) {
540
+ level = newLevel;
541
+ levelElement.textContent = level;
542
+
543
+ // Increase speed
544
+ gameSpeed = Math.max(100, 800 - (level - 1) * 70);
545
+ clearInterval(dropInterval);
546
+ dropInterval = setInterval(dropPiece, gameSpeed);
547
+ }
548
+ }
549
+ }
550
+
551
+ // Hard drop - move piece all the way down immediately
552
+ function hardDrop() {
553
+ while (!isColliding(0, 1)) {
554
+ currentPiece.y++;
555
+ }
556
+ lockPiece();
557
+ clearLines();
558
+ newPiece();
559
+ draw();
560
+ }
561
+
562
+ // Toggle pause
563
+ function togglePause() {
564
+ isPaused = !isPaused;
565
+ if (isPaused) {
566
+ // Show pause message
567
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
568
+ ctx.fillRect(0, canvas.height / 2 - 30, canvas.width, 60);
569
+ ctx.fillStyle = '#00ffff';
570
+ ctx.font = '30px Arial';
571
+ ctx.textAlign = 'center';
572
+ ctx.fillText('PAUSED', canvas.width / 2, canvas.height / 2 + 10);
573
+ } else {
574
+ // Redraw game board
575
+ draw();
576
+ }
577
+ }
578
+
579
+ // End game
580
+ function endGame() {
581
+ gameOver = true;
582
+ clearInterval(dropInterval);
583
+ finalScoreElement.textContent = score;
584
+ gameOverElement.style.display = 'flex';
585
+ }
586
+
587
+ // Event listeners
588
+ document.addEventListener('keydown', (e) => {
589
+ if (gameOver) return;
590
+
591
+ switch (e.keyCode) {
592
+ case 37: // Left arrow
593
+ moveLeft();
594
+ break;
595
+ case 39: // Right arrow
596
+ moveRight();
597
+ break;
598
+ case 40: // Down arrow
599
+ dropPiece();
600
+ break;
601
+ case 38: // Up arrow
602
+ rotate();
603
+ break;
604
+ case 32: // Space (hard drop)
605
+ hardDrop();
606
+ break;
607
+ case 80: // P (pause)
608
+ togglePause();
609
+ break;
610
+ }
611
+ });
612
+
613
+ // Mobile touch controls
614
+ canvas.addEventListener('touchstart', handleTouchStart, false);
615
+ canvas.addEventListener('touchmove', handleTouchMove, false);
616
+ let touchStartX = null;
617
+
618
+ function handleTouchStart(e) {
619
+ if (gameOver || isPaused) return;
620
+ touchStartX = e.touches[0].clientX;
621
+ }
622
+
623
+ function handleTouchMove(e) {
624
+ if (gameOver || isPaused || touchStartX === null) return;
625
+ e.preventDefault();
626
+
627
+ const touchX = e.touches[0].clientX;
628
+ const dx = touchX - touchStartX;
629
+
630
+ if (Math.abs(dx) > 50) { // Threshold to register a swipe
631
+ if (dx > 0) {
632
+ moveRight();
633
+ } else {
634
+ moveLeft();
635
+ }
636
+ touchStartX = touchX;
637
+ }
638
+ }
639
+
640
+ // Mobile tap for rotation
641
+ canvas.addEventListener('touchend', (e) => {
642
+ if (gameOver || isPaused) return;
643
+
644
+ if (touchStartX !== null) {
645
+ const touchY = e.changedTouches[0].clientY;
646
+ const startY = e.changedTouches[0].clientY;
647
+ const dy = touchY - startY;
648
+
649
+ if (Math.abs(dy) < 10) { // Tapped without much movement
650
+ rotate();
651
+ } else if (dy > 30) { // Swiped down
652
+ hardDrop();
653
+ }
654
+ }
655
+
656
+ touchStartX = null;
657
+ });
658
+
659
+ // Restart button
660
+ restartBtn.addEventListener('click', init);
661
+
662
+ // Game constants
663
+ const COLS = 10;
664
+ const ROWS = 20;
665
+
666
+ // Tetromino pieces
667
+ const PIECES = [
668
+ { // Z
669
+ tetromino: [
670
+ [1, 1, 0],
671
+ [0, 1, 1],
672
+ [0, 0, 0]
673
+ ],
674
+ color: '#FF0000',
675
+ rotation: 0
676
+ },
677
+ { // S
678
+ tetromino: [
679
+ [0, 1, 1],
680
+ [1, 1, 0],
681
+ [0, 0, 0]
682
+ ],
683
+ color: '#00FF00',
684
+ rotation: 0
685
+ },
686
+ { // T
687
+ tetromino: [
688
+ [0, 1, 0],
689
+ [1, 1, 1],
690
+ [0, 0, 0]
691
+ ],
692
+ color: '#AA00FF',
693
+ rotation: 0
694
+ },
695
+ { // O
696
+ tetromino: [
697
+ [1, 1],
698
+ [1, 1]
699
+ ],
700
+ color: '#FFFF00',
701
+ rotation: 0
702
+ },
703
+ { // L
704
+ tetromino: [
705
+ [0, 0, 1],
706
+ [1, 1, 1],
707
+ [0, 0, 0]
708
+ ],
709
+ color: '#FFA500',
710
+ rotation: 0
711
+ },
712
+ { // J
713
+ tetromino: [
714
+ [1, 0, 0],
715
+ [1, 1, 1],
716
+ [0, 0, 0]
717
+ ],
718
+ color: '#0000FF',
719
+ rotation: 0
720
+ },
721
+ { // I
722
+ tetromino: [
723
+ [0, 0, 0, 0],
724
+ [1, 1, 1, 1],
725
+ [0, 0, 0, 0],
726
+ [0, 0, 0, 0]
727
+ ],
728
+ color: '#00FFFF',
729
+ rotation: 0
730
+ }
731
+ ];
732
+
733
+ // Start the game
734
+ init();
735
+ });
736
+ </script>
737
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
738
+ </html>