hiepnh commited on
Commit
7ccaf4a
·
verified ·
1 Parent(s): 232a257

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +688 -19
index.html CHANGED
@@ -1,19 +1,688 @@
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, maximum-scale=1.0, user-scalable=no">
6
+ <title>Neon Tetris | Ultimate Edition</title>
7
+ <style>
8
+ /* =========================================
9
+ 1. CSS VARIABLES & RESET
10
+ ========================================= */
11
+ :root {
12
+ --bg-color: #0f172a;
13
+ --surface-color: #1e293b;
14
+ --primary-color: #38bdf8;
15
+ --accent-color: #f472b6;
16
+ --text-color: #f1f5f9;
17
+ --grid-line: rgba(255, 255, 255, 0.05);
18
+ --glass-bg: rgba(30, 41, 59, 0.7);
19
+ --glass-border: rgba(255, 255, 255, 0.1);
20
+
21
+ /* Tetromino Colors */
22
+ --color-i: #00f0f0;
23
+ --color-j: #0000f0;
24
+ --color-l: #f0a000;
25
+ --color-o: #f0f000;
26
+ --color-s: #00f000;
27
+ --color-t: #a000f0;
28
+ --color-z: #f00000;
29
+ }
30
+
31
+ * {
32
+ box-sizing: border-box;
33
+ margin: 0;
34
+ padding: 0;
35
+ user-select: none;
36
+ -webkit-tap-highlight-color: transparent;
37
+ }
38
+
39
+ body {
40
+ font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
41
+ background-color: var(--bg-color);
42
+ background-image:
43
+ radial-gradient(circle at 10% 20%, rgba(56, 189, 248, 0.1) 0%, transparent 20%),
44
+ radial-gradient(circle at 90% 80%, rgba(244, 114, 182, 0.1) 0%, transparent 20%);
45
+ color: var(--text-color);
46
+ height: 100vh;
47
+ display: flex;
48
+ flex-direction: column;
49
+ align-items: center;
50
+ justify-content: center;
51
+ overflow: hidden;
52
+ }
53
+
54
+ /* =========================================
55
+ 2. LAYOUT & CONTAINERS
56
+ ========================================= */
57
+ .game-wrapper {
58
+ display: flex;
59
+ gap: 2rem;
60
+ padding: 2rem;
61
+ background: var(--glass-bg);
62
+ backdrop-filter: blur(12px);
63
+ -webkit-backdrop-filter: blur(12px);
64
+ border: 1px solid var(--glass-border);
65
+ border-radius: 24px;
66
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
67
+ position: relative;
68
+ max-width: 95vw;
69
+ }
70
+
71
+ /* Main Game Canvas */
72
+ .canvas-container {
73
+ position: relative;
74
+ border: 2px solid var(--surface-color);
75
+ border-radius: 8px;
76
+ box-shadow: inset 0 0 20px rgba(0,0,0,0.5);
77
+ background-color: #000;
78
+ }
79
+
80
+ canvas {
81
+ display: block;
82
+ border-radius: 6px;
83
+ }
84
+
85
+ /* Sidebar UI */
86
+ .sidebar {
87
+ display: flex;
88
+ flex-direction: column;
89
+ gap: 1.5rem;
90
+ min-width: 200px;
91
+ }
92
+
93
+ .panel {
94
+ background: rgba(15, 23, 42, 0.6);
95
+ padding: 1rem;
96
+ border-radius: 12px;
97
+ border: 1px solid var(--glass-border);
98
+ }
99
+
100
+ .panel-title {
101
+ font-size: 0.85rem;
102
+ text-transform: uppercase;
103
+ letter-spacing: 2px;
104
+ color: #94a3b8;
105
+ margin-bottom: 0.5rem;
106
+ }
107
+
108
+ .stat-value {
109
+ font-size: 1.8rem;
110
+ font-weight: 700;
111
+ color: var(--primary-color);
112
+ text-shadow: 0 0 10px rgba(56, 189, 248, 0.3);
113
+ }
114
+
115
+ /* Next Piece Preview */
116
+ #next-canvas {
117
+ background: transparent;
118
+ margin: 0 auto;
119
+ border-radius: 4px;
120
+ }
121
+
122
+ /* Controls Guide */
123
+ .controls {
124
+ font-size: 0.8rem;
125
+ color: #64748b;
126
+ line-height: 1.6;
127
+ }
128
+
129
+ .key {
130
+ display: inline-block;
131
+ background: var(--surface-color);
132
+ padding: 2px 6px;
133
+ border-radius: 4px;
134
+ color: var(--text-color);
135
+ font-family: monospace;
136
+ border: 1px solid var(--glass-border);
137
+ }
138
+
139
+ /* Buttons */
140
+ .btn {
141
+ width: 100%;
142
+ padding: 12px;
143
+ border: none;
144
+ border-radius: 8px;
145
+ font-weight: 600;
146
+ cursor: pointer;
147
+ transition: all 0.2s ease;
148
+ text-transform: uppercase;
149
+ font-size: 0.9rem;
150
+ }
151
+
152
+ .btn-primary {
153
+ background: linear-gradient(135deg, var(--primary-color), #2563eb);
154
+ color: white;
155
+ box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
156
+ }
157
+
158
+ .btn-primary:hover {
159
+ transform: translateY(-2px);
160
+ box-shadow: 0 6px 16px rgba(37, 99, 235, 0.4);
161
+ }
162
+
163
+ .btn-primary:active {
164
+ transform: translateY(0);
165
+ }
166
+
167
+ /* Overlay (Start/Game Over) */
168
+ .overlay {
169
+ position: absolute;
170
+ top: 0;
171
+ left: 0;
172
+ width: 100%;
173
+ height: 100%;
174
+ background: rgba(15, 23, 42, 0.85);
175
+ backdrop-filter: blur(8px);
176
+ display: flex;
177
+ flex-direction: column;
178
+ align-items: center;
179
+ justify-content: center;
180
+ border-radius: 24px;
181
+ z-index: 10;
182
+ transition: opacity 0.3s ease;
183
+ }
184
+
185
+ .overlay.hidden {
186
+ opacity: 0;
187
+ pointer-events: none;
188
+ }
189
+
190
+ .overlay h1 {
191
+ font-size: 3rem;
192
+ margin-bottom: 0.5rem;
193
+ background: linear-gradient(to right, var(--primary-color), var(--accent-color));
194
+ -webkit-background-clip: text;
195
+ -webkit-text-fill-color: transparent;
196
+ text-shadow: 0 0 30px rgba(56, 189, 248, 0.2);
197
+ }
198
+
199
+ .overlay p {
200
+ font-size: 1.2rem;
201
+ color: #cbd5e1;
202
+ margin-bottom: 2rem;
203
+ }
204
+
205
+ /* =========================================
206
+ 3. RESPONSIVE DESIGN
207
+ ========================================= */
208
+ @media (max-width: 768px) {
209
+ .game-wrapper {
210
+ flex-direction: column;
211
+ padding: 1rem;
212
+ gap: 1rem;
213
+ }
214
+
215
+ .sidebar {
216
+ flex-direction: row;
217
+ flex-wrap: wrap;
218
+ justify-content: center;
219
+ min-width: auto;
220
+ }
221
+
222
+ .panel {
223
+ padding: 0.5rem 1rem;
224
+ flex: 1;
225
+ }
226
+
227
+ .controls {
228
+ display: none; /* Hide complex controls on mobile to save space */
229
+ }
230
+
231
+ canvas {
232
+ max-height: 60vh;
233
+ width: auto;
234
+ }
235
+ }
236
+
237
+ /* Footer Credit */
238
+ footer {
239
+ margin-top: 1rem;
240
+ font-size: 0.75rem;
241
+ color: #475569;
242
+ }
243
+
244
+ footer a {
245
+ color: var(--primary-color);
246
+ text-decoration: none;
247
+ }
248
+
249
+ </style>
250
+ </head>
251
+ <body>
252
+
253
+ <div class="game-wrapper">
254
+ <!-- Game Canvas -->
255
+ <div class="canvas-container">
256
+ <canvas id="tetris" width="300" height="600"></canvas>
257
+
258
+ <!-- Start / Game Over Overlay -->
259
+ <div id="overlay" class="overlay">
260
+ <h1 id="overlay-title">NEON TETRIS</h1>
261
+ <p id="overlay-msg">Press Start to Play</p>
262
+ <button id="start-btn" class="btn btn-primary">Start Game</button>
263
+ </div>
264
+ </div>
265
+
266
+ <!-- Sidebar -->
267
+ <aside class="sidebar">
268
+ <!-- Score Panel -->
269
+ <div class="panel">
270
+ <div class="panel-title">Score</div>
271
+ <div id="score" class="stat-value">0</div>
272
+ </div>
273
+
274
+ <!-- Level Panel -->
275
+ <div class="panel">
276
+ <div class="panel-title">Level</div>
277
+ <div id="level" class="stat-value">1</div>
278
+ </div>
279
+
280
+ <!-- Lines Panel -->
281
+ <div class="panel">
282
+ <div class="panel-title">Lines</div>
283
+ <div id="lines" class="stat-value">0</div>
284
+ </div>
285
+
286
+ <!-- Next Piece -->
287
+ <div class="panel" style="text-align: center;">
288
+ <div class="panel-title">Next</div>
289
+ <canvas id="next-canvas" width="100" height="100"></canvas>
290
+ </div>
291
+
292
+ <!-- Controls -->
293
+ <div class="panel controls">
294
+ <div class="panel-title">Controls</div>
295
+ <p><span class="key">←</span> <span class="key">→</span> Move</p>
296
+ <p><span class="key">↑</span> Rotate</p>
297
+ <p><span class="key">↓</span> Soft Drop</p>
298
+ <p><span class="key">Space</span> Hard Drop</p>
299
+ <p><span class="key">P</span> Pause</p>
300
+ </div>
301
+
302
+ <button id="reset-btn" class="btn btn-primary" style="background: var(--accent-color);">Reset</button>
303
+ </aside>
304
+ </div>
305
+
306
+ <footer>
307
+ Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
308
+ </footer>
309
+
310
+ <script>
311
+ /**
312
+ * TETRIS GAME ENGINE
313
+ * Uses HTML5 Canvas for rendering.
314
+ * Implements standard Tetris mechanics with modern JS classes.
315
+ */
316
+
317
+ // --- Configuration ---
318
+ const COLS = 10;
319
+ const ROWS = 20;
320
+ const BLOCK_SIZE = 30; // Base size, scaled by canvas
321
+ const COLORS = [
322
+ null,
323
+ '#00f0f0', // I - Cyan
324
+ '#0000f0', // J - Blue
325
+ '#f0a000', // L - Orange
326
+ '#f0f000', // O - Yellow
327
+ '#00f000', // S - Green
328
+ '#a000f0', // T - Purple
329
+ '#f00000' // Z - Red
330
+ ];
331
+
332
+ // --- Shapes Definitions ---
333
+ const PIECES = [
334
+ [],
335
+ [[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]], // I
336
+ [[2, 0, 0], [2, 2, 2], [0, 0, 0]], // J
337
+ [[0, 0, 3], [3, 3, 3], [0, 0, 0]], // L
338
+ [[4, 4], [4, 4]], // O
339
+ [[0, 5, 5], [5, 5, 0], [0, 0, 0]], // S
340
+ [[0, 6, 0], [6, 6, 6], [0, 0, 0]], // T
341
+ [[7, 7, 0], [0, 7, 7], [0, 0, 0]] // Z
342
+ ];
343
+
344
+ // --- State Management ---
345
+ const canvas = document.getElementById('tetris');
346
+ const context = canvas.getContext('2d');
347
+ const nextCanvas = document.getElementById('next-canvas');
348
+ const nextContext = nextCanvas.getContext('2d');
349
+
350
+ // Scale canvas for high DPI
351
+ function scaleCanvas(c, ctx, w, h) {
352
+ const dpr = window.devicePixelRatio || 1;
353
+ c.width = w * dpr;
354
+ c.height = h * dpr;
355
+ c.style.width = `${w}px`;
356
+ c.style.height = `${h}px`;
357
+ ctx.scale(dpr, dpr);
358
+ }
359
+
360
+ scaleCanvas(canvas, context, COLS * BLOCK_SIZE, ROWS * BLOCK_SIZE);
361
+ scaleCanvas(nextCanvas, nextContext, 4 * BLOCK_SIZE, 4 * BLOCK_SIZE);
362
+
363
+ let arena = createMatrix(COLS, ROWS);
364
+
365
+ let player = {
366
+ pos: {x: 0, y: 0},
367
+ matrix: null,
368
+ score: 0,
369
+ lines: 0,
370
+ level: 1,
371
+ next: null
372
+ };
373
+
374
+ let dropCounter = 0;
375
+ let dropInterval = 1000;
376
+ let lastTime = 0;
377
+ let isPaused = false;
378
+ let isGameOver = false;
379
+ let requestId = null;
380
+
381
+ // --- Core Functions ---
382
+
383
+ function createMatrix(w, h) {
384
+ const matrix = [];
385
+ while (h--) {
386
+ matrix.push(new Array(w).fill(0));
387
+ }
388
+ return matrix;
389
+ }
390
+
391
+ function createPiece(type) {
392
+ // Returns a deep copy of the piece definition
393
+ return PIECES[type].map(row => [...row]);
394
+ }
395
+
396
+ function drawMatrix(matrix, offset, ctx = context) {
397
+ matrix.forEach((row, y) => {
398
+ row.forEach((value, x) => {
399
+ if (value !== 0) {
400
+ // Main Block
401
+ ctx.fillStyle = COLORS[value];
402
+ ctx.fillRect(
403
+ (x + offset.x) * BLOCK_SIZE,
404
+ (y + offset.y) * BLOCK_SIZE,
405
+ BLOCK_SIZE,
406
+ BLOCK_SIZE
407
+ );
408
+
409
+ // Inner Bevel/Highlight (Pseudo-3D)
410
+ ctx.lineWidth = 2;
411
+ ctx.strokeStyle = 'rgba(255,255,255,0.5)';
412
+ ctx.strokeRect(
413
+ (x + offset.x) * BLOCK_SIZE,
414
+ (y + offset.y) * BLOCK_SIZE,
415
+ BLOCK_SIZE,
416
+ BLOCK_SIZE
417
+ );
418
+
419
+ ctx.fillStyle = 'rgba(0,0,0,0.2)';
420
+ ctx.fillRect(
421
+ (x + offset.x) * BLOCK_SIZE + 5,
422
+ (y + offset.y) * BLOCK_SIZE + 5,
423
+ BLOCK_SIZE - 10,
424
+ BLOCK_SIZE - 10
425
+ );
426
+ }
427
+ });
428
+ });
429
+ }
430
+
431
+ function draw() {
432
+ // Fill background with semi-transparent black for trail effect? No, clean clear is better for Tetris.
433
+ context.fillStyle = '#000';
434
+ context.fillRect(0, 0, canvas.width, canvas.height);
435
+
436
+ drawMatrix(arena, {x: 0, y: 0});
437
+ drawMatrix(player.matrix, player.pos);
438
+ }
439
+
440
+ function drawNext() {
441
+ nextContext.fillStyle = '#1e293b'; // Match surface color
442
+ nextContext.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
443
+
444
+ // Center the piece
445
+ const offsetX = (4 - player.next[0].length) / 2;
446
+ const offsetY = (4 - player.next.length) / 2;
447
+
448
+ drawMatrix(player.next, {x: offsetX, y: offsetY}, nextContext);
449
+ }
450
+
451
+ function merge(arena, player) {
452
+ player.matrix.forEach((row, y) => {
453
+ row.forEach((value, x) => {
454
+ if (value !== 0) {
455
+ arena[y + player.pos.y][x + player.pos.x] = value;
456
+ }
457
+ });
458
+ });
459
+ }
460
+
461
+ function rotate(matrix, dir) {
462
+ for (let y = 0; y < matrix.length; ++y) {
463
+ for (let x = 0; x < y; ++x) {
464
+ [matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]];
465
+ }
466
+ }
467
+ if (dir > 0) {
468
+ matrix.forEach(row => row.reverse());
469
+ } else {
470
+ matrix.reverse();
471
+ }
472
+ }
473
+
474
+ function playerRotate(dir) {
475
+ const pos = player.pos.x;
476
+ let offset = 1;
477
+ rotate(player.matrix, dir);
478
+ // Wall kick logic (basic)
479
+ while (collide(arena, player)) {
480
+ player.pos.x += offset;
481
+ offset = -(offset + (offset > 0 ? 1 : -1));
482
+ if (offset > player.matrix[0].length) {
483
+ rotate(player.matrix, -dir); // Rotate back if fails
484
+ player.pos.x = pos;
485
+ return;
486
+ }
487
+ }
488
+ }
489
+
490
+ function collide(arena, player) {
491
+ const [m, o] = [player.matrix, player.pos];
492
+ for (let y = 0; y < m.length; ++y) {
493
+ for (let x = 0; x < m[y].length; ++x) {
494
+ if (m[y][x] !== 0 &&
495
+ (arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) {
496
+ return true;
497
+ }
498
+ }
499
+ }
500
+ return false;
501
+ }
502
+
503
+ function playerReset() {
504
+ if (player.next === null) {
505
+ player.next = createPiece((Math.random() * 7 | 0) + 1);
506
+ }
507
+ player.matrix = player.next;
508
+ player.next = createPiece((Math.random() * 7 | 0) + 1);
509
+ drawNext();
510
+
511
+ player.pos.y = 0;
512
+ player.pos.x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0);
513
+
514
+ if (collide(arena, player)) {
515
+ gameOver();
516
+ }
517
+ }
518
+
519
+ function arenaSweep() {
520
+ let rowCount = 0;
521
+ outer: for (let y = arena.length - 1; y > 0; --y) {
522
+ for (let x = 0; x < arena[y].length; ++x) {
523
+ if (arena[y][x] === 0) {
524
+ continue outer;
525
+ }
526
+ }
527
+
528
+ const row = arena.splice(y, 1)[0].fill(0);
529
+ arena.unshift(row);
530
+ ++y;
531
+ rowCount++;
532
+ }
533
+
534
+ if (rowCount > 0) {
535
+ // Scoring: 100, 300, 500, 800
536
+ const lineScores = [0, 100, 300, 500, 800];
537
+ player.score += lineScores[rowCount] * player.level;
538
+ player.lines += rowCount;
539
+ player.level = Math.floor(player.lines / 10) + 1;
540
+
541
+ // Speed up
542
+ dropInterval = Math.max(100, 1000 - (player.level - 1) * 100);
543
+
544
+ updateScore();
545
+ }
546
+ }
547
+
548
+ function playerDrop() {
549
+ player.pos.y++;
550
+ if (collide(arena, player)) {
551
+ player.pos.y--;
552
+ merge(arena, player);
553
+ playerReset();
554
+ arenaSweep();
555
+ updateScore();
556
+ }
557
+ dropCounter = 0;
558
+ }
559
+
560
+ function playerMove(dir) {
561
+ player.pos.x += dir;
562
+ if (collide(arena, player)) {
563
+ player.pos.x -= dir;
564
+ }
565
+ }
566
+
567
+ function playerHardDrop() {
568
+ while (!collide(arena, player)) {
569
+ player.pos.y++;
570
+ }
571
+ player.pos.y--; // Back up one step
572
+ merge(arena, player);
573
+ playerReset();
574
+ arenaSweep();
575
+ updateScore();
576
+ dropCounter = 0;
577
+ }
578
+
579
+ function updateScore() {
580
+ document.getElementById('score').innerText = player.score;
581
+ document.getElementById('level').innerText = player.level;
582
+ document.getElementById('lines').innerText = player.lines;
583
+ }
584
+
585
+ function gameOver() {
586
+ isGameOver = true;
587
+ cancelAnimationFrame(requestId);
588
+ const overlay = document.getElementById('overlay');
589
+ const title = document.getElementById('overlay-title');
590
+ const msg = document.getElementById('overlay-msg');
591
+ const btn = document.getElementById('start-btn');
592
+
593
+ title.innerText = "GAME OVER";
594
+ title.style.background = "linear-gradient(to right, #f472b6, #ef4444)";
595
+ title.style.webkitBackgroundClip = "text";
596
+ title.style.webkitTextFillColor = "transparent";
597
+
598
+ msg.innerText = `Final Score: ${player.score}`;
599
+ btn.innerText = "Try Again";
600
+
601
+ overlay.classList.remove('hidden');
602
+ }
603
+
604
+ function resetGame() {
605
+ arena.forEach(row => row.fill(0));
606
+ player.score = 0;
607
+ player.lines = 0;
608
+ player.level = 1;
609
+ player.next = null;
610
+ dropInterval = 1000;
611
+ updateScore();
612
+ playerReset();
613
+ isGameOver = false;
614
+ isPaused = false;
615
+ }
616
+
617
+ function update(time = 0) {
618
+ if (isPaused || isGameOver) return;
619
+
620
+ const deltaTime = time - lastTime;
621
+ lastTime = time;
622
+
623
+ dropCounter += deltaTime;
624
+ if (dropCounter > dropInterval) {
625
+ playerDrop();
626
+ }
627
+
628
+ draw();
629
+ requestId = requestAnimationFrame(update);
630
+ }
631
+
632
+ // --- Input Handling ---
633
+
634
+ document.addEventListener('keydown', event => {
635
+ if (isGameOver) return;
636
+
637
+ // Prevent default scrolling for game keys
638
+ if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"," "].indexOf(event.code) > -1) {
639
+ event.preventDefault();
640
+ }
641
+
642
+ if (event.code === 'KeyP') {
643
+ isPaused = !isPaused;
644
+ if (!isPaused) {
645
+ lastTime = performance.now(); // Reset time to prevent jump
646
+ update();
647
+ }
648
+ return;
649
+ }
650
+
651
+ if (isPaused) return;
652
+
653
+ if (event.code === 'ArrowLeft') {
654
+ playerMove(-1);
655
+ } else if (event.code === 'ArrowRight') {
656
+ playerMove(1);
657
+ } else if (event.code === 'ArrowDown') {
658
+ playerDrop();
659
+ } else if (event.code === 'ArrowUp') {
660
+ playerRotate(1);
661
+ } else if (event.code === 'Space') {
662
+ playerHardDrop();
663
+ }
664
+ });
665
+
666
+ document.getElementById('start-btn').addEventListener('click', () => {
667
+ document.getElementById('overlay').classList.add('hidden');
668
+ resetGame();
669
+ update();
670
+ });
671
+
672
+ document.getElementById('reset-btn').addEventListener('click', () => {
673
+ // If game is running, pause it
674
+ if (!isGameOver && !isPaused) {
675
+ isPaused = true;
676
+ }
677
+ resetGame();
678
+ document.getElementById('overlay').classList.add('hidden');
679
+ update();
680
+ });
681
+
682
+ // Initial Draw (Empty Board)
683
+ context.fillStyle = '#000';
684
+ context.fillRect(0, 0, canvas.width, canvas.height);
685
+
686
+ </script>
687
+ </body>
688
+ </html>