MitchiMitch commited on
Commit
bdf8912
·
verified ·
1 Parent(s): a19888f

it doesnt work now, please fix the game and make sure it works - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +987 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Retrotetris
3
- emoji: 👀
4
  colorFrom: red
5
- colorTo: blue
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: retrotetris
3
+ emoji: 🐳
4
  colorFrom: red
5
+ colorTo: purple
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,987 @@
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>Retro Tetris</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .particle {
11
+ position: absolute;
12
+ width: 4px;
13
+ height: 4px;
14
+ border-radius: 50%;
15
+ pointer-events: none;
16
+ z-index: 10;
17
+ }
18
+
19
+ @keyframes flash {
20
+ 0% { opacity: 1; }
21
+ 50% { opacity: 0.5; }
22
+ 100% { opacity: 1; }
23
+ }
24
+
25
+ .flash {
26
+ animation: flash 0.5s infinite;
27
+ }
28
+
29
+ @keyframes lineClear {
30
+ 0% { transform: scaleY(1); opacity: 1; }
31
+ 50% { transform: scaleY(0.1); opacity: 0.5; }
32
+ 100% { transform: scaleY(1); opacity: 1; }
33
+ }
34
+
35
+ .line-clear {
36
+ animation: lineClear 0.3s ease-out;
37
+ }
38
+
39
+ .cell {
40
+ width: 30px;
41
+ height: 30px;
42
+ border: 1px solid rgba(255, 255, 255, 0.1);
43
+ box-sizing: border-box;
44
+ }
45
+
46
+ @media (max-width: 640px) {
47
+ .cell {
48
+ width: 20px;
49
+ height: 20px;
50
+ }
51
+ }
52
+
53
+ .game-container {
54
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
55
+ box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
56
+ }
57
+
58
+ .grid-line {
59
+ position: absolute;
60
+ background-color: rgba(255, 255, 255, 0.05);
61
+ }
62
+
63
+ .piece-I { background-color: #00f0f0; }
64
+ .piece-J { background-color: #0000f0; }
65
+ .piece-L { background-color: #f0a000; }
66
+ .piece-O { background-color: #f0f000; }
67
+ .piece-S { background-color: #00f000; }
68
+ .piece-T { background-color: #a000f0; }
69
+ .piece-Z { background-color: #f00000; }
70
+
71
+ .ghost {
72
+ opacity: 0.3;
73
+ }
74
+ </style>
75
+ </head>
76
+ <body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4 font-mono">
77
+ <div class="text-center mb-4">
78
+ <h1 class="text-4xl md:text-5xl font-bold mb-2 text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-cyan-400">
79
+ RETRO TETRIS
80
+ </h1>
81
+ <p class="text-gray-400">Use arrow keys to move, space to drop, and 'P' to pause</p>
82
+ </div>
83
+
84
+ <div class="flex flex-col md:flex-row items-center justify-center gap-8">
85
+ <div class="game-container relative rounded-lg overflow-hidden">
86
+ <div id="game-board" class="grid grid-cols-10 grid-rows-20 gap-0 relative"></div>
87
+
88
+ <!-- Grid lines for visual enhancement -->
89
+ <div class="grid-lines absolute inset-0 pointer-events-none">
90
+ <!-- Vertical lines -->
91
+ <div class="grid-line w-px h-full" style="left: 0%;"></div>
92
+ <div class="grid-line w-px h-full" style="left: 10%;"></div>
93
+ <div class="grid-line w-px h-full" style="left: 20%;"></div>
94
+ <div class="grid-line w-px h-full" style="left: 30%;"></div>
95
+ <div class="grid-line w-px h-full" style="left: 40%;"></div>
96
+ <div class="grid-line w-px h-full" style="left: 50%;"></div>
97
+ <div class="grid-line w-px h-full" style="left: 60%;"></div>
98
+ <div class="grid-line w-px h-full" style="left: 70%;"></div>
99
+ <div class="grid-line w-px h-full" style="left: 80%;"></div>
100
+ <div class="grid-line w-px h-full" style="left: 90%;"></div>
101
+ <div class="grid-line w-px h-full" style="left: 100%;"></div>
102
+
103
+ <!-- Horizontal lines -->
104
+ <div class="grid-line w-full h-px" style="top: 0%;"></div>
105
+ <div class="grid-line w-full h-px" style="top: 5%;"></div>
106
+ <div class="grid-line w-full h-px" style="top: 10%;"></div>
107
+ <div class="grid-line w-full h-px" style="top: 15%;"></div>
108
+ <div class="grid-line w-full h-px" style="top: 20%;"></div>
109
+ <div class="grid-line w-full h-px" style="top: 25%;"></div>
110
+ <div class="grid-line w-full h-px" style="top: 30%;"></div>
111
+ <div class="grid-line w-full h-px" style="top: 35%;"></div>
112
+ <div class="grid-line w-full h-px" style="top: 40%;"></div>
113
+ <div class="grid-line w-full h-px" style="top: 45%;"></div>
114
+ <div class="grid-line w-full h-px" style="top: 50%;"></div>
115
+ <div class="grid-line w-full h-px" style="top: 55%;"></div>
116
+ <div class="grid-line w-full h-px" style="top: 60%;"></div>
117
+ <div class="grid-line w-full h-px" style="top: 65%;"></div>
118
+ <div class="grid-line w-full h-px" style="top: 70%;"></div>
119
+ <div class="grid-line w-full h-px" style="top: 75%;"></div>
120
+ <div class="grid-line w-full h-px" style="top: 80%;"></div>
121
+ <div class="grid-line w-full h-px" style="top: 85%;"></div>
122
+ <div class="grid-line w-full h-px" style="top: 90%;"></div>
123
+ <div class="grid-line w-full h-px" style="top: 95%;"></div>
124
+ <div class="grid-line w-full h-px" style="top: 100%;"></div>
125
+ </div>
126
+ </div>
127
+
128
+ <div class="game-info bg-gray-800 p-6 rounded-lg w-full max-w-xs">
129
+ <div class="mb-6">
130
+ <h2 class="text-xl font-bold mb-2 text-cyan-400">STATS</h2>
131
+ <div class="grid grid-cols-2 gap-4">
132
+ <div>
133
+ <p class="text-gray-400">Score</p>
134
+ <p id="score" class="text-2xl font-bold">0</p>
135
+ </div>
136
+ <div>
137
+ <p class="text-gray-400">Level</p>
138
+ <p id="level" class="text-2xl font-bold">1</p>
139
+ </div>
140
+ <div>
141
+ <p class="text-gray-400">Lines</p>
142
+ <p id="lines" class="text-2xl font-bold">0</p>
143
+ </div>
144
+ </div>
145
+ </div>
146
+
147
+ <div class="mb-6">
148
+ <h2 class="text-xl font-bold mb-2 text-cyan-400">HOLD</h2>
149
+ <div id="hold-piece" class="grid grid-cols-4 grid-rows-4 gap-1 bg-gray-700 p-2 rounded h-32 mb-4"></div>
150
+
151
+ <h2 class="text-xl font-bold mb-2 text-cyan-400">NEXT</h2>
152
+ <div id="next-piece" class="grid grid-cols-4 grid-rows-4 gap-1 bg-gray-700 p-2 rounded h-32"></div>
153
+ <div id="next-piece-2" class="grid grid-cols-4 grid-rows-4 gap-1 bg-gray-700 p-2 rounded h-32 mt-2"></div>
154
+ <div id="next-piece-3" class="grid grid-cols-4 grid-rows-4 gap-1 bg-gray-700 p-2 rounded h-32 mt-2"></div>
155
+ </div>
156
+
157
+ <div class="mb-6">
158
+ <h2 class="text-xl font-bold mb-2 text-cyan-400">CONTROLS</h2>
159
+ <div class="grid grid-cols-3 gap-2 text-center">
160
+ <div class="bg-gray-700 p-2 rounded">
161
+ <i class="fas fa-arrow-up"></i>
162
+ <p class="text-xs mt-1">Rotate</p>
163
+ </div>
164
+ <div class="bg-gray-700 p-2 rounded">
165
+ <i class="fas fa-arrow-left"></i>
166
+ <p class="text-xs mt-1">Left</p>
167
+ </div>
168
+ <div class="bg-gray-700 p-2 rounded">
169
+ <i class="fas fa-arrow-right"></i>
170
+ <p class="text-xs mt-1">Right</p>
171
+ </div>
172
+ <div class="bg-gray-700 p-2 rounded">
173
+ <i class="fas fa-arrow-down"></i>
174
+ <p class="text-xs mt-1">Soft Drop</p>
175
+ </div>
176
+ <div class="bg-gray-700 p-2 rounded">
177
+ <i class="fas fa-space-shuttle"></i>
178
+ <p class="text-xs mt-1">Hard Drop</p>
179
+ </div>
180
+ <div class="bg-gray-700 p-2 rounded">
181
+ <i class="fas fa-pause"></i>
182
+ <p class="text-xs mt-1">Pause</p>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <button id="start-btn" class="w-full bg-gradient-to-r from-purple-500 to-cyan-500 py-2 rounded-lg font-bold hover:opacity-90 transition">
188
+ START GAME
189
+ </button>
190
+
191
+ <button id="pause-btn" class="w-full bg-yellow-500 py-2 rounded-lg font-bold hover:opacity-90 transition hidden mt-2">
192
+ PAUSE
193
+ </button>
194
+ </div>
195
+ </div>
196
+
197
+ <div id="game-over" class="fixed inset-0 bg-black bg-opacity-80 flex items-center justify-center hidden z-50">
198
+ <div class="bg-gray-800 p-8 rounded-lg max-w-md w-full text-center">
199
+ <h2 class="text-3xl font-bold text-red-500 mb-4">GAME OVER</h2>
200
+ <p class="text-xl mb-2">Final Score: <span id="final-score" class="font-bold">0</span></p>
201
+ <p class="text-lg mb-6">Lines Cleared: <span id="final-lines" class="font-bold">0</span></p>
202
+ <button id="restart-btn" class="bg-gradient-to-r from-purple-500 to-cyan-500 py-2 px-6 rounded-lg font-bold hover:opacity-90 transition">
203
+ PLAY AGAIN
204
+ </button>
205
+ </div>
206
+ </div>
207
+
208
+ <div id="pause-screen" class="fixed inset-0 bg-black bg-opacity-80 flex items-center justify-center hidden z-50">
209
+ <div class="bg-gray-800 p-8 rounded-lg max-w-md w-full text-center">
210
+ <h2 class="text-3xl font-bold text-yellow-500 mb-6">GAME PAUSED</h2>
211
+ <button id="resume-btn" class="bg-gradient-to-r from-purple-500 to-cyan-500 py-2 px-6 rounded-lg font-bold hover:opacity-90 transition">
212
+ RESUME
213
+ </button>
214
+ </div>
215
+ </div>
216
+
217
+ <script>
218
+ document.addEventListener('DOMContentLoaded', () => {
219
+ // Sound effects
220
+ const sounds = {
221
+ rotate: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-game-click-1114.mp3'),
222
+ move: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-jump-coin-216.mp3'),
223
+ drop: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-quick-jump-arcade-game-239.mp3'),
224
+ clear: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-unlock-game-notification-253.mp3'),
225
+ gameover: new Audio('https://assets.mixkit.co/sfx/preview/mixkit-retro-arcade-lose-2027.mp3')
226
+ };
227
+
228
+ // Game constants
229
+ const COLS = 10;
230
+ const ROWS = 20;
231
+ const BLOCK_SIZE = 30;
232
+ const NEXT_PIECE_SIZE = 4;
233
+
234
+ // Game variables
235
+ let board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
236
+ let currentPiece = null;
237
+ let nextPiece = null;
238
+ let holdPiece = null;
239
+ let canHold = true;
240
+ let currentPosition = { x: 0, y: 0 };
241
+ let score = 0;
242
+ let level = 1;
243
+ let lines = 0;
244
+ let gameInterval = null;
245
+ let gameSpeed = 1000;
246
+ let isGameRunning = false;
247
+ let isPaused = false;
248
+ let lastDropTime = 0;
249
+
250
+ // DOM elements
251
+ const gameBoard = document.getElementById('game-board');
252
+ const nextPieceDisplay = document.getElementById('next-piece');
253
+ const holdPieceDisplay = document.getElementById('hold-piece');
254
+ const scoreDisplay = document.getElementById('score');
255
+ const levelDisplay = document.getElementById('level');
256
+ const linesDisplay = document.getElementById('lines');
257
+ const startBtn = document.getElementById('start-btn');
258
+ const pauseBtn = document.getElementById('pause-btn');
259
+ const gameOverScreen = document.getElementById('game-over');
260
+ const finalScoreDisplay = document.getElementById('final-score');
261
+ const finalLinesDisplay = document.getElementById('final-lines');
262
+ const restartBtn = document.getElementById('restart-btn');
263
+ const pauseScreen = document.getElementById('pause-screen');
264
+ const resumeBtn = document.getElementById('resume-btn');
265
+
266
+ // Tetromino shapes
267
+ const SHAPES = {
268
+ I: [
269
+ [0, 0, 0, 0],
270
+ [1, 1, 1, 1],
271
+ [0, 0, 0, 0],
272
+ [0, 0, 0, 0]
273
+ ],
274
+ J: [
275
+ [1, 0, 0],
276
+ [1, 1, 1],
277
+ [0, 0, 0]
278
+ ],
279
+ L: [
280
+ [0, 0, 1],
281
+ [1, 1, 1],
282
+ [0, 0, 0]
283
+ ],
284
+ O: [
285
+ [1, 1],
286
+ [1, 1]
287
+ ],
288
+ S: [
289
+ [0, 1, 1],
290
+ [1, 1, 0],
291
+ [0, 0, 0]
292
+ ],
293
+ T: [
294
+ [0, 1, 0],
295
+ [1, 1, 1],
296
+ [0, 0, 0]
297
+ ],
298
+ Z: [
299
+ [1, 1, 0],
300
+ [0, 1, 1],
301
+ [0, 0, 0]
302
+ ]
303
+ };
304
+
305
+ const COLORS = {
306
+ I: 'piece-I',
307
+ J: 'piece-J',
308
+ L: 'piece-L',
309
+ O: 'piece-O',
310
+ S: 'piece-S',
311
+ T: 'piece-T',
312
+ Z: 'piece-Z'
313
+ };
314
+
315
+ // Initialize game board
316
+ function initBoard() {
317
+ gameBoard.innerHTML = '';
318
+ gameBoard.style.width = `${COLS * BLOCK_SIZE}px`;
319
+ gameBoard.style.height = `${ROWS * BLOCK_SIZE}px`;
320
+
321
+ for (let y = 0; y < ROWS; y++) {
322
+ for (let x = 0; x < COLS; x++) {
323
+ const cell = document.createElement('div');
324
+ cell.className = 'cell';
325
+ cell.id = `cell-${y}-${x}`;
326
+ gameBoard.appendChild(cell);
327
+ }
328
+ }
329
+ }
330
+
331
+ // Initialize piece displays
332
+ function initPieceDisplays() {
333
+ // Next piece display
334
+ nextPieceDisplay.innerHTML = '';
335
+ nextPieceDisplay.style.width = `${NEXT_PIECE_SIZE * BLOCK_SIZE}px`;
336
+ nextPieceDisplay.style.height = `${NEXT_PIECE_SIZE * BLOCK_SIZE}px`;
337
+
338
+ for (let y = 0; y < NEXT_PIECE_SIZE; y++) {
339
+ for (let x = 0; x < NEXT_PIECE_SIZE; x++) {
340
+ const cell = document.createElement('div');
341
+ cell.className = 'cell bg-gray-700';
342
+ cell.id = `next-cell-${y}-${x}`;
343
+ nextPieceDisplay.appendChild(cell);
344
+ }
345
+ }
346
+
347
+ // Hold piece display
348
+ holdPieceDisplay.innerHTML = '';
349
+ holdPieceDisplay.style.width = `${NEXT_PIECE_SIZE * BLOCK_SIZE}px`;
350
+ holdPieceDisplay.style.height = `${NEXT_PIECE_SIZE * BLOCK_SIZE}px`;
351
+
352
+ for (let y = 0; y < NEXT_PIECE_SIZE; y++) {
353
+ for (let x = 0; x < NEXT_PIECE_SIZE; x++) {
354
+ const cell = document.createElement('div');
355
+ cell.className = 'cell bg-gray-700';
356
+ cell.id = `hold-cell-${y}-${x}`;
357
+ holdPieceDisplay.appendChild(cell);
358
+ }
359
+ }
360
+ }
361
+
362
+ // Get random piece
363
+ function getRandomPiece() {
364
+ const pieces = Object.keys(SHAPES);
365
+ const randomPiece = pieces[Math.floor(Math.random() * pieces.length)];
366
+ return {
367
+ shape: SHAPES[randomPiece],
368
+ color: COLORS[randomPiece],
369
+ type: randomPiece
370
+ };
371
+ }
372
+
373
+ // Draw piece on board
374
+ function drawPiece(piece, position, isGhost = false) {
375
+ for (let y = 0; y < piece.shape.length; y++) {
376
+ for (let x = 0; x < piece.shape[y].length; x++) {
377
+ if (piece.shape[y][x]) {
378
+ const boardY = position.y + y;
379
+ const boardX = position.x + x;
380
+
381
+ if (boardY >= 0 && boardY < ROWS && boardX >= 0 && boardX < COLS) {
382
+ const cell = document.getElementById(`cell-${boardY}-${boardX}`);
383
+ if (cell) {
384
+ cell.className = `cell ${piece.color} ${isGhost ? 'ghost' : ''}`;
385
+ }
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // Draw next piece
393
+ function drawNextPiece() {
394
+ // Clear next piece display
395
+ const cells = nextPieceDisplay.querySelectorAll('.cell');
396
+ cells.forEach(cell => {
397
+ cell.className = 'cell bg-gray-700';
398
+ });
399
+
400
+ // Center the piece in the next piece display
401
+ const offsetX = Math.floor((NEXT_PIECE_SIZE - nextPiece.shape[0].length) / 2);
402
+ const offsetY = Math.floor((NEXT_PIECE_SIZE - nextPiece.shape.length) / 2);
403
+
404
+ for (let y = 0; y < nextPiece.shape.length; y++) {
405
+ for (let x = 0; x < nextPiece.shape[y].length; x++) {
406
+ if (nextPiece.shape[y][x]) {
407
+ const displayY = y + offsetY;
408
+ const displayX = x + offsetX;
409
+ const cell = document.getElementById(`next-cell-${displayY}-${displayX}`);
410
+ if (cell) {
411
+ cell.className = `cell ${nextPiece.color}`;
412
+ }
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ // Clear board (except locked pieces)
419
+ function clearBoard() {
420
+ for (let y = 0; y < ROWS; y++) {
421
+ for (let x = 0; x < COLS; x++) {
422
+ if (board[y][x] === 0) {
423
+ const cell = document.getElementById(`cell-${y}-${x}`);
424
+ if (cell) {
425
+ cell.className = 'cell';
426
+ }
427
+ }
428
+ }
429
+ }
430
+ }
431
+
432
+ // Draw locked pieces
433
+ function drawLockedPieces() {
434
+ for (let y = 0; y < ROWS; y++) {
435
+ for (let x = 0; x < COLS; x++) {
436
+ if (board[y][x] !== 0) {
437
+ const cell = document.getElementById(`cell-${y}-${x}`);
438
+ if (cell) {
439
+ cell.className = `cell ${board[y][x]}`;
440
+ }
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ // Check collision
447
+ function checkCollision(piece, position) {
448
+ for (let y = 0; y < piece.shape.length; y++) {
449
+ for (let x = 0; x < piece.shape[y].length; x++) {
450
+ if (piece.shape[y][x]) {
451
+ const boardY = position.y + y;
452
+ const boardX = position.x + x;
453
+
454
+ // Check boundaries
455
+ if (boardX < 0 || boardX >= COLS || boardY >= ROWS) {
456
+ return true;
457
+ }
458
+
459
+ // Check if already occupied (and not above the board)
460
+ if (boardY >= 0 && board[boardY][boardX] !== 0) {
461
+ return true;
462
+ }
463
+ }
464
+ }
465
+ }
466
+ return false;
467
+ }
468
+
469
+ // Rotate piece
470
+ function rotatePiece() {
471
+ if (!currentPiece) return;
472
+
473
+ const rotated = [];
474
+ for (let x = 0; x < currentPiece.shape[0].length; x++) {
475
+ const newRow = [];
476
+ for (let y = currentPiece.shape.length - 1; y >= 0; y--) {
477
+ newRow.push(currentPiece.shape[y][x]);
478
+ }
479
+ rotated.push(newRow);
480
+ }
481
+
482
+ const originalShape = currentPiece.shape;
483
+ currentPiece.shape = rotated;
484
+
485
+ // Check if rotation causes collision
486
+ if (checkCollision(currentPiece, currentPosition)) {
487
+ // Try wall kicks
488
+ const originalX = currentPosition.x;
489
+
490
+ // Try moving left
491
+ currentPosition.x--;
492
+ if (checkCollision(currentPiece, currentPosition)) {
493
+ // Try moving right
494
+ currentPosition.x = originalX + 1;
495
+ if (checkCollision(currentPiece, currentPosition)) {
496
+ // Try moving left twice
497
+ currentPosition.x = originalX - 2;
498
+ if (checkCollision(currentPiece, currentPosition)) {
499
+ // Try moving right twice
500
+ currentPosition.x = originalX + 2;
501
+ if (checkCollision(currentPiece, currentPosition)) {
502
+ // Revert if all wall kicks fail
503
+ currentPosition.x = originalX;
504
+ currentPiece.shape = originalShape;
505
+ }
506
+ }
507
+ }
508
+ }
509
+ }
510
+
511
+ updateDisplay();
512
+ }
513
+
514
+ // Move piece
515
+ function movePiece(direction) {
516
+ if (!currentPiece || isPaused) return;
517
+
518
+ const newPosition = { ...currentPosition };
519
+
520
+ switch (direction) {
521
+ case 'left':
522
+ newPosition.x--;
523
+ break;
524
+ case 'right':
525
+ newPosition.x++;
526
+ break;
527
+ case 'down':
528
+ newPosition.y++;
529
+ break;
530
+ }
531
+
532
+ if (!checkCollision(currentPiece, newPosition)) {
533
+ currentPosition = newPosition;
534
+ updateDisplay();
535
+ return true;
536
+ }
537
+
538
+ // If moving down and collision, lock the piece
539
+ if (direction === 'down') {
540
+ lockPiece();
541
+ return false;
542
+ }
543
+
544
+ return false;
545
+ }
546
+
547
+ // Hard drop
548
+ function hardDrop() {
549
+ if (!currentPiece || isPaused) return;
550
+
551
+ let dropDistance = 0;
552
+ while (!checkCollision(currentPiece, { ...currentPosition, y: currentPosition.y + dropDistance + 1 })) {
553
+ dropDistance++;
554
+ }
555
+
556
+ if (dropDistance > 0) {
557
+ currentPosition.y += dropDistance;
558
+ updateDisplay();
559
+ }
560
+
561
+ lockPiece();
562
+ }
563
+
564
+ // Lock piece
565
+ function lockPiece() {
566
+ for (let y = 0; y < currentPiece.shape.length; y++) {
567
+ for (let x = 0; x < currentPiece.shape[y].length; x++) {
568
+ if (currentPiece.shape[y][x]) {
569
+ const boardY = currentPosition.y + y;
570
+ const boardX = currentPosition.x + x;
571
+
572
+ // If piece is locked above the board, game over
573
+ if (boardY < 0) {
574
+ gameOver();
575
+ return;
576
+ }
577
+
578
+ board[boardY][boardX] = currentPiece.color;
579
+ }
580
+ }
581
+ }
582
+
583
+ // Check for completed lines
584
+ checkLines();
585
+
586
+ // Get next piece
587
+ spawnPiece();
588
+ }
589
+
590
+ // Create particle effects
591
+ function createParticles(x, y, color, count = 10) {
592
+ for (let i = 0; i < count; i++) {
593
+ const particle = document.createElement('div');
594
+ particle.className = 'particle';
595
+ particle.style.backgroundColor = color;
596
+ particle.style.left = `${x}px`;
597
+ particle.style.top = `${y}px`;
598
+
599
+ const angle = Math.random() * Math.PI * 2;
600
+ const velocity = 2 + Math.random() * 3;
601
+ const lifetime = 500 + Math.random() * 500;
602
+
603
+ document.body.appendChild(particle);
604
+
605
+ const startTime = Date.now();
606
+
607
+ function update() {
608
+ const elapsed = Date.now() - startTime;
609
+ const progress = elapsed / lifetime;
610
+
611
+ if (progress >= 1) {
612
+ particle.remove();
613
+ return;
614
+ }
615
+
616
+ particle.style.opacity = 1 - progress;
617
+ particle.style.transform = `translate(${Math.cos(angle) * velocity * elapsed / 20}px, ${Math.sin(angle) * velocity * elapsed / 20}px)`;
618
+
619
+ requestAnimationFrame(update);
620
+ }
621
+
622
+ update();
623
+ }
624
+ }
625
+
626
+ // Check for completed lines
627
+ function checkLines() {
628
+ let linesCleared = 0;
629
+ let completedLines = [];
630
+
631
+ for (let y = ROWS - 1; y >= 0; y--) {
632
+ if (board[y].every(cell => cell !== 0)) {
633
+ completedLines.push(y);
634
+ }
635
+ }
636
+
637
+ if (completedLines.length > 0) {
638
+ // Visual feedback for line clears
639
+ completedLines.forEach(y => {
640
+ for (let x = 0; x < COLS; x++) {
641
+ const cell = document.getElementById(`cell-${y}-${x}`);
642
+ if (cell) cell.classList.add('line-clear');
643
+ }
644
+ });
645
+
646
+ // Wait for animation to complete
647
+ setTimeout(() => {
648
+ completedLines.sort((a, b) => a - b);
649
+
650
+ completedLines.forEach(y => {
651
+ // Remove the line
652
+ board.splice(y, 1);
653
+ // Add new empty line at top
654
+ board.unshift(Array(COLS).fill(0));
655
+ linesCleared++;
656
+ });
657
+
658
+ // Update score with combo bonus and back-to-back
659
+ const basePoints = [0, 100, 300, 500, 800];
660
+ const comboBonus = (completedLines.length - 1) * 100;
661
+ const backToBackBonus = completedLines.length >= 4 ? 1200 : 0;
662
+ score += (basePoints[completedLines.length] + comboBonus + backToBackBonus) * level;
663
+
664
+ // Play clear sound
665
+ sounds.clear.currentTime = 0;
666
+ sounds.clear.play();
667
+
668
+ // Create particles
669
+ completedLines.forEach(y => {
670
+ for (let x = 0; x < COLS; x++) {
671
+ const cell = document.getElementById(`cell-${y}-${x}`);
672
+ if (cell) {
673
+ const rect = cell.getBoundingClientRect();
674
+ createParticles(
675
+ rect.left + rect.width/2,
676
+ rect.top + rect.height/2,
677
+ getComputedStyle(cell).backgroundColor,
678
+ 5
679
+ );
680
+ }
681
+ }
682
+ });
683
+ lines += completedLines.length;
684
+
685
+ // Update level (every 10 lines)
686
+ const newLevel = Math.floor(lines / 10) + 1;
687
+ if (newLevel > level) {
688
+ level = newLevel;
689
+ // Increase game speed (capped at 100ms)
690
+ gameSpeed = Math.max(100, 1000 - (level - 1) * 75);
691
+ clearInterval(gameInterval);
692
+ gameInterval = setInterval(gameTick, gameSpeed);
693
+ }
694
+
695
+ updateStats();
696
+ updateDisplay();
697
+ }, 300);
698
+ }
699
+ }
700
+
701
+ // Hold current piece
702
+ function holdCurrentPiece() {
703
+ if (!canHold) return;
704
+
705
+ if (holdPiece) {
706
+ // Swap current piece with hold piece
707
+ const temp = currentPiece;
708
+ currentPiece = holdPiece;
709
+ holdPiece = temp;
710
+ } else {
711
+ // First hold - just store current piece
712
+ holdPiece = currentPiece;
713
+ currentPiece = nextPiece;
714
+ nextPiece = getRandomPiece();
715
+ }
716
+
717
+ // Reset position
718
+ currentPosition = {
719
+ x: Math.floor((COLS - currentPiece.shape[0].length) / 2),
720
+ y: -2
721
+ };
722
+
723
+ // Update displays
724
+ drawHoldPiece();
725
+ drawNextPiece();
726
+ updateDisplay();
727
+
728
+ canHold = false;
729
+ }
730
+
731
+ // Draw hold piece
732
+ function drawHoldPiece() {
733
+ // Clear hold piece display
734
+ const cells = holdPieceDisplay.querySelectorAll('.cell');
735
+ cells.forEach(cell => {
736
+ cell.className = 'cell bg-gray-700';
737
+ });
738
+
739
+ if (!holdPiece) return;
740
+
741
+ // Center the piece in the hold display
742
+ const offsetX = Math.floor((NEXT_PIECE_SIZE - holdPiece.shape[0].length) / 2);
743
+ const offsetY = Math.floor((NEXT_PIECE_SIZE - holdPiece.shape.length) / 2);
744
+
745
+ for (let y = 0; y < holdPiece.shape.length; y++) {
746
+ for (let x = 0; x < holdPiece.shape[y].length; x++) {
747
+ if (holdPiece.shape[y][x]) {
748
+ const displayY = y + offsetY;
749
+ const displayX = x + offsetX;
750
+ const cell = document.getElementById(`hold-cell-${displayY}-${displayX}`);
751
+ if (cell) {
752
+ cell.className = `cell ${holdPiece.color}`;
753
+ }
754
+ }
755
+ }
756
+ }
757
+ }
758
+
759
+ // Spawn new piece
760
+ function spawnPiece() {
761
+ currentPiece = nextPiece || getRandomPiece();
762
+ nextPiece = getRandomPiece();
763
+
764
+ // Set starting position (centered at top)
765
+ currentPosition = {
766
+ x: Math.floor((COLS - currentPiece.shape[0].length) / 2),
767
+ y: -2 // Start slightly above the board
768
+ };
769
+
770
+ // If collision immediately, game over
771
+ if (checkCollision(currentPiece, currentPosition)) {
772
+ gameOver();
773
+ return;
774
+ }
775
+
776
+ canHold = true;
777
+ drawHoldPiece();
778
+ drawNextPiece();
779
+ updateDisplay();
780
+ }
781
+
782
+ // Update display
783
+ function updateDisplay() {
784
+ clearBoard();
785
+ drawLockedPieces();
786
+
787
+ // Draw ghost piece (shows where piece will land)
788
+ if (currentPiece) {
789
+ const ghostPosition = { ...currentPosition };
790
+ while (!checkCollision(currentPiece, { ...ghostPosition, y: ghostPosition.y + 1 })) {
791
+ ghostPosition.y++;
792
+ }
793
+ drawPiece(currentPiece, ghostPosition, true);
794
+ }
795
+
796
+ // Draw current piece
797
+ if (currentPiece) {
798
+ drawPiece(currentPiece, currentPosition);
799
+ }
800
+ }
801
+
802
+ // Update stats display
803
+ function updateStats() {
804
+ scoreDisplay.textContent = score;
805
+ levelDisplay.textContent = level;
806
+ linesDisplay.textContent = lines;
807
+ }
808
+
809
+ // Game tick
810
+ function gameTick() {
811
+ if (!isPaused) {
812
+ movePiece('down');
813
+ }
814
+ }
815
+
816
+ // Start game
817
+ function startGame() {
818
+ // Reset game state
819
+ board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
820
+ score = 0;
821
+ level = 1;
822
+ lines = 0;
823
+ gameSpeed = 1000;
824
+ isGameRunning = true;
825
+ isPaused = false;
826
+
827
+ updateStats();
828
+ initBoard();
829
+ initPieceDisplays();
830
+
831
+ // Get first pieces
832
+ nextPiece = getRandomPiece();
833
+ spawnPiece();
834
+
835
+ // Start game loop
836
+ clearInterval(gameInterval);
837
+ gameInterval = setInterval(gameTick, gameSpeed);
838
+
839
+ // Update UI
840
+ startBtn.classList.add('hidden');
841
+ pauseBtn.classList.remove('hidden');
842
+ gameOverScreen.classList.add('hidden');
843
+ pauseScreen.classList.add('hidden');
844
+ }
845
+
846
+ // Pause game
847
+ function pauseGame() {
848
+ isPaused = !isPaused;
849
+
850
+ if (isPaused) {
851
+ pauseBtn.textContent = 'RESUME';
852
+ pauseScreen.classList.remove('hidden');
853
+ } else {
854
+ pauseBtn.textContent = 'PAUSE';
855
+ pauseScreen.classList.add('hidden');
856
+ }
857
+ }
858
+
859
+ // Game over
860
+ function gameOver() {
861
+ isGameRunning = false;
862
+ clearInterval(gameInterval);
863
+
864
+ // Flash the board
865
+ const cells = gameBoard.querySelectorAll('.cell');
866
+ cells.forEach(cell => {
867
+ cell.classList.add('flash');
868
+ });
869
+
870
+ // Show game over screen
871
+ setTimeout(() => {
872
+ cells.forEach(cell => {
873
+ cell.classList.remove('flash');
874
+ });
875
+
876
+ finalScoreDisplay.textContent = score;
877
+ finalLinesDisplay.textContent = lines;
878
+ gameOverScreen.classList.remove('hidden');
879
+ pauseBtn.classList.add('hidden');
880
+ startBtn.classList.remove('hidden');
881
+ }, 1000);
882
+ }
883
+
884
+ // Event listeners
885
+ startBtn.addEventListener('click', startGame);
886
+ restartBtn.addEventListener('click', startGame);
887
+ pauseBtn.addEventListener('click', pauseGame);
888
+ resumeBtn.addEventListener('click', pauseGame);
889
+
890
+ // Keyboard controls
891
+ document.addEventListener('keydown', (e) => {
892
+ if (!isGameRunning) return;
893
+
894
+ switch (e.key) {
895
+ case 'ArrowLeft':
896
+ movePiece('left');
897
+ break;
898
+ case 'ArrowRight':
899
+ movePiece('right');
900
+ break;
901
+ case 'ArrowDown':
902
+ movePiece('down');
903
+ break;
904
+ case 'ArrowUp':
905
+ rotatePiece();
906
+ break;
907
+ case ' ':
908
+ hardDrop();
909
+ break;
910
+ case 'p':
911
+ case 'P':
912
+ pauseGame();
913
+ break;
914
+ case 'c':
915
+ case 'C':
916
+ case 'Shift':
917
+ holdCurrentPiece();
918
+ break;
919
+ }
920
+ });
921
+
922
+ // Touch controls for mobile
923
+ let touchStartX = 0;
924
+ let touchStartY = 0;
925
+
926
+ gameBoard.addEventListener('touchstart', (e) => {
927
+ if (!isGameRunning) return;
928
+
929
+ touchStartX = e.touches[0].clientX;
930
+ touchStartY = e.touches[0].clientY;
931
+ e.preventDefault();
932
+ }, { passive: false });
933
+
934
+ gameBoard.addEventListener('touchmove', (e) => {
935
+ if (!isGameRunning) return;
936
+
937
+ const touchX = e.touches[0].clientX;
938
+ const touchY = e.touches[0].clientY;
939
+ const diffX = touchX - touchStartX;
940
+ const diffY = touchY - touchStartY;
941
+
942
+ // Horizontal swipe
943
+ if (Math.abs(diffX) > Math.abs(diffY)) {
944
+ if (diffX > 30) {
945
+ movePiece('right');
946
+ touchStartX = touchX;
947
+ } else if (diffX < -30) {
948
+ movePiece('left');
949
+ touchStartX = touchX;
950
+ }
951
+ }
952
+ // Vertical swipe down
953
+ else if (diffY > 30) {
954
+ movePiece('down');
955
+ touchStartY = touchY;
956
+ }
957
+
958
+ e.preventDefault();
959
+ }, { passive: false });
960
+
961
+ gameBoard.addEventListener('touchend', (e) => {
962
+ if (!isGameRunning) return;
963
+
964
+ const touchEndX = e.changedTouches[0].clientX;
965
+ const touchEndY = e.changedTouches[0].clientY;
966
+ const diffX = touchEndX - touchStartX;
967
+ const diffY = touchEndY - touchStartY;
968
+
969
+ // Tap (small movement)
970
+ if (Math.abs(diffX) < 10 && Math.abs(diffY) < 10) {
971
+ rotatePiece();
972
+ }
973
+ // Quick swipe up
974
+ else if (diffY < -30) {
975
+ hardDrop();
976
+ }
977
+
978
+ e.preventDefault();
979
+ }, { passive: false });
980
+
981
+ // Initialize displays
982
+ initBoard();
983
+ initNextPieceDisplay();
984
+ });
985
+ </script>
986
+ <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=MitchiMitch/retrotetris" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
987
+ </html>