Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Snake Game - Reach 100!</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| #game-board { | |
| background-color: #1a2e05; | |
| border: 4px solid #3a5f0b; | |
| box-shadow: 0 0 20px rgba(58, 95, 11, 0.5); | |
| } | |
| .snake-segment { | |
| background-color: #8bc34a; | |
| border: 1px solid #689f38; | |
| border-radius: 20%; | |
| } | |
| .food { | |
| background-color: #ff5252; | |
| border: 2px solid #ff1744; | |
| border-radius: 50%; | |
| animation: pulse 0.5s infinite alternate; | |
| } | |
| @keyframes pulse { | |
| from { transform: scale(0.95); } | |
| to { transform: scale(1.05); } | |
| } | |
| .game-over-overlay, .win-overlay { | |
| background-color: rgba(0, 0, 0, 0.7); | |
| } | |
| .difficulty-btn.active { | |
| background-color: #4CAF50; | |
| color: white; | |
| transform: scale(1.05); | |
| box-shadow: 0 0 10px rgba(76, 175, 80, 0.5); | |
| } | |
| .progress-bar { | |
| height: 10px; | |
| background-color: #2d3748; | |
| border-radius: 5px; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, #4CAF50, #8BC34A); | |
| transition: width 0.3s ease; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4"> | |
| <div class="text-center mb-6"> | |
| <h1 class="text-4xl font-bold text-green-400 mb-2">Snake Game</h1> | |
| <p class="text-gray-300">Reach 100 points to win!</p> | |
| </div> | |
| <div class="flex gap-4 mb-4"> | |
| <button id="easy-btn" class="difficulty-btn active bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg transition"> | |
| Easy | |
| </button> | |
| <button id="medium-btn" class="difficulty-btn bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg transition"> | |
| Medium | |
| </button> | |
| <button id="hard-btn" class="difficulty-btn bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg transition"> | |
| Hard | |
| </button> | |
| </div> | |
| <div class="relative"> | |
| <div class="flex justify-between items-center mb-4 w-full max-w-md"> | |
| <div class="bg-gray-800 px-4 py-2 rounded-lg"> | |
| <span class="text-gray-300 mr-2">Score:</span> | |
| <span id="score" class="text-green-400 font-bold text-xl">0</span> | |
| <span class="text-gray-400">/100</span> | |
| </div> | |
| <button id="restart-btn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition"> | |
| Restart Game | |
| </button> | |
| </div> | |
| <div class="progress-bar w-full max-w-md mb-2"> | |
| <div id="progress-fill" class="progress-fill" style="width: 0%"></div> | |
| </div> | |
| <canvas id="game-board" width="400" height="400" class="rounded-lg"></canvas> | |
| <div id="game-over" class="game-over-overlay absolute inset-0 flex flex-col items-center justify-center rounded-lg hidden"> | |
| <div class="bg-gray-800 p-6 rounded-lg text-center"> | |
| <h2 class="text-2xl font-bold text-red-400 mb-4">Game Over!</h2> | |
| <p class="text-xl mb-4">Your score: <span id="final-score" class="text-green-400">0</span>/100</p> | |
| <button id="play-again-btn" class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded-lg transition"> | |
| Play Again | |
| </button> | |
| </div> | |
| </div> | |
| <div id="win-screen" class="win-overlay absolute inset-0 flex flex-col items-center justify-center rounded-lg hidden"> | |
| <div class="bg-gray-800 p-6 rounded-lg text-center"> | |
| <h2 class="text-2xl font-bold text-green-400 mb-4">You Win! 🎉</h2> | |
| <p class="text-xl mb-4">You reached 100 points!</p> | |
| <button id="win-play-again-btn" class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded-lg transition"> | |
| Play Again | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6 text-gray-400 text-sm text-center max-w-md"> | |
| <p>Controls: Arrow keys or WASD (W=Up, A=Left, S=Down, D=Right)</p> | |
| <p class="mt-1">Press any control key to start the game</p> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const canvas = document.getElementById('game-board'); | |
| const ctx = canvas.getContext('2d'); | |
| const scoreDisplay = document.getElementById('score'); | |
| const finalScoreDisplay = document.getElementById('final-score'); | |
| const restartBtn = document.getElementById('restart-btn'); | |
| const playAgainBtn = document.getElementById('play-again-btn'); | |
| const winPlayAgainBtn = document.getElementById('win-play-again-btn'); | |
| const gameOverDisplay = document.getElementById('game-over'); | |
| const winDisplay = document.getElementById('win-screen'); | |
| const progressFill = document.getElementById('progress-fill'); | |
| // Difficulty buttons | |
| const easyBtn = document.getElementById('easy-btn'); | |
| const mediumBtn = document.getElementById('medium-btn'); | |
| const hardBtn = document.getElementById('hard-btn'); | |
| const gridSize = 20; | |
| const tileCount = canvas.width / gridSize; | |
| const WIN_SCORE = 100; | |
| let snake = []; | |
| let food = {}; | |
| let score = 0; | |
| let direction = 'right'; | |
| let nextDirection = 'right'; | |
| let gameSpeed; | |
| let gameRunning = false; | |
| let gameLoop; | |
| let currentDifficulty = 'easy'; | |
| // Difficulty settings | |
| const difficultySettings = { | |
| easy: { speed: 150, foodScore: 1 }, | |
| medium: { speed: 100, foodScore: 2 }, | |
| hard: { speed: 70, foodScore: 3 } | |
| }; | |
| // Initialize game | |
| function initGame() { | |
| snake = [ | |
| {x: 10, y: 10}, | |
| {x: 9, y: 10}, | |
| {x: 8, y: 10} | |
| ]; | |
| direction = 'right'; | |
| nextDirection = 'right'; | |
| score = 0; | |
| scoreDisplay.textContent = score; | |
| progressFill.style.width = '0%'; | |
| // Set game speed based on difficulty | |
| gameSpeed = difficultySettings[currentDifficulty].speed; | |
| placeFood(); | |
| gameRunning = true; | |
| gameOverDisplay.classList.add('hidden'); | |
| winDisplay.classList.add('hidden'); | |
| if (gameLoop) clearInterval(gameLoop); | |
| gameLoop = setInterval(gameStep, gameSpeed); | |
| } | |
| // Game step | |
| function gameStep() { | |
| if (!gameRunning) return; | |
| direction = nextDirection; | |
| // Move snake | |
| const head = {x: snake[0].x, y: snake[0].y}; | |
| switch (direction) { | |
| case 'up': | |
| head.y--; | |
| break; | |
| case 'down': | |
| head.y++; | |
| break; | |
| case 'left': | |
| head.x--; | |
| break; | |
| case 'right': | |
| head.x++; | |
| break; | |
| } | |
| // Check collision with walls | |
| if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) { | |
| gameOver(); | |
| return; | |
| } | |
| // Check collision with self | |
| for (let i = 0; i < snake.length; i++) { | |
| if (snake[i].x === head.x && snake[i].y === head.y) { | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| // Check if snake ate food | |
| if (head.x === food.x && head.y === food.y) { | |
| // Don't remove tail (snake grows) | |
| placeFood(); | |
| score += difficultySettings[currentDifficulty].foodScore; | |
| scoreDisplay.textContent = score; | |
| updateProgress(); | |
| // Check for win condition | |
| if (score >= WIN_SCORE) { | |
| winGame(); | |
| return; | |
| } | |
| // Increase speed slightly every 10 points | |
| if (score % 10 === 0) { | |
| clearInterval(gameLoop); | |
| gameSpeed = Math.max(gameSpeed - 5, 50); | |
| gameLoop = setInterval(gameStep, gameSpeed); | |
| } | |
| } else { | |
| // Remove tail | |
| snake.pop(); | |
| } | |
| // Add new head | |
| snake.unshift(head); | |
| // Draw everything | |
| draw(); | |
| } | |
| // Update progress bar | |
| function updateProgress() { | |
| const progress = (score / WIN_SCORE) * 100; | |
| progressFill.style.width = `${progress}%`; | |
| } | |
| // Draw game | |
| function draw() { | |
| // Clear canvas | |
| ctx.fillStyle = '#1a2e05'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw snake | |
| snake.forEach((segment, index) => { | |
| // Head is slightly different color | |
| const isHead = index === 0; | |
| ctx.fillStyle = isHead ? '#a5d6a7' : '#8bc34a'; | |
| ctx.strokeStyle = isHead ? '#689f38' : '#5a9216'; | |
| ctx.beginPath(); | |
| ctx.roundRect( | |
| segment.x * gridSize, | |
| segment.y * gridSize, | |
| gridSize, | |
| gridSize, | |
| 4 | |
| ); | |
| ctx.fill(); | |
| ctx.stroke(); | |
| }); | |
| // Draw food | |
| ctx.fillStyle = '#ff5252'; | |
| ctx.strokeStyle = '#ff1744'; | |
| ctx.beginPath(); | |
| ctx.arc( | |
| food.x * gridSize + gridSize/2, | |
| food.y * gridSize + gridSize/2, | |
| gridSize/2 - 2, | |
| 0, | |
| Math.PI * 2 | |
| ); | |
| ctx.fill(); | |
| ctx.stroke(); | |
| // Draw grid (optional) | |
| ctx.strokeStyle = 'rgba(58, 95, 11, 0.2)'; | |
| ctx.lineWidth = 0.5; | |
| for (let i = 0; i < tileCount; i++) { | |
| // Vertical lines | |
| ctx.beginPath(); | |
| ctx.moveTo(i * gridSize, 0); | |
| ctx.lineTo(i * gridSize, canvas.height); | |
| ctx.stroke(); | |
| // Horizontal lines | |
| ctx.beginPath(); | |
| ctx.moveTo(0, i * gridSize); | |
| ctx.lineTo(canvas.width, i * gridSize); | |
| ctx.stroke(); | |
| } | |
| } | |
| // Place food randomly | |
| function placeFood() { | |
| let validPosition = false; | |
| while (!validPosition) { | |
| food = { | |
| x: Math.floor(Math.random() * tileCount), | |
| y: Math.floor(Math.random() * tileCount) | |
| }; | |
| // Check if food is on snake | |
| validPosition = true; | |
| for (let i = 0; i < snake.length; i++) { | |
| if (snake[i].x === food.x && snake[i].y === food.y) { | |
| validPosition = false; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| // Game over | |
| function gameOver() { | |
| gameRunning = false; | |
| clearInterval(gameLoop); | |
| finalScoreDisplay.textContent = score; | |
| gameOverDisplay.classList.remove('hidden'); | |
| } | |
| // Win game | |
| function winGame() { | |
| gameRunning = false; | |
| clearInterval(gameLoop); | |
| winDisplay.classList.remove('hidden'); | |
| } | |
| // Event listeners | |
| document.addEventListener('keydown', (e) => { | |
| // Prevent default for arrow keys to avoid page scrolling | |
| const controlKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'a', 's', 'd']; | |
| if (controlKeys.includes(e.key)) { | |
| e.preventDefault(); | |
| // Start game on first key press if not running | |
| if (!gameRunning && snake.length === 0) { | |
| initGame(); | |
| return; | |
| } | |
| } | |
| // Change direction (can't go opposite current direction) | |
| switch (e.key.toLowerCase()) { | |
| case 'arrowup': | |
| case 'w': | |
| if (direction !== 'down') nextDirection = 'up'; | |
| break; | |
| case 'arrowdown': | |
| case 's': | |
| if (direction !== 'up') nextDirection = 'down'; | |
| break; | |
| case 'arrowleft': | |
| case 'a': | |
| if (direction !== 'right') nextDirection = 'left'; | |
| break; | |
| case 'arrowright': | |
| case 'd': | |
| if (direction !== 'left') nextDirection = 'right'; | |
| break; | |
| } | |
| }); | |
| // Difficulty buttons | |
| function setDifficulty(difficulty) { | |
| currentDifficulty = difficulty; | |
| // Update active button styling | |
| easyBtn.classList.remove('active'); | |
| mediumBtn.classList.remove('active'); | |
| hardBtn.classList.remove('active'); | |
| if (difficulty === 'easy') easyBtn.classList.add('active'); | |
| if (difficulty === 'medium') mediumBtn.classList.add('active'); | |
| if (difficulty === 'hard') hardBtn.classList.add('active'); | |
| // Restart game if already running | |
| if (gameRunning) { | |
| initGame(); | |
| } | |
| } | |
| easyBtn.addEventListener('click', () => setDifficulty('easy')); | |
| mediumBtn.addEventListener('click', () => setDifficulty('medium')); | |
| hardBtn.addEventListener('click', () => setDifficulty('hard')); | |
| restartBtn.addEventListener('click', initGame); | |
| playAgainBtn.addEventListener('click', initGame); | |
| winPlayAgainBtn.addEventListener('click', initGame); | |
| // Touch controls for mobile | |
| let touchStartX = 0; | |
| let touchStartY = 0; | |
| canvas.addEventListener('touchstart', (e) => { | |
| if (!gameRunning && snake.length === 0) { | |
| initGame(); | |
| return; | |
| } | |
| touchStartX = e.touches[0].clientX; | |
| touchStartY = e.touches[0].clientY; | |
| e.preventDefault(); | |
| }, false); | |
| canvas.addEventListener('touchmove', (e) => { | |
| if (!gameRunning) return; | |
| const touchEndX = e.touches[0].clientX; | |
| const touchEndY = e.touches[0].clientY; | |
| const dx = touchEndX - touchStartX; | |
| const dy = touchEndY - touchStartY; | |
| // Determine primary direction of swipe | |
| if (Math.abs(dx) > Math.abs(dy)) { | |
| // Horizontal swipe | |
| if (dx > 0 && direction !== 'left') { | |
| nextDirection = 'right'; | |
| } else if (dx < 0 && direction !== 'right') { | |
| nextDirection = 'left'; | |
| } | |
| } else { | |
| // Vertical swipe | |
| if (dy > 0 && direction !== 'up') { | |
| nextDirection = 'down'; | |
| } else if (dy < 0 && direction !== 'down') { | |
| nextDirection = 'up'; | |
| } | |
| } | |
| e.preventDefault(); | |
| }, false); | |
| // Initialize with easy difficulty | |
| setDifficulty('easy'); | |
| }); | |
| </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=andreaschandra/snake-frenzy" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |