Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Pixel Arcade - Retro Gaming</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); | |
| body { | |
| font-family: 'Press Start 2P', cursive; | |
| background-color: #0f0f1a; | |
| color: #e0e0e0; | |
| overflow-x: hidden; | |
| } | |
| .pixel-border { | |
| border: 4px solid #ff00ff; | |
| box-shadow: | |
| 0 0 0 4px #00ffff, | |
| 0 0 0 8px #ff00ff, | |
| 0 0 0 12px #ffff00; | |
| } | |
| .pixel-text { | |
| text-shadow: 4px 4px 0px #ff00ff, -4px -4px 0px #00ffff; | |
| } | |
| .scanlines { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient( | |
| rgba(18, 16, 16, 0) 50%, | |
| rgba(0, 0, 0, 0.25) 50% | |
| ); | |
| background-size: 100% 4px; | |
| pointer-events: none; | |
| z-index: 100; | |
| } | |
| .game-screen { | |
| background: repeating-linear-gradient( | |
| 0deg, | |
| rgba(0, 0, 0, 0.15), | |
| rgba(0, 0, 0, 0.15) 1px, | |
| transparent 1px, | |
| transparent 2px | |
| ); | |
| } | |
| .pixel-btn { | |
| position: relative; | |
| transition: all 0.2s; | |
| } | |
| .pixel-btn:hover { | |
| transform: translate(-2px, -2px); | |
| box-shadow: 4px 4px 0px #ff00ff; | |
| } | |
| .pixel-btn:active { | |
| transform: translate(0, 0); | |
| box-shadow: none; | |
| } | |
| @keyframes blink { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .blink { | |
| animation: blink 1s infinite; | |
| } | |
| /* Game canvases */ | |
| #snake-game, #tetris-game, #pong-game { | |
| border: 4px solid #00ff00; | |
| image-rendering: pixelated; | |
| } | |
| #tetris-game { border-color: #ff00ff; } | |
| #pong-game { border-color: #ffff00; } | |
| /* CRT TV effect */ | |
| .crt-effect::before { | |
| content: " "; | |
| display: block; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| bottom: 0; | |
| right: 0; | |
| background: rgba(18, 16, 16, 0.1); | |
| opacity: 0.15; | |
| z-index: 2; | |
| pointer-events: none; | |
| } | |
| .crt-effect::after { | |
| content: " "; | |
| display: block; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| bottom: 0; | |
| right: 0; | |
| background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); | |
| z-index: 2; | |
| background-size: 100% 2px, 3px 100%; | |
| pointer-events: none; | |
| } | |
| /* Tetris blocks */ | |
| .tetris-I { background-color: #00FFFF; } | |
| .tetris-J { background-color: #0000FF; } | |
| .tetris-L { background-color: #FF7F00; } | |
| .tetris-O { background-color: #FFFF00; } | |
| .tetris-S { background-color: #00FF00; } | |
| .tetris-T { background-color: #800080; } | |
| .tetris-Z { background-color: #FF0000; } | |
| </style> | |
| </head> | |
| <body class="min-h-screen"> | |
| <!-- CRT Scanlines Effect --> | |
| <div class="scanlines"></div> | |
| <!-- Main Container --> | |
| <div class="container mx-auto px-4 py-8 crt-effect"> | |
| <!-- Header --> | |
| <header class="text-center mb-12"> | |
| <h1 class="text-5xl md:text-6xl lg:text-7xl mb-6 text-purple-400 pixel-text">PIXEL ARCADE</h1> | |
| <p class="text-lg text-cyan-300 mb-8">RETRO GAMING EXPERIENCE</p> | |
| <div class="flex justify-center space-x-4 mb-8"> | |
| <button class="pixel-btn bg-purple-600 text-white px-6 py-3 rounded-none">PLAY NOW</button> | |
| <button class="pixel-btn bg-cyan-600 text-white px-6 py-3 rounded-none">HIGH SCORES</button> | |
| <button class="pixel-btn bg-yellow-600 text-white px-6 py-3 rounded-none">ABOUT</button> | |
| </div> | |
| <div class="flex justify-center"> | |
| <div class="pixel-border p-4 inline-block"> | |
| <div class="bg-black p-2 game-screen"> | |
| <p class="text-green-400 blink">INSERT COIN</p> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Featured Games --> | |
| <section class="mb-16"> | |
| <h2 class="text-3xl text-yellow-400 mb-8 text-center pixel-text">CLASSIC GAMES</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-8"> | |
| <!-- Snake Game --> | |
| <div class="pixel-border p-4 bg-gray-900"> | |
| <h3 class="text-xl text-green-400 mb-4">SNAKE</h3> | |
| <div class="relative"> | |
| <canvas id="snake-game" width="300" height="300" class="w-full bg-black"></canvas> | |
| <div class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 p-2"> | |
| <p class="text-xs text-white">Arrow keys to move</p> | |
| </div> | |
| </div> | |
| <button onclick="startSnakeGame()" class="pixel-btn bg-green-600 text-white px-4 py-2 mt-4 w-full">PLAY SNAKE</button> | |
| </div> | |
| <!-- Tetris Game --> | |
| <div class="pixel-border p-4 bg-gray-900"> | |
| <h3 class="text-xl text-purple-400 mb-4">TETRIS</h3> | |
| <div class="relative"> | |
| <canvas id="tetris-game" width="300" height="480" class="w-full bg-black"></canvas> | |
| <div class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 p-2"> | |
| <p class="text-xs text-white">Arrow keys to move, Up to rotate</p> | |
| </div> | |
| </div> | |
| <button onclick="startTetrisGame()" class="pixel-btn bg-purple-600 text-white px-4 py-2 mt-4 w-full">PLAY TETRIS</button> | |
| </div> | |
| <!-- Pong Game --> | |
| <div class="pixel-border p-4 bg-gray-900"> | |
| <h3 class="text-xl text-yellow-400 mb-4">PONG</h3> | |
| <div class="relative"> | |
| <canvas id="pong-game" width="300" height="400" class="w-full bg-black"></canvas> | |
| <div class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 p-2"> | |
| <p class="text-xs text-white">W/S keys for left paddle</p> | |
| <p class="text-xs text-white">Up/Down for right paddle</p> | |
| </div> | |
| </div> | |
| <button onclick="startPongGame()" class="pixel-btn bg-yellow-600 text-white px-4 py-2 mt-4 w-full">PLAY PONG</button> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Retro Consoles --> | |
| <section class="mb-16"> | |
| <h2 class="text-3xl text-cyan-400 mb-8 text-center pixel-text">RETRO CONSOLES</h2> | |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-6"> | |
| <div class="text-center"> | |
| <div class="pixel-border p-2 bg-gray-800 mb-2"> | |
| <i class="fas fa-gamepad text-4xl text-red-500"></i> | |
| </div> | |
| <p class="text-sm">NES</p> | |
| </div> | |
| <div class="text-center"> | |
| <div class="pixel-border p-2 bg-gray-800 mb-2"> | |
| <i class="fas fa-gamepad text-4xl text-blue-500"></i> | |
| </div> | |
| <p class="text-sm">SNES</p> | |
| </div> | |
| <div class="text-center"> | |
| <div class="pixel-border p-2 bg-gray-800 mb-2"> | |
| <i class="fas fa-gamepad text-4xl text-green-500"></i> | |
| </div> | |
| <p class="text-sm">SEGA</p> | |
| </div> | |
| <div class="text-center"> | |
| <div class="pixel-border p-2 bg-gray-800 mb-2"> | |
| <i class="fas fa-gamepad text-4xl text-yellow-500"></i> | |
| </div> | |
| <p class="text-sm">ATARI</p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Footer --> | |
| <footer class="text-center py-8 border-t border-purple-900"> | |
| <div class="flex justify-center space-x-6 mb-4"> | |
| <a href="#" class="text-purple-400 hover:text-cyan-400"><i class="fab fa-twitter"></i></a> | |
| <a href="#" class="text-purple-400 hover:text-cyan-400"><i class="fab fa-facebook"></i></a> | |
| <a href="#" class="text-purple-400 hover:text-cyan-400"><i class="fab fa-instagram"></i></a> | |
| <a href="#" class="text-purple-400 hover:text-cyan-400"><i class="fab fa-discord"></i></a> | |
| </div> | |
| <p class="text-xs text-gray-500">© 2023 PIXEL ARCADE - ALL RIGHTS RESERVED</p> | |
| <p class="text-xs text-gray-600 mt-2">CREATED WITH <i class="fas fa-heart text-red-500"></i> FOR RETRO GAMERS</p> | |
| </footer> | |
| </div> | |
| <script> | |
| // ========== SNAKE GAME ========== | |
| let snakeGame; | |
| let snakeCtx; | |
| let snake = []; | |
| let food = {}; | |
| let direction = 'right'; | |
| let snakeLoop; | |
| let snakeScore = 0; | |
| function startSnakeGame() { | |
| snakeGame = document.getElementById('snake-game'); | |
| snakeCtx = snakeGame.getContext('2d'); | |
| // Initialize snake | |
| snake = [ | |
| {x: 150, y: 150}, | |
| {x: 140, y: 150}, | |
| {x: 130, y: 150}, | |
| {x: 120, y: 150}, | |
| {x: 110, y: 150} | |
| ]; | |
| // Create first food | |
| createSnakeFood(); | |
| // Reset score | |
| snakeScore = 0; | |
| // Start game loop | |
| if (snakeLoop) clearInterval(snakeLoop); | |
| snakeLoop = setInterval(updateSnakeGame, 100); | |
| // Focus on canvas for keyboard controls | |
| snakeGame.focus(); | |
| } | |
| function createSnakeFood() { | |
| food = { | |
| x: Math.floor(Math.random() * 30) * 10, | |
| y: Math.floor(Math.random() * 30) * 10 | |
| }; | |
| // Make sure food doesn't appear on snake | |
| for (let segment of snake) { | |
| if (segment.x === food.x && segment.y === food.y) { | |
| return createSnakeFood(); | |
| } | |
| } | |
| } | |
| function updateSnakeGame() { | |
| // Move snake | |
| let head = {x: snake[0].x, y: snake[0].y}; | |
| switch(direction) { | |
| case 'up': head.y -= 10; break; | |
| case 'down': head.y += 10; break; | |
| case 'left': head.x -= 10; break; | |
| case 'right': head.x += 10; break; | |
| } | |
| // Check collision with walls | |
| if (head.x < 0 || head.x >= snakeGame.width || head.y < 0 || head.y >= snakeGame.height) { | |
| clearInterval(snakeLoop); | |
| alert('Game Over! Score: ' + snakeScore); | |
| return; | |
| } | |
| // Check collision with self | |
| for (let segment of snake) { | |
| if (segment.x === head.x && segment.y === head.y) { | |
| clearInterval(snakeLoop); | |
| alert('Game Over! Score: ' + snakeScore); | |
| return; | |
| } | |
| } | |
| // Check if snake ate food | |
| if (head.x === food.x && head.y === food.y) { | |
| snakeScore += 10; | |
| createSnakeFood(); | |
| } else { | |
| snake.pop(); // Remove tail if no food eaten | |
| } | |
| snake.unshift(head); // Add new head | |
| // Draw everything | |
| snakeCtx.fillStyle = 'black'; | |
| snakeCtx.fillRect(0, 0, snakeGame.width, snakeGame.height); | |
| // Draw snake | |
| snakeCtx.fillStyle = 'lime'; | |
| for (let segment of snake) { | |
| snakeCtx.fillRect(segment.x, segment.y, 10, 10); | |
| } | |
| // Draw food | |
| snakeCtx.fillStyle = 'red'; | |
| snakeCtx.fillRect(food.x, food.y, 10, 10); | |
| // Draw score | |
| snakeCtx.fillStyle = 'white'; | |
| snakeCtx.font = '20px "Press Start 2P"'; | |
| snakeCtx.fillText('Score: ' + snakeScore, 10, 25); | |
| } | |
| // Keyboard controls for snake | |
| document.getElementById('snake-game').addEventListener('keydown', function(e) { | |
| switch(e.key) { | |
| case 'ArrowUp': if (direction !== 'down') direction = 'up'; break; | |
| case 'ArrowDown': if (direction !== 'up') direction = 'down'; break; | |
| case 'ArrowLeft': if (direction !== 'right') direction = 'left'; break; | |
| case 'ArrowRight': if (direction !== 'left') direction = 'right'; break; | |
| } | |
| }); | |
| // ========== TETRIS GAME ========== | |
| let tetrisGame; | |
| let tetrisCtx; | |
| let tetrisBoard = []; | |
| let currentPiece; | |
| let nextPiece; | |
| let tetrisScore = 0; | |
| let tetrisLoop; | |
| let dropCounter = 0; | |
| let dropInterval = 1000; | |
| let lastTime = 0; | |
| const COLS = 10; | |
| const ROWS = 20; | |
| const BLOCK_SIZE = 30; | |
| // Tetris pieces | |
| const PIECES = [ | |
| { shape: [[1,1,1,1]], color: 'tetris-I' }, // I | |
| { shape: [[1,0,0],[1,1,1]], color: 'tetris-J' }, // J | |
| { shape: [[0,0,1],[1,1,1]], color: 'tetris-L' }, // L | |
| { shape: [[1,1],[1,1]], color: 'tetris-O' }, // O | |
| { shape: [[0,1,1],[1,1,0]], color: 'tetris-S' }, // S | |
| { shape: [[0,1,0],[1,1,1]], color: 'tetris-T' }, // T | |
| { shape: [[1,1,0],[0,1,1]], color: 'tetris-Z' } // Z | |
| ]; | |
| function startTetrisGame() { | |
| tetrisGame = document.getElementById('tetris-game'); | |
| tetrisCtx = tetrisGame.getContext('2d'); | |
| tetrisGame.width = COLS * BLOCK_SIZE; | |
| tetrisGame.height = ROWS * BLOCK_SIZE; | |
| // Scale canvas for display | |
| tetrisGame.style.width = '300px'; | |
| tetrisGame.style.height = '480px'; | |
| // Initialize empty board | |
| createBoard(); | |
| // Create first piece | |
| currentPiece = createPiece(); | |
| nextPiece = createPiece(); | |
| // Reset score | |
| tetrisScore = 0; | |
| // Start game loop | |
| if (tetrisLoop) cancelAnimationFrame(tetrisLoop); | |
| lastTime = 0; | |
| tetrisLoop = requestAnimationFrame(updateTetrisGame); | |
| // Focus on canvas for keyboard controls | |
| tetrisGame.focus(); | |
| } | |
| function createBoard() { | |
| tetrisBoard = Array.from(Array(ROWS), () => Array(COLS).fill(0)); | |
| } | |
| function createPiece() { | |
| const piece = JSON.parse(JSON.stringify(PIECES[Math.floor(Math.random() * PIECES.length)])); | |
| piece.pos = {x: Math.floor(COLS/2) - 1, y: -2}; | |
| return piece; | |
| } | |
| function updateTetrisGame(time = 0) { | |
| const deltaTime = time - lastTime; | |
| lastTime = time; | |
| dropCounter += deltaTime; | |
| if (dropCounter > dropInterval) { | |
| movePieceDown(); | |
| dropCounter = 0; | |
| } | |
| drawTetrisGame(); | |
| tetrisLoop = requestAnimationFrame(updateTetrisGame); | |
| } | |
| function drawTetrisGame() { | |
| tetrisCtx.fillStyle = 'black'; | |
| tetrisCtx.fillRect(0, 0, tetrisGame.width, tetrisGame.height); | |
| // Draw board | |
| drawBoard(); | |
| // Draw current piece | |
| drawPiece(); | |
| // Draw score | |
| tetrisCtx.fillStyle = 'white'; | |
| tetrisCtx.font = '18px "Press Start 2P"'; | |
| tetrisCtx.fillText('Score: ' + tetrisScore, 10, 25); | |
| } | |
| function drawBoard() { | |
| tetrisBoard.forEach((row, y) => { | |
| row.forEach((value, x) => { | |
| if (value) { | |
| tetrisCtx.fillStyle = PIECES[value-1].color; | |
| tetrisCtx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); | |
| tetrisCtx.strokeStyle = 'black'; | |
| tetrisCtx.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); | |
| } | |
| }); | |
| }); | |
| } | |
| function drawPiece() { | |
| currentPiece.shape.forEach((row, y) => { | |
| row.forEach((value, x) => { | |
| if (value) { | |
| tetrisCtx.fillStyle = currentPiece.color; | |
| tetrisCtx.fillRect( | |
| (currentPiece.pos.x + x) * BLOCK_SIZE, | |
| (currentPiece.pos.y + y) * BLOCK_SIZE, | |
| BLOCK_SIZE, | |
| BLOCK_SIZE | |
| ); | |
| tetrisCtx.strokeStyle = 'black'; | |
| tetrisCtx.strokeRect( | |
| (currentPiece.pos.x + x) * BLOCK_SIZE, | |
| (currentPiece.pos.y + y) * BLOCK_SIZE, | |
| BLOCK_SIZE, | |
| BLOCK_SIZE | |
| ); | |
| } | |
| }); | |
| }); | |
| } | |
| function movePieceDown() { | |
| currentPiece.pos.y++; | |
| if (collision()) { | |
| currentPiece.pos.y--; | |
| mergePiece(); | |
| removeRows(); | |
| currentPiece = nextPiece; | |
| nextPiece = createPiece(); | |
| // Check game over | |
| if (collision()) { | |
| cancelAnimationFrame(tetrisLoop); | |
| alert('Game Over! Score: ' + tetrisScore); | |
| return; | |
| } | |
| } | |
| dropCounter = 0; | |
| } | |
| function collision() { | |
| return currentPiece.shape.some((row, y) => { | |
| return row.some((value, x) => { | |
| return value && | |
| (currentPiece.pos.x + x < 0 || | |
| currentPiece.pos.x + x >= COLS || | |
| currentPiece.pos.y + y >= ROWS || | |
| tetrisBoard[currentPiece.pos.y + y][currentPiece.pos.x + x]); | |
| }); | |
| }); | |
| } | |
| function mergePiece() { | |
| currentPiece.shape.forEach((row, y) => { | |
| row.forEach((value, x) => { | |
| if (value) { | |
| tetrisBoard[currentPiece.pos.y + y][currentPiece.pos.x + x] = | |
| PIECES.findIndex(p => p.color === currentPiece.color) + 1; | |
| } | |
| }); | |
| }); | |
| } | |
| function removeRows() { | |
| let rowsCleared = 0; | |
| outer: for (let y = ROWS - 1; y >= 0; y--) { | |
| for (let x = 0; x < COLS; x++) { | |
| if (tetrisBoard[y][x] === 0) { | |
| continue outer; | |
| } | |
| } | |
| // Remove the row | |
| const row = tetrisBoard.splice(y, 1)[0].fill(0); | |
| tetrisBoard.unshift(row); | |
| y++; // Check the same row again (the row moved down) | |
| rowsCleared++; | |
| } | |
| // Update score | |
| if (rowsCleared > 0) { | |
| tetrisScore += [100, 300, 500, 800][rowsCleared - 1] || 0; | |
| dropInterval *= 0.9; // Increase speed | |
| } | |
| } | |
| function rotatePiece() { | |
| const originalShape = currentPiece.shape; | |
| // Transpose and reverse rows = rotation | |
| currentPiece.shape = currentPiece.shape[0].map((_, index) => | |
| currentPiece.shape.map(row => row[index]).reverse() | |
| ); | |
| if (collision()) { | |
| currentPiece.shape = originalShape; | |
| } | |
| } | |
| // Keyboard controls for Tetris | |
| document.getElementById('tetris-game').addEventListener('keydown', function(e) { | |
| if (e.key === 'ArrowLeft') { | |
| currentPiece.pos.x--; | |
| if (collision()) currentPiece.pos.x++; | |
| } else if (e.key === 'ArrowRight') { | |
| currentPiece.pos.x++; | |
| if (collision()) currentPiece.pos.x--; | |
| } else if (e.key === 'ArrowDown') { | |
| movePieceDown(); | |
| } else if (e.key === 'ArrowUp') { | |
| rotatePiece(); | |
| } | |
| }); | |
| // ========== PONG GAME ========== | |
| let pongGame; | |
| let pongCtx; | |
| let pongLoop; | |
| let leftPaddle = { y: 150, width: 10, height: 80 }; | |
| let rightPaddle = { y: 150, width: 10, height: 80 }; | |
| let ball = { x: 150, y: 200, radius: 8, dx: 4, dy: 4 }; | |
| let playerScore = 0; | |
| let computerScore = 0; | |
| const PADDLE_SPEED = 6; | |
| function startPongGame() { | |
| pongGame = document.getElementById('pong-game'); | |
| pongCtx = pongGame.getContext('2d'); | |
| pongGame.width = 300; | |
| pongGame.height = 400; | |
| // Reset positions | |
| leftPaddle = { y: 150, width: 10, height: 80 }; | |
| rightPaddle = { y: 150, width: 10, height: 80 }; | |
| ball = { x: 150, y: 200, radius: 8, dx: 4, dy: 4 }; | |
| playerScore = 0; | |
| computerScore = 0; | |
| // Start game loop | |
| if (pongLoop) cancelAnimationFrame(pongLoop); | |
| pongLoop = requestAnimationFrame(updatePongGame); | |
| // Focus on canvas for keyboard controls | |
| pongGame.focus(); | |
| } | |
| function updatePongGame() { | |
| // Move ball | |
| ball.x += ball.dx; | |
| ball.y += ball.dy; | |
| // Simple AI for right paddle | |
| if (rightPaddle.y + rightPaddle.height/2 < ball.y) { | |
| rightPaddle.y += PADDLE_SPEED - 1; | |
| } else { | |
| rightPaddle.y -= PADDLE_SPEED - 1; | |
| } | |
| // Wall collision (top/bottom) | |
| if (ball.y - ball.radius < 0 || ball.y + ball.radius > pongGame.height) { | |
| ball.dy = -ball.dy; | |
| } | |
| // Paddle collision | |
| // Left paddle | |
| if ( | |
| ball.dx < 0 && | |
| ball.x - ball.radius < leftPaddle.width && | |
| ball.y > leftPaddle.y && | |
| ball.y < leftPaddle.y + leftPaddle.height | |
| ) { | |
| ball.dx = -ball.dx; | |
| // Add angle based on where ball hits paddle | |
| const hitPos = (ball.y - (leftPaddle.y + leftPaddle.height/2)) / (leftPaddle.height/2); | |
| ball.dy = hitPos * 5; | |
| } | |
| // Right paddle | |
| if ( | |
| ball.dx > 0 && | |
| ball.x + ball.radius > pongGame.width - rightPaddle.width && | |
| ball.y > rightPaddle.y && | |
| ball.y < rightPaddle.y + rightPaddle.height | |
| ) { | |
| ball.dx = -ball.dx; | |
| // Add angle based on where ball hits paddle | |
| const hitPos = (ball.y - (rightPaddle.y + rightPaddle.height/2)) / (rightPaddle.height/2); | |
| ball.dy = hitPos * 5; | |
| } | |
| // Score points | |
| if (ball.x - ball.radius < 0) { | |
| computerScore++; | |
| resetBall(); | |
| } else if (ball.x + ball.radius > pongGame.width) { | |
| playerScore++; | |
| resetBall(); | |
| } | |
| // Game over (first to 5 points) | |
| if (playerScore >= 5 || computerScore >= 5) { | |
| cancelAnimationFrame(pongLoop); | |
| const winner = playerScore >= 5 ? "PLAYER" : "COMPUTER"; | |
| alert(`${winner} WINS! Final Score: ${playerScore}-${computerScore}`); | |
| return; | |
| } | |
| drawPongGame(); | |
| pongLoop = requestAnimationFrame(updatePongGame); | |
| } | |
| function resetBall() { | |
| ball.x = pongGame.width / 2; | |
| ball.y = pongGame.height / 2; | |
| ball.dx = -ball.dx; | |
| ball.dy = Math.random() * 4 - 2; | |
| } | |
| function drawPongGame() { | |
| // Draw background | |
| pongCtx.fillStyle = 'black'; | |
| pongCtx.fillRect(0, 0, pongGame.width, pongGame.height); | |
| // Draw center line | |
| pongCtx.strokeStyle = '#333'; | |
| pongCtx.setLineDash([10, 10]); | |
| pongCtx.beginPath(); | |
| pongCtx.moveTo(pongGame.width/2, 0); | |
| pongCtx.lineTo(pongGame.width/2, pongGame.height); | |
| pongCtx.stroke(); | |
| pongCtx.setLineDash([]); | |
| // Draw paddles | |
| pongCtx.fillStyle = 'cyan'; | |
| pongCtx.fillRect(0, leftPaddle.y, leftPaddle.width, leftPaddle.height); | |
| pongCtx.fillRect(pongGame.width - rightPaddle.width, rightPaddle.y, rightPaddle.width, rightPaddle.height); | |
| // Draw ball | |
| pongCtx.fillStyle = 'white'; | |
| pongCtx.beginPath(); | |
| pongCtx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2); | |
| pongCtx.fill(); | |
| // Draw scores | |
| pongCtx.fillStyle = 'white'; | |
| pongCtx.font = '24px "Press Start 2P"'; | |
| pongCtx.textAlign = 'center'; | |
| pongCtx.fillText(playerScore, pongGame.width / 4, 40); | |
| pongCtx.fillText(computerScore, (pongGame.width / 4) * 3, 40); | |
| } | |
| // Keyboard controls for Pong | |
| document.addEventListener('keydown', (e) => { | |
| switch(e.key) { | |
| case 'w': | |
| case 'W': | |
| if (leftPaddle.y > 0) leftPaddle.y -= PADDLE_SPEED; | |
| break; | |
| case 's': | |
| case 'S': | |
| if (leftPaddle.y < pongGame.height - leftPaddle.height) leftPaddle.y += PADDLE_SPEED; | |
| break; | |
| case 'ArrowUp': | |
| if (rightPaddle.y > 0) rightPaddle.y -= PADDLE_SPEED; | |
| break; | |
| case 'ArrowDown': | |
| if (rightPaddle.y < pongGame.height - rightPaddle.height) rightPaddle.y += PADDLE_SPEED; | |
| break; | |
| } | |
| }); | |
| // Make canvases focusable | |
| document.getElementById('snake-game').tabIndex = 0; | |
| document.getElementById('tetris-game').tabIndex = 0; | |
| document.getElementById('pong-game').tabIndex = 0; | |
| // Start snake game by default | |
| window.onload = function() { | |
| startSnakeGame(); | |
| }; | |
| </script> | |
| <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=saishshinde15/classic-games" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> | |
| </html> |