dlma commited on
Commit
936984b
·
verified ·
1 Parent(s): af10fac

add max disagn rnd anymation - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +610 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Subway
3
- emoji: 📚
4
  colorFrom: blue
5
- colorTo: pink
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: subway
3
+ emoji: 🐳
4
  colorFrom: blue
5
+ colorTo: yellow
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,610 @@
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>Metro Dash - Subway Surfers Inspired Game</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ overflow: hidden;
13
+ touch-action: none;
14
+ font-family: 'Arial', sans-serif;
15
+ }
16
+
17
+ #gameContainer {
18
+ position: relative;
19
+ width: 100vw;
20
+ height: 100vh;
21
+ background-color: #87CEEB;
22
+ overflow: hidden;
23
+ }
24
+
25
+ #player {
26
+ position: absolute;
27
+ width: 60px;
28
+ height: 100px;
29
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 150"><rect x="30" y="50" width="40" height="60" fill="%23FF5733"/><circle cx="50" cy="30" r="20" fill="%23FFC300"/><rect x="20" y="110" width="20" height="30" fill="%23333"/><rect x="60" y="110" width="20" height="30" fill="%23333"/></svg>');
30
+ background-size: contain;
31
+ z-index: 10;
32
+ transition: left 0.2s ease-out;
33
+ animation: bounce 0.5s infinite alternate;
34
+ }
35
+
36
+ .obstacle {
37
+ position: absolute;
38
+ width: 60px;
39
+ height: 60px;
40
+ background-color: #333;
41
+ border-radius: 5px;
42
+ animation: pulseObstacle 1.5s ease-in-out infinite;
43
+ }
44
+
45
+ .coin {
46
+ position: absolute;
47
+ width: 30px;
48
+ height: 30px;
49
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="%23FFD700" stroke="%23FFA500" stroke-width="5"/></svg>');
50
+ background-size: contain;
51
+ z-index: 5;
52
+ animation: rotateCoin 2s linear infinite;
53
+ }
54
+
55
+ .lane {
56
+ position: absolute;
57
+ width: 100%;
58
+ height: 33.33%;
59
+ border-top: 2px dashed white;
60
+ border-bottom: 2px dashed white;
61
+ }
62
+
63
+ #scoreContainer {
64
+ position: absolute;
65
+ top: 20px;
66
+ left: 20px;
67
+ z-index: 100;
68
+ color: white;
69
+ font-size: 24px;
70
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
71
+ }
72
+
73
+ #gameOverScreen {
74
+ position: absolute;
75
+ top: 0;
76
+ left: 0;
77
+ width: 100%;
78
+ height: 100%;
79
+ background-color: rgba(0, 0, 0, 0.7);
80
+ display: none;
81
+ flex-direction: column;
82
+ justify-content: center;
83
+ align-items: center;
84
+ z-index: 200;
85
+ color: white;
86
+ }
87
+
88
+ #startScreen {
89
+ position: absolute;
90
+ top: 0;
91
+ left: 0;
92
+ width: 100%;
93
+ height: 100%;
94
+ background-color: rgba(0, 0, 0, 0.7);
95
+ display: flex;
96
+ flex-direction: column;
97
+ justify-content: center;
98
+ align-items: center;
99
+ z-index: 200;
100
+ color: white;
101
+ }
102
+
103
+ .train {
104
+ position: absolute;
105
+ width: 120px;
106
+ height: 80px;
107
+ background-color: #c00;
108
+ border-radius: 10px;
109
+ }
110
+
111
+ @keyframes slide {
112
+ from { background-position: 0 0; }
113
+ to { background-position: -1000px 0; }
114
+ }
115
+
116
+ @keyframes bounce {
117
+ 0% { transform: translateY(0); }
118
+ 100% { transform: translateY(-5px); }
119
+ }
120
+
121
+ @keyframes rotateCoin {
122
+ 0% { transform: rotate(0deg) scale(1); }
123
+ 50% { transform: rotate(180deg) scale(1.1); }
124
+ 100% { transform: rotate(360deg) scale(1); }
125
+ }
126
+
127
+ @keyframes pulseObstacle {
128
+ 0% { opacity: 0.8; }
129
+ 50% { opacity: 1; }
130
+ 100% { opacity: 0.8; }
131
+ }
132
+
133
+ @keyframes fadeOut {
134
+ 0% { transform: scale(1); opacity: 0.8; }
135
+ 100% { transform: scale(2); opacity: 0; }
136
+ }
137
+
138
+ .ground {
139
+ position: absolute;
140
+ width: 100%;
141
+ height: 33.33%;
142
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect width="50" height="100" fill="%23555"/><rect x="50" width="50" height="100" fill="%23444"/></svg>');
143
+ background-size: 100px 100px;
144
+ animation: slide 1s linear infinite;
145
+ }
146
+ </style>
147
+ </head>
148
+ <body class="bg-gray-900">
149
+ <div id="gameContainer">
150
+ <div id="scoreContainer" class="flex items-center space-x-4">
151
+ <div class="flex items-center">
152
+ <span>🏆</span>
153
+ <span id="score">0</span>
154
+ </div>
155
+ <div class="flex items-center">
156
+ <span>💰</span>
157
+ <span id="coins">0</span>
158
+ </div>
159
+ </div>
160
+
161
+ <div id="startScreen">
162
+ <h1 class="text-5xl font-bold mb-6 text-yellow-300">METRO DASH</h1>
163
+ <p class="text-xl mb-8">Swipe to change lanes and avoid obstacles!</p>
164
+ <button id="startButton" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-8 rounded-full text-xl transition duration-300 transform hover:scale-105">
165
+ START RUN
166
+ </button>
167
+ <div class="mt-8 flex space-x-4">
168
+ <div class="text-center">
169
+ <div class="w-16 h-16 mx-auto bg-gray-800 rounded-lg flex items-center justify-center mb-2">
170
+ <span class="text-3xl">👆</span>
171
+ </div>
172
+ <p>Swipe Up</p>
173
+ </div>
174
+ <div class="text-center">
175
+ <div class="w-16 h-16 mx-auto bg-gray-800 rounded-lg flex items-center justify-center mb-2">
176
+ <span class="text-3xl">👇</span>
177
+ </div>
178
+ <p>Swipe Down</p>
179
+ </div>
180
+ <div class="text-center">
181
+ <div class="w-16 h-16 mx-auto bg-gray-800 rounded-lg flex items-center justify-center mb-2">
182
+ <span class="text-3xl">🔄</span>
183
+ </div>
184
+ <p>Jump/Roll</p>
185
+ </div>
186
+ </div>
187
+ </div>
188
+
189
+ <div id="gameOverScreen">
190
+ <h1 class="text-5xl font-bold mb-4 text-red-500">GAME OVER</h1>
191
+ <div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6 text-center">
192
+ <p class="text-2xl mb-2">Score: <span id="finalScore">0</span></p>
193
+ <p class="text-2xl">Coins: <span id="finalCoins">0</span></p>
194
+ </div>
195
+ <button id="restartButton" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-8 rounded-full text-xl transition duration-300 transform hover:scale-105">
196
+ PLAY AGAIN
197
+ </button>
198
+ </div>
199
+
200
+ <div class="ground" style="top: 0%;"></div>
201
+ <div class="ground" style="top: 33.33%;"></div>
202
+ <div class="ground" style="top: 66.66%;"></div>
203
+
204
+ <div id="player"></div>
205
+ </div>
206
+
207
+ <script>
208
+ // Game variables
209
+ let score = 0;
210
+ let coins = 0;
211
+ let gameSpeed = 5;
212
+ let gameRunning = false;
213
+ let playerLane = 1; // 0: top, 1: middle, 2: bottom
214
+ let obstacles = [];
215
+ let coinsArray = [];
216
+ let trains = [];
217
+ let animationId;
218
+ let obstacleSpawnRate = 100; // frames
219
+ let coinSpawnRate = 50; // frames
220
+ let trainSpawnRate = 200; // frames
221
+ let frameCount = 0;
222
+ let isJumping = false;
223
+ let isRolling = false;
224
+
225
+ // DOM elements
226
+ const gameContainer = document.getElementById('gameContainer');
227
+ const player = document.getElementById('player');
228
+ const scoreElement = document.getElementById('score');
229
+ const coinsElement = document.getElementById('coins');
230
+ const gameOverScreen = document.getElementById('gameOverScreen');
231
+ const startScreen = document.getElementById('startScreen');
232
+ const startButton = document.getElementById('startButton');
233
+ const restartButton = document.getElementById('restartButton');
234
+ const finalScoreElement = document.getElementById('finalScore');
235
+ const finalCoinsElement = document.getElementById('finalCoins');
236
+
237
+ // Game dimensions
238
+ const gameWidth = gameContainer.offsetWidth;
239
+ const gameHeight = gameContainer.offsetHeight;
240
+ const laneHeight = gameHeight / 3;
241
+
242
+ // Initialize player position
243
+ function initPlayer() {
244
+ player.style.width = '60px';
245
+ player.style.height = '100px';
246
+ player.style.left = '100px';
247
+ moveToLane(playerLane);
248
+ isJumping = false;
249
+ isRolling = false;
250
+ player.style.transform = 'none';
251
+ }
252
+
253
+ // Move player to specified lane (0, 1, or 2)
254
+ function moveToLane(lane) {
255
+ playerLane = lane;
256
+ player.style.top = `${lane * laneHeight + (laneHeight - 100) / 2}px`;
257
+ }
258
+
259
+ // Create a new obstacle
260
+ function createObstacle() {
261
+ const obstacle = document.createElement('div');
262
+ obstacle.className = 'obstacle';
263
+
264
+ // Random lane (0, 1, or 2)
265
+ const lane = Math.floor(Math.random() * 3);
266
+
267
+ // Position obstacle at the right edge of the screen
268
+ obstacle.style.left = `${gameWidth}px`;
269
+ obstacle.style.top = `${lane * laneHeight + (laneHeight - 60) / 2}px`;
270
+
271
+ // Random obstacle type (30% chance of being a train)
272
+ if (Math.random() < 0.3) {
273
+ obstacle.classList.add('train');
274
+ obstacle.style.width = '120px';
275
+ obstacle.style.height = '80px';
276
+ obstacle.style.top = `${lane * laneHeight + (laneHeight - 80) / 2}px`;
277
+ } else {
278
+ // Random color for regular obstacles
279
+ const colors = ['#333', '#555', '#777', '#8B4513', '#A0522D'];
280
+ obstacle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
281
+ }
282
+
283
+ gameContainer.appendChild(obstacle);
284
+ obstacles.push({
285
+ element: obstacle,
286
+ lane: lane,
287
+ isTrain: obstacle.classList.contains('train')
288
+ });
289
+ }
290
+
291
+ // Create a new coin
292
+ function createCoin() {
293
+ const coin = document.createElement('div');
294
+ coin.className = 'coin';
295
+
296
+ // Random lane (0, 1, or 2)
297
+ const lane = Math.floor(Math.random() * 3);
298
+
299
+ // Position coin at the right edge of the screen
300
+ coin.style.left = `${gameWidth}px`;
301
+ coin.style.top = `${lane * laneHeight + (laneHeight - 30) / 2}px`;
302
+
303
+ gameContainer.appendChild(coin);
304
+ coinsArray.push({
305
+ element: coin,
306
+ lane: lane
307
+ });
308
+ }
309
+
310
+ // Update obstacle positions
311
+ function updateObstacles() {
312
+ for (let i = obstacles.length - 1; i >= 0; i--) {
313
+ const obstacle = obstacles[i];
314
+ const currentLeft = parseInt(obstacle.element.style.left);
315
+ const newLeft = currentLeft - gameSpeed;
316
+
317
+ obstacle.element.style.left = `${newLeft}px`;
318
+
319
+ // Remove obstacle if it's off screen
320
+ if (newLeft < -100) {
321
+ gameContainer.removeChild(obstacle.element);
322
+ obstacles.splice(i, 1);
323
+ }
324
+
325
+ // Check for collision
326
+ if (checkCollision(player, obstacle.element) && obstacle.lane === playerLane && !isJumping && !isRolling) {
327
+ gameOver();
328
+ return;
329
+ }
330
+ }
331
+ }
332
+
333
+ // Update coin positions
334
+ function updateCoins() {
335
+ for (let i = coinsArray.length - 1; i >= 0; i--) {
336
+ const coin = coinsArray[i];
337
+ const currentLeft = parseInt(coin.element.style.left);
338
+ const newLeft = currentLeft - gameSpeed;
339
+
340
+ coin.element.style.left = `${newLeft}px`;
341
+
342
+ // Remove coin if it's off screen
343
+ if (newLeft < -50) {
344
+ gameContainer.removeChild(coin.element);
345
+ coinsArray.splice(i, 1);
346
+ }
347
+
348
+ // Check for collection
349
+ if (checkCollision(player, coin.element) && coin.lane === playerLane) {
350
+ coins++;
351
+ coinsElement.textContent = coins;
352
+
353
+ // Create sparkle effect
354
+ const sparkle = document.createElement('div');
355
+ sparkle.style.position = 'absolute';
356
+ sparkle.style.left = coin.element.style.left;
357
+ sparkle.style.top = coin.element.style.top;
358
+ sparkle.style.width = '40px';
359
+ sparkle.style.height = '40px';
360
+ sparkle.style.backgroundImage = 'radial-gradient(circle, yellow, transparent)';
361
+ sparkle.style.zIndex = '15';
362
+ sparkle.style.pointerEvents = 'none';
363
+ sparkle.style.opacity = '0.8';
364
+ sparkle.style.animation = 'fadeOut 0.5s forwards';
365
+
366
+ gameContainer.appendChild(sparkle);
367
+ setTimeout(() => {
368
+ gameContainer.removeChild(sparkle);
369
+ }, 500);
370
+
371
+ gameContainer.removeChild(coin.element);
372
+ coinsArray.splice(i, 1);
373
+
374
+ // Play coin sound
375
+ playSound('coin');
376
+ }
377
+ }
378
+ }
379
+
380
+ // Check collision between two elements
381
+ function checkCollision(element1, element2) {
382
+ const rect1 = element1.getBoundingClientRect();
383
+ const rect2 = element2.getBoundingClientRect();
384
+
385
+ return !(
386
+ rect1.right < rect2.left ||
387
+ rect1.left > rect2.right ||
388
+ rect1.bottom < rect2.top ||
389
+ rect1.top > rect2.bottom
390
+ );
391
+ }
392
+
393
+ // Game over function
394
+ function gameOver() {
395
+ gameRunning = false;
396
+ cancelAnimationFrame(animationId);
397
+
398
+ // Show game over screen
399
+ finalScoreElement.textContent = score;
400
+ finalCoinsElement.textContent = coins;
401
+ gameOverScreen.style.display = 'flex';
402
+
403
+ // Play crash sound
404
+ playSound('crash');
405
+ }
406
+
407
+ // Reset game state
408
+ function resetGame() {
409
+ // Clear all obstacles and coins
410
+ obstacles.forEach(obstacle => {
411
+ if (obstacle.element.parentNode) {
412
+ gameContainer.removeChild(obstacle.element);
413
+ }
414
+ });
415
+
416
+ coinsArray.forEach(coin => {
417
+ if (coin.element.parentNode) {
418
+ gameContainer.removeChild(coin.element);
419
+ }
420
+ });
421
+
422
+ obstacles = [];
423
+ coinsArray = [];
424
+
425
+ // Reset player
426
+ initPlayer();
427
+
428
+ // Reset score and speed
429
+ score = 0;
430
+ coins = 0;
431
+ gameSpeed = 5;
432
+ frameCount = 0;
433
+
434
+ // Update UI
435
+ scoreElement.textContent = score;
436
+ coinsElement.textContent = coins;
437
+ }
438
+
439
+ // Main game loop
440
+ function gameLoop() {
441
+ if (!gameRunning) return;
442
+
443
+ frameCount++;
444
+
445
+ // Spawn new obstacles and coins
446
+ if (frameCount % obstacleSpawnRate === 0) {
447
+ createObstacle();
448
+
449
+ // Increase difficulty over time
450
+ if (obstacleSpawnRate > 40) {
451
+ obstacleSpawnRate -= 1;
452
+ }
453
+ }
454
+
455
+ if (frameCount % coinSpawnRate === 0) {
456
+ createCoin();
457
+ }
458
+
459
+ // Update game elements
460
+ updateObstacles();
461
+ updateCoins();
462
+
463
+ // Increase score
464
+ score++;
465
+ scoreElement.textContent = Math.floor(score / 10);
466
+
467
+ // Gradually increase game speed
468
+ if (frameCount % 500 === 0) {
469
+ gameSpeed += 0.5;
470
+ }
471
+
472
+ // End jump/roll animation after short time
473
+ if (isJumping && frameCount % 30 === 0) {
474
+ isJumping = false;
475
+ player.style.transform = 'none';
476
+ }
477
+
478
+ if (isRolling && frameCount % 40 === 0) {
479
+ isRolling = false;
480
+ player.style.transform = 'none';
481
+ player.style.height = '100px';
482
+ }
483
+
484
+ animationId = requestAnimationFrame(gameLoop);
485
+ }
486
+
487
+ // Start the game
488
+ function startGame() {
489
+ resetGame();
490
+ gameRunning = true;
491
+ startScreen.style.display = 'none';
492
+ gameOverScreen.style.display = 'none';
493
+ gameLoop();
494
+ }
495
+
496
+ // Play sound effects
497
+ function playSound(type) {
498
+ // In a real game, you would play actual sound files here
499
+ console.log(`Playing ${type} sound`);
500
+ }
501
+
502
+ // Handle swipe gestures
503
+ let touchStartY = 0;
504
+ let touchEndY = 0;
505
+
506
+ function handleTouchStart(e) {
507
+ touchStartY = e.changedTouches[0].screenY;
508
+ }
509
+
510
+ function handleTouchEnd(e) {
511
+ if (!gameRunning) return;
512
+
513
+ touchEndY = e.changedTouches[0].screenY;
514
+ const diffY = touchStartY - touchEndY;
515
+
516
+ // Swipe up
517
+ if (diffY > 50) {
518
+ if (playerLane > 0) {
519
+ moveToLane(playerLane - 1);
520
+ playSound('swipe');
521
+ } else {
522
+ // Jump if already in top lane
523
+ if (!isJumping && !isRolling) {
524
+ isJumping = true;
525
+ player.style.transform = 'translateY(-30px)';
526
+ playSound('jump');
527
+ }
528
+ }
529
+ }
530
+ // Swipe down
531
+ else if (diffY < -50) {
532
+ if (playerLane < 2) {
533
+ moveToLane(playerLane + 1);
534
+ playSound('swipe');
535
+ } else {
536
+ // Roll if already in bottom lane
537
+ if (!isJumping && !isRolling) {
538
+ isRolling = true;
539
+ player.style.height = '50px';
540
+ player.style.transform = 'rotate(360deg)';
541
+ playSound('roll');
542
+ }
543
+ }
544
+ }
545
+ }
546
+
547
+ // Keyboard controls
548
+ function handleKeyDown(e) {
549
+ if (!gameRunning) return;
550
+
551
+ switch(e.key) {
552
+ case 'ArrowUp':
553
+ if (playerLane > 0) {
554
+ moveToLane(playerLane - 1);
555
+ playSound('swipe');
556
+ } else {
557
+ // Jump if already in top lane
558
+ if (!isJumping && !isRolling) {
559
+ isJumping = true;
560
+ player.style.transform = 'translateY(-30px)';
561
+ playSound('jump');
562
+ }
563
+ }
564
+ break;
565
+ case 'ArrowDown':
566
+ if (playerLane < 2) {
567
+ moveToLane(playerLane + 1);
568
+ playSound('swipe');
569
+ } else {
570
+ // Roll if already in bottom lane
571
+ if (!isJumping && !isRolling) {
572
+ isRolling = true;
573
+ player.style.height = '50px';
574
+ player.style.transform = 'rotate(360deg)';
575
+ playSound('roll');
576
+ }
577
+ }
578
+ break;
579
+ case ' ':
580
+ // Jump or roll based on current lane
581
+ if (playerLane === 0 && !isJumping && !isRolling) {
582
+ isJumping = true;
583
+ player.style.transform = 'translateY(-30px)';
584
+ playSound('jump');
585
+ } else if (playerLane === 2 && !isJumping && !isRolling) {
586
+ isRolling = true;
587
+ player.style.height = '50px';
588
+ player.style.transform = 'rotate(360deg)';
589
+ playSound('roll');
590
+ }
591
+ break;
592
+ }
593
+ }
594
+
595
+ // Event listeners
596
+ startButton.addEventListener('click', startGame);
597
+ restartButton.addEventListener('click', startGame);
598
+
599
+ // Touch controls for mobile
600
+ gameContainer.addEventListener('touchstart', handleTouchStart, false);
601
+ gameContainer.addEventListener('touchend', handleTouchEnd, false);
602
+
603
+ // Keyboard controls for desktop
604
+ document.addEventListener('keydown', handleKeyDown);
605
+
606
+ // Initialize player position
607
+ initPlayer();
608
+ </script>
609
+ <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=dlma/subway" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
610
+ </html>