bozhong commited on
Commit
c9ee90d
·
verified ·
1 Parent(s): b6335c3

请实现一个俄罗斯方块小游戏 - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +658 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Tetris
3
- emoji: 🚀
4
- colorFrom: gray
5
- colorTo: gray
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
3
+ emoji: 🐳
4
+ colorFrom: purple
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,658 @@
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>Responsive Tetris Game</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ /* Custom styles that can't be done with Tailwind */
10
+ .cell {
11
+ box-sizing: border-box;
12
+ border: 1px solid rgba(255, 255, 255, 0.1);
13
+ }
14
+
15
+ .cell-filled {
16
+ box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
17
+ }
18
+
19
+ @keyframes flash {
20
+ 0% { opacity: 1; }
21
+ 50% { opacity: 0.5; }
22
+ 100% { opacity: 1; }
23
+ }
24
+
25
+ .flash-animation {
26
+ animation: flash 0.3s 2;
27
+ }
28
+
29
+ /* Tetromino colors */
30
+ .cell-I { background-color: #00FFFF; }
31
+ .cell-O { background-color: #FFFF00; }
32
+ .cell-T { background-color: #AA00FF; }
33
+ .cell-S { background-color: #00FF00; }
34
+ .cell-Z { background-color: #FF0000; }
35
+ .cell-J { background-color: #0000FF; }
36
+ .cell-L { background-color: #FFAA00; }
37
+ </style>
38
+ </head>
39
+ <body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
40
+ <div class="text-center mb-4">
41
+ <h1 class="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-600 mb-2">Tetris</h1>
42
+ <div class="flex justify-center gap-8">
43
+ <div class="text-xl">
44
+ <span class="text-gray-300">Score:</span>
45
+ <span id="score" class="font-bold ml-2">0</span>
46
+ </div>
47
+ <div class="text-xl">
48
+ <span class="text-gray-300">Level:</span>
49
+ <span id="level" class="font-bold ml-2">1</span>
50
+ </div>
51
+ <div class="text-xl">
52
+ <span class="text-gray-300">Lines:</span>
53
+ <span id="lines" class="font-bold ml-2">0</span>
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ <div class="flex flex-col md:flex-row items-center gap-8">
59
+ <div class="relative">
60
+ <div id="game-board" class="grid grid-cols-10 grid-rows-20 gap-0 bg-gray-800 border-2 border-gray-700 rounded-md overflow-hidden"></div>
61
+ <div id="game-over" class="absolute inset-0 bg-black bg-opacity-80 flex flex-col items-center justify-center hidden">
62
+ <h2 class="text-3xl font-bold text-red-500 mb-4">Game Over!</h2>
63
+ <button id="restart-btn" class="px-6 py-2 bg-blue-600 hover:bg-blue-700 rounded-md font-bold transition">Play Again</button>
64
+ </div>
65
+ <div id="pause-screen" class="absolute inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden">
66
+ <h2 class="text-3xl font-bold text-yellow-400">Paused</h2>
67
+ </div>
68
+ </div>
69
+
70
+ <div class="flex flex-col gap-6">
71
+ <div class="bg-gray-800 p-4 rounded-md border border-gray-700">
72
+ <h3 class="text-xl font-semibold mb-2 text-center">Next Piece</h3>
73
+ <div id="next-piece" class="grid grid-cols-4 grid-rows-4 gap-0 w-24 h-24 mx-auto"></div>
74
+ </div>
75
+
76
+ <div class="bg-gray-800 p-4 rounded-md border border-gray-700">
77
+ <h3 class="text-xl font-semibold mb-2 text-center">Controls</h3>
78
+ <div class="grid grid-cols-3 gap-2 text-center">
79
+ <div class="bg-gray-700 p-2 rounded">← Left</div>
80
+ <div class="bg-gray-700 p-2 rounded">→ Right</div>
81
+ <div class="bg-gray-700 p-2 rounded">↑ Rotate</div>
82
+ <div class="bg-gray-700 p-2 rounded">↓ Soft Drop</div>
83
+ <div class="bg-gray-700 p-2 rounded">Space Hard Drop</div>
84
+ <div class="bg-gray-700 p-2 rounded">P Pause</div>
85
+ </div>
86
+ </div>
87
+
88
+ <button id="pause-btn" class="px-4 py-2 bg-yellow-600 hover:bg-yellow-700 rounded-md font-bold transition">Pause</button>
89
+ </div>
90
+ </div>
91
+
92
+ <div class="mt-8 text-gray-400 text-sm">
93
+ <p>Use keyboard or touch controls to play. Clear lines to score points!</p>
94
+ </div>
95
+
96
+ <script>
97
+ document.addEventListener('DOMContentLoaded', () => {
98
+ // Game constants
99
+ const COLS = 10;
100
+ const ROWS = 20;
101
+ const BLOCK_SIZE = 30;
102
+ const NEXT_PIECE_SIZE = 4;
103
+
104
+ // Game variables
105
+ let board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
106
+ let currentPiece = null;
107
+ let nextPiece = null;
108
+ let score = 0;
109
+ let level = 1;
110
+ let lines = 0;
111
+ let gameOver = false;
112
+ let isPaused = false;
113
+ let dropInterval = null;
114
+ let dropSpeed = 1000; // Initial speed (ms)
115
+
116
+ // DOM elements
117
+ const gameBoard = document.getElementById('game-board');
118
+ const nextPieceDisplay = document.getElementById('next-piece');
119
+ const scoreDisplay = document.getElementById('score');
120
+ const levelDisplay = document.getElementById('level');
121
+ const linesDisplay = document.getElementById('lines');
122
+ const gameOverScreen = document.getElementById('game-over');
123
+ const pauseScreen = document.getElementById('pause-screen');
124
+ const restartBtn = document.getElementById('restart-btn');
125
+ const pauseBtn = document.getElementById('pause-btn');
126
+
127
+ // Tetromino shapes
128
+ const SHAPES = {
129
+ I: [
130
+ [0, 0, 0, 0],
131
+ [1, 1, 1, 1],
132
+ [0, 0, 0, 0],
133
+ [0, 0, 0, 0]
134
+ ],
135
+ O: [
136
+ [1, 1],
137
+ [1, 1]
138
+ ],
139
+ T: [
140
+ [0, 1, 0],
141
+ [1, 1, 1],
142
+ [0, 0, 0]
143
+ ],
144
+ S: [
145
+ [0, 1, 1],
146
+ [1, 1, 0],
147
+ [0, 0, 0]
148
+ ],
149
+ Z: [
150
+ [1, 1, 0],
151
+ [0, 1, 1],
152
+ [0, 0, 0]
153
+ ],
154
+ J: [
155
+ [1, 0, 0],
156
+ [1, 1, 1],
157
+ [0, 0, 0]
158
+ ],
159
+ L: [
160
+ [0, 0, 1],
161
+ [1, 1, 1],
162
+ [0, 0, 0]
163
+ ]
164
+ };
165
+
166
+ const COLORS = {
167
+ I: 'cell-I',
168
+ O: 'cell-O',
169
+ T: 'cell-T',
170
+ S: 'cell-S',
171
+ Z: 'cell-Z',
172
+ J: 'cell-J',
173
+ L: 'cell-L'
174
+ };
175
+
176
+ // Initialize the game board
177
+ function initBoard() {
178
+ gameBoard.innerHTML = '';
179
+ gameBoard.style.width = `${COLS * BLOCK_SIZE}px`;
180
+ gameBoard.style.height = `${ROWS * BLOCK_SIZE}px`;
181
+
182
+ for (let row = 0; row < ROWS; row++) {
183
+ for (let col = 0; col < COLS; col++) {
184
+ const cell = document.createElement('div');
185
+ cell.className = 'cell w-full h-full';
186
+ cell.dataset.row = row;
187
+ cell.dataset.col = col;
188
+ gameBoard.appendChild(cell);
189
+ }
190
+ }
191
+ }
192
+
193
+ // Initialize the next piece display
194
+ function initNextPieceDisplay() {
195
+ nextPieceDisplay.innerHTML = '';
196
+ nextPieceDisplay.style.width = `${NEXT_PIECE_SIZE * BLOCK_SIZE}px`;
197
+ nextPieceDisplay.style.height = `${NEXT_PIECE_SIZE * BLOCK_SIZE}px`;
198
+
199
+ for (let row = 0; row < NEXT_PIECE_SIZE; row++) {
200
+ for (let col = 0; col < NEXT_PIECE_SIZE; col++) {
201
+ const cell = document.createElement('div');
202
+ cell.className = 'cell w-full h-full';
203
+ cell.dataset.row = row;
204
+ cell.dataset.col = col;
205
+ nextPieceDisplay.appendChild(cell);
206
+ }
207
+ }
208
+ }
209
+
210
+ // Create a new random piece
211
+ function createPiece() {
212
+ const types = Object.keys(SHAPES);
213
+ const type = types[Math.floor(Math.random() * types.length)];
214
+ const shape = SHAPES[type];
215
+
216
+ // For O piece (2x2), adjust starting position
217
+ const startCol = type === 'O' ? Math.floor(COLS / 2) - 1 : Math.floor(COLS / 2) - 2;
218
+
219
+ return {
220
+ shape,
221
+ type,
222
+ color: COLORS[type],
223
+ x: startCol,
224
+ y: 0
225
+ };
226
+ }
227
+
228
+ // Draw the current piece on the board
229
+ function drawPiece() {
230
+ if (!currentPiece) return;
231
+
232
+ for (let row = 0; row < currentPiece.shape.length; row++) {
233
+ for (let col = 0; col < currentPiece.shape[row].length; col++) {
234
+ if (currentPiece.shape[row][col]) {
235
+ const boardRow = currentPiece.y + row;
236
+ const boardCol = currentPiece.x + col;
237
+
238
+ if (boardRow >= 0 && boardRow < ROWS && boardCol >= 0 && boardCol < COLS) {
239
+ const cellIndex = boardRow * COLS + boardCol;
240
+ const cell = gameBoard.children[cellIndex];
241
+ cell.className = `cell cell-filled ${currentPiece.color}`;
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+
248
+ // Draw the next piece in the preview
249
+ function drawNextPiece() {
250
+ if (!nextPiece) return;
251
+
252
+ // Clear the next piece display
253
+ for (let i = 0; i < nextPieceDisplay.children.length; i++) {
254
+ nextPieceDisplay.children[i].className = 'cell w-full h-full';
255
+ }
256
+
257
+ // Center the piece in the 4x4 grid
258
+ const offsetX = Math.floor((NEXT_PIECE_SIZE - nextPiece.shape[0].length) / 2);
259
+ const offsetY = Math.floor((NEXT_PIECE_SIZE - nextPiece.shape.length) / 2);
260
+
261
+ for (let row = 0; row < nextPiece.shape.length; row++) {
262
+ for (let col = 0; col < nextPiece.shape[row].length; col++) {
263
+ if (nextPiece.shape[row][col]) {
264
+ const displayRow = row + offsetY;
265
+ const displayCol = col + offsetX;
266
+ const cellIndex = displayRow * NEXT_PIECE_SIZE + displayCol;
267
+ const cell = nextPieceDisplay.children[cellIndex];
268
+ cell.className = `cell cell-filled ${nextPiece.color}`;
269
+ }
270
+ }
271
+ }
272
+ }
273
+
274
+ // Clear the current piece from the board (before moving/rotating)
275
+ function clearPiece() {
276
+ if (!currentPiece) return;
277
+
278
+ for (let row = 0; row < currentPiece.shape.length; row++) {
279
+ for (let col = 0; col < currentPiece.shape[row].length; col++) {
280
+ if (currentPiece.shape[row][col]) {
281
+ const boardRow = currentPiece.y + row;
282
+ const boardCol = currentPiece.x + col;
283
+
284
+ if (boardRow >= 0 && boardRow < ROWS && boardCol >= 0 && boardCol < COLS) {
285
+ const cellIndex = boardRow * COLS + boardCol;
286
+ const cell = gameBoard.children[cellIndex];
287
+ cell.className = 'cell w-full h-full';
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+
294
+ // Check if the current piece can move to the specified position
295
+ function isValidMove(offsetX, offsetY, rotatedShape = null) {
296
+ const shape = rotatedShape || currentPiece.shape;
297
+
298
+ for (let row = 0; row < shape.length; row++) {
299
+ for (let col = 0; col < shape[row].length; col++) {
300
+ if (shape[row][col]) {
301
+ const newX = currentPiece.x + col + offsetX;
302
+ const newY = currentPiece.y + row + offsetY;
303
+
304
+ // Check boundaries
305
+ if (newX < 0 || newX >= COLS || newY >= ROWS) {
306
+ return false;
307
+ }
308
+
309
+ // Check collision with existing pieces (only check if moving down)
310
+ if (newY >= 0 && board[newY][newX] && offsetY >= 0) {
311
+ return false;
312
+ }
313
+ }
314
+ }
315
+ }
316
+
317
+ return true;
318
+ }
319
+
320
+ // Rotate the current piece
321
+ function rotatePiece() {
322
+ if (!currentPiece) return;
323
+
324
+ // Create a rotated version of the shape
325
+ const rotated = [];
326
+ for (let col = 0; col < currentPiece.shape[0].length; col++) {
327
+ const newRow = [];
328
+ for (let row = currentPiece.shape.length - 1; row >= 0; row--) {
329
+ newRow.push(currentPiece.shape[row][col]);
330
+ }
331
+ rotated.push(newRow);
332
+ }
333
+
334
+ // Check if the rotation is valid
335
+ if (isValidMove(0, 0, rotated)) {
336
+ clearPiece();
337
+ currentPiece.shape = rotated;
338
+ drawPiece();
339
+ } else {
340
+ // Try wall kicks (adjust position if rotation would cause collision)
341
+ const kicks = [-1, 1, -2, 2];
342
+ for (const kick of kicks) {
343
+ if (isValidMove(kick, 0, rotated)) {
344
+ clearPiece();
345
+ currentPiece.shape = rotated;
346
+ currentPiece.x += kick;
347
+ drawPiece();
348
+ break;
349
+ }
350
+ }
351
+ }
352
+ }
353
+
354
+ // Move the current piece left
355
+ function moveLeft() {
356
+ if (!currentPiece || gameOver || isPaused) return;
357
+
358
+ if (isValidMove(-1, 0)) {
359
+ clearPiece();
360
+ currentPiece.x--;
361
+ drawPiece();
362
+ }
363
+ }
364
+
365
+ // Move the current piece right
366
+ function moveRight() {
367
+ if (!currentPiece || gameOver || isPaused) return;
368
+
369
+ if (isValidMove(1, 0)) {
370
+ clearPiece();
371
+ currentPiece.x++;
372
+ drawPiece();
373
+ }
374
+ }
375
+
376
+ // Move the current piece down (soft drop)
377
+ function moveDown() {
378
+ if (!currentPiece || gameOver || isPaused) return;
379
+
380
+ if (isValidMove(0, 1)) {
381
+ clearPiece();
382
+ currentPiece.y++;
383
+ drawPiece();
384
+ return true;
385
+ } else {
386
+ lockPiece();
387
+ return false;
388
+ }
389
+ }
390
+
391
+ // Hard drop - move piece all the way down immediately
392
+ function hardDrop() {
393
+ if (!currentPiece || gameOver || isPaused) return;
394
+
395
+ while (moveDown()) {
396
+ // Keep moving down until it can't anymore
397
+ }
398
+ }
399
+
400
+ // Lock the current piece in place and check for completed lines
401
+ function lockPiece() {
402
+ if (!currentPiece) return;
403
+
404
+ // Add piece to the board
405
+ for (let row = 0; row < currentPiece.shape.length; row++) {
406
+ for (let col = 0; col < currentPiece.shape[row].length; col++) {
407
+ if (currentPiece.shape[row][col]) {
408
+ const boardRow = currentPiece.y + row;
409
+ const boardCol = currentPiece.x + col;
410
+
411
+ if (boardRow >= 0 && boardRow < ROWS && boardCol >= 0 && boardCol < COLS) {
412
+ board[boardRow][boardCol] = currentPiece.color;
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ // Check for completed lines
419
+ checkLines();
420
+
421
+ // Get next piece
422
+ currentPiece = nextPiece;
423
+ nextPiece = createPiece();
424
+ drawNextPiece();
425
+
426
+ // Check if game over (new piece can't be placed)
427
+ if (!isValidMove(0, 0)) {
428
+ gameOver = true;
429
+ clearInterval(dropInterval);
430
+ gameOverScreen.classList.remove('hidden');
431
+ }
432
+ }
433
+
434
+ // Check for completed lines and clear them
435
+ function checkLines() {
436
+ let linesCleared = 0;
437
+
438
+ for (let row = ROWS - 1; row >= 0; row--) {
439
+ if (board[row].every(cell => cell !== 0)) {
440
+ // Line is complete
441
+ linesCleared++;
442
+
443
+ // Flash animation for cleared lines
444
+ for (let col = 0; col < COLS; col++) {
445
+ const cellIndex = row * COLS + col;
446
+ const cell = gameBoard.children[cellIndex];
447
+ cell.classList.add('flash-animation');
448
+ }
449
+
450
+ // Remove the line after animation
451
+ setTimeout(() => {
452
+ // Shift all rows above down
453
+ for (let r = row; r > 0; r--) {
454
+ board[r] = [...board[r - 1]];
455
+ }
456
+ board[0] = Array(COLS).fill(0);
457
+
458
+ // Redraw the entire board
459
+ redrawBoard();
460
+ }, 300);
461
+ }
462
+ }
463
+
464
+ if (linesCleared > 0) {
465
+ // Update score based on lines cleared
466
+ const points = [0, 40, 100, 300, 1200]; // Points for 0, 1, 2, 3, 4 lines
467
+ score += points[linesCleared] * level;
468
+ lines += linesCleared;
469
+
470
+ // Every 10 lines increases the level
471
+ level = Math.floor(lines / 10) + 1;
472
+
473
+ // Increase speed with level (capped at 100ms)
474
+ dropSpeed = Math.max(100, 1000 - (level - 1) * 100);
475
+
476
+ // Update displays
477
+ updateDisplays();
478
+
479
+ // Restart the drop interval with new speed
480
+ clearInterval(dropInterval);
481
+ dropInterval = setInterval(moveDown, dropSpeed);
482
+ }
483
+ }
484
+
485
+ // Redraw the entire board (after line clears)
486
+ function redrawBoard() {
487
+ for (let row = 0; row < ROWS; row++) {
488
+ for (let col = 0; col < COLS; col++) {
489
+ const cellIndex = row * COLS + col;
490
+ const cell = gameBoard.children[cellIndex];
491
+
492
+ // Remove animation class if present
493
+ cell.classList.remove('flash-animation');
494
+
495
+ if (board[row][col]) {
496
+ cell.className = `cell cell-filled ${board[row][col]}`;
497
+ } else {
498
+ cell.className = 'cell w-full h-full';
499
+ }
500
+ }
501
+ }
502
+ }
503
+
504
+ // Update score, level, and lines displays
505
+ function updateDisplays() {
506
+ scoreDisplay.textContent = score;
507
+ levelDisplay.textContent = level;
508
+ linesDisplay.textContent = lines;
509
+ }
510
+
511
+ // Start the game
512
+ function startGame() {
513
+ // Reset game state
514
+ board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
515
+ score = 0;
516
+ level = 1;
517
+ lines = 0;
518
+ gameOver = false;
519
+ dropSpeed = 1000;
520
+
521
+ // Create pieces
522
+ currentPiece = createPiece();
523
+ nextPiece = createPiece();
524
+
525
+ // Update displays
526
+ updateDisplays();
527
+
528
+ // Draw initial state
529
+ redrawBoard();
530
+ drawPiece();
531
+ drawNextPiece();
532
+
533
+ // Hide game over screen
534
+ gameOverScreen.classList.add('hidden');
535
+
536
+ // Start the drop interval
537
+ clearInterval(dropInterval);
538
+ dropInterval = setInterval(moveDown, dropSpeed);
539
+ }
540
+
541
+ // Toggle pause
542
+ function togglePause() {
543
+ if (gameOver) return;
544
+
545
+ isPaused = !isPaused;
546
+
547
+ if (isPaused) {
548
+ clearInterval(dropInterval);
549
+ pauseScreen.classList.remove('hidden');
550
+ } else {
551
+ dropInterval = setInterval(moveDown, dropSpeed);
552
+ pauseScreen.classList.add('hidden');
553
+ }
554
+ }
555
+
556
+ // Event listeners
557
+ document.addEventListener('keydown', (e) => {
558
+ if (gameOver && e.key === 'Enter') {
559
+ startGame();
560
+ return;
561
+ }
562
+
563
+ if (isPaused && e.key !== 'p') return;
564
+
565
+ switch (e.key) {
566
+ case 'ArrowLeft':
567
+ moveLeft();
568
+ break;
569
+ case 'ArrowRight':
570
+ moveRight();
571
+ break;
572
+ case 'ArrowDown':
573
+ moveDown();
574
+ break;
575
+ case 'ArrowUp':
576
+ rotatePiece();
577
+ break;
578
+ case ' ':
579
+ hardDrop();
580
+ break;
581
+ case 'p':
582
+ case 'P':
583
+ togglePause();
584
+ break;
585
+ }
586
+ });
587
+
588
+ // Touch controls for mobile
589
+ let touchStartX = 0;
590
+ let touchStartY = 0;
591
+
592
+ gameBoard.addEventListener('touchstart', (e) => {
593
+ if (gameOver || isPaused) return;
594
+
595
+ touchStartX = e.touches[0].clientX;
596
+ touchStartY = e.touches[0].clientY;
597
+ e.preventDefault();
598
+ }, { passive: false });
599
+
600
+ gameBoard.addEventListener('touchmove', (e) => {
601
+ if (gameOver || isPaused) return;
602
+
603
+ const touchX = e.touches[0].clientX;
604
+ const touchY = e.touches[0].clientY;
605
+ const diffX = touchX - touchStartX;
606
+ const diffY = touchY - touchStartY;
607
+
608
+ // Horizontal swipe
609
+ if (Math.abs(diffX) > Math.abs(diffY)) {
610
+ if (diffX > 30) {
611
+ moveRight();
612
+ touchStartX = touchX;
613
+ } else if (diffX < -30) {
614
+ moveLeft();
615
+ touchStartX = touchX;
616
+ }
617
+ }
618
+
619
+ // Vertical swipe down (soft drop)
620
+ if (diffY > 30) {
621
+ moveDown();
622
+ touchStartY = touchY;
623
+ }
624
+
625
+ e.preventDefault();
626
+ }, { passive: false });
627
+
628
+ gameBoard.addEventListener('touchend', (e) => {
629
+ if (gameOver || isPaused) return;
630
+
631
+ const touchEndX = e.changedTouches[0].clientX;
632
+ const touchEndY = e.changedTouches[0].clientY;
633
+ const diffX = touchEndX - touchStartX;
634
+ const diffY = touchEndY - touchStartY;
635
+
636
+ // Tap (rotate)
637
+ if (Math.abs(diffX) < 10 && Math.abs(diffY) < 10) {
638
+ rotatePiece();
639
+ }
640
+
641
+ // Quick swipe up (hard drop)
642
+ if (diffY < -50) {
643
+ hardDrop();
644
+ }
645
+ });
646
+
647
+ // Button event listeners
648
+ restartBtn.addEventListener('click', startGame);
649
+ pauseBtn.addEventListener('click', togglePause);
650
+
651
+ // Initialize the game
652
+ initBoard();
653
+ initNextPieceDisplay();
654
+ startGame();
655
+ });
656
+ </script>
657
+ <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 <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=bozhong/tetris" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
658
+ </html>