|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Snake Game</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<style> |
|
|
|
.game-container { |
|
position: relative; |
|
margin: 0 auto; |
|
border: 8px solid #4B5563; |
|
border-radius: 8px; |
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); |
|
} |
|
|
|
canvas { |
|
display: block; |
|
background-color: #1F2937; |
|
} |
|
|
|
.game-overlay { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
display: flex; |
|
flex-direction: column; |
|
justify-content: center; |
|
align-items: center; |
|
background-color: rgba(0, 0, 0, 0.7); |
|
color: white; |
|
font-size: 2rem; |
|
z-index: 10; |
|
} |
|
|
|
.snake-cell { |
|
transition: all 0.1s ease; |
|
} |
|
|
|
@keyframes pulse { |
|
0% { transform: scale(1); } |
|
50% { transform: scale(1.1); } |
|
100% { transform: scale(1); } |
|
} |
|
|
|
.pulse { |
|
animation: pulse 0.5s infinite; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-900 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">Use arrow keys or swipe to control the snake</p> |
|
</div> |
|
|
|
<div class="game-container relative"> |
|
<canvas id="gameCanvas" width="400" height="400"></canvas> |
|
|
|
<div id="gameOverlay" class="game-overlay hidden"> |
|
<h2 class="text-4xl font-bold text-red-500 mb-4">Game Over!</h2> |
|
<p class="text-xl mb-6">Your score: <span id="finalScore" class="text-green-400 font-bold">0</span></p> |
|
<button id="restartBtn" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-6 rounded-full transition-all transform hover:scale-105"> |
|
Play Again |
|
</button> |
|
</div> |
|
|
|
<div id="startScreen" class="game-overlay flex"> |
|
<div class="text-center"> |
|
<h2 class="text-4xl font-bold text-green-400 mb-6 pulse">Ready to Play?</h2> |
|
<button id="startBtn" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-8 rounded-full text-xl transition-all transform hover:scale-105"> |
|
Start Game |
|
</button> |
|
<div class="mt-8 text-left text-gray-300"> |
|
<p class="flex items-center mb-2"><span class="inline-block w-6 mr-2">🔼</span> Move Up</p> |
|
<p class="flex items-center mb-2"><span class="inline-block w-6 mr-2">🔽</span> Move Down</p> |
|
<p class="flex items-center mb-2"><span class="inline-block w-6 mr-2">◀️</span> Move Left</p> |
|
<p class="flex items-center"><span class="inline-block w-6 mr-2">▶️</span> Move Right</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="mt-6 flex justify-between w-full max-w-md"> |
|
<div class="bg-gray-800 p-4 rounded-lg text-center flex-1 mx-2"> |
|
<p class="text-gray-400">Score</p> |
|
<p id="score" class="text-2xl font-bold text-green-400">0</p> |
|
</div> |
|
<div class="bg-gray-800 p-4 rounded-lg text-center flex-1 mx-2"> |
|
<p class="text-gray-400">High Score</p> |
|
<p id="highScore" class="text-2xl font-bold text-yellow-400">0</p> |
|
</div> |
|
</div> |
|
|
|
<div class="mt-8 grid grid-cols-4 gap-2 md:hidden"> |
|
<button id="upBtn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-4 rounded-full transition-all"> |
|
▲ |
|
</button> |
|
<div class="col-span-2"></div> |
|
<button id="leftBtn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-4 rounded-full transition-all"> |
|
◀ |
|
</button> |
|
<button id="downBtn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-4 rounded-full transition-all"> |
|
▼ |
|
</button> |
|
<button id="rightBtn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-4 rounded-full transition-all"> |
|
▶ |
|
</button> |
|
</div> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
|
const canvas = document.getElementById('gameCanvas'); |
|
const ctx = canvas.getContext('2d'); |
|
const scoreElement = document.getElementById('score'); |
|
const highScoreElement = document.getElementById('highScore'); |
|
const gameOverlay = document.getElementById('gameOverlay'); |
|
const startScreen = document.getElementById('startScreen'); |
|
const finalScoreElement = document.getElementById('finalScore'); |
|
const restartBtn = document.getElementById('restartBtn'); |
|
const startBtn = document.getElementById('startBtn'); |
|
|
|
|
|
const upBtn = document.getElementById('upBtn'); |
|
const downBtn = document.getElementById('downBtn'); |
|
const leftBtn = document.getElementById('leftBtn'); |
|
const rightBtn = document.getElementById('rightBtn'); |
|
|
|
|
|
const gridSize = 20; |
|
const tileCount = canvas.width / gridSize; |
|
let speed = 7; |
|
|
|
|
|
let snake = []; |
|
let food = {}; |
|
let direction = 'right'; |
|
let nextDirection = 'right'; |
|
let score = 0; |
|
let highScore = localStorage.getItem('snakeHighScore') || 0; |
|
let gameRunning = false; |
|
let gameLoop; |
|
|
|
|
|
highScoreElement.textContent = highScore; |
|
|
|
|
|
document.addEventListener('keydown', changeDirection); |
|
restartBtn.addEventListener('click', resetGame); |
|
startBtn.addEventListener('click', startGame); |
|
|
|
|
|
upBtn.addEventListener('click', () => changeDirection({keyCode: 38})); |
|
downBtn.addEventListener('click', () => changeDirection({keyCode: 40})); |
|
leftBtn.addEventListener('click', () => changeDirection({keyCode: 37})); |
|
rightBtn.addEventListener('click', () => changeDirection({keyCode: 39})); |
|
|
|
|
|
let touchStartX = 0; |
|
let touchStartY = 0; |
|
|
|
canvas.addEventListener('touchstart', (e) => { |
|
touchStartX = e.touches[0].clientX; |
|
touchStartY = e.touches[0].clientY; |
|
}, false); |
|
|
|
canvas.addEventListener('touchmove', (e) => { |
|
if (!touchStartX || !touchStartY) return; |
|
|
|
const touchEndX = e.touches[0].clientX; |
|
const touchEndY = e.touches[0].clientY; |
|
|
|
const diffX = touchStartX - touchEndX; |
|
const diffY = touchStartY - touchEndY; |
|
|
|
if (Math.abs(diffX) > Math.abs(diffY)) { |
|
|
|
if (diffX > 0 && direction !== 'right') { |
|
nextDirection = 'left'; |
|
} else if (diffX < 0 && direction !== 'left') { |
|
nextDirection = 'right'; |
|
} |
|
} else { |
|
|
|
if (diffY > 0 && direction !== 'down') { |
|
nextDirection = 'up'; |
|
} else if (diffY < 0 && direction !== 'up') { |
|
nextDirection = 'down'; |
|
} |
|
} |
|
|
|
touchStartX = 0; |
|
touchStartY = 0; |
|
e.preventDefault(); |
|
}, false); |
|
|
|
function startGame() { |
|
startScreen.classList.add('hidden'); |
|
resetGame(); |
|
} |
|
|
|
function resetGame() { |
|
|
|
snake = []; |
|
for (let i = 3; i >= 0; i--) { |
|
snake.push({x: i, y: 0}); |
|
} |
|
|
|
|
|
direction = 'right'; |
|
nextDirection = 'right'; |
|
score = 0; |
|
scoreElement.textContent = score; |
|
gameOverlay.classList.add('hidden'); |
|
gameRunning = true; |
|
|
|
|
|
createFood(); |
|
|
|
|
|
if (gameLoop) clearInterval(gameLoop); |
|
gameLoop = setInterval(gameUpdate, 1000 / speed); |
|
} |
|
|
|
function gameUpdate() { |
|
|
|
direction = nextDirection; |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) { |
|
gameOver(); |
|
return; |
|
} |
|
|
|
|
|
for (let i = 0; i < snake.length; i++) { |
|
if (snake[i].x === head.x && snake[i].y === head.y) { |
|
gameOver(); |
|
return; |
|
} |
|
} |
|
|
|
|
|
if (head.x === food.x && head.y === food.y) { |
|
|
|
createFood(); |
|
score++; |
|
scoreElement.textContent = score; |
|
|
|
|
|
if (score % 5 === 0) { |
|
speed += 0.5; |
|
clearInterval(gameLoop); |
|
gameLoop = setInterval(gameUpdate, 1000 / speed); |
|
} |
|
} else { |
|
|
|
snake.pop(); |
|
} |
|
|
|
|
|
snake.unshift(head); |
|
|
|
|
|
drawGame(); |
|
} |
|
|
|
function drawGame() { |
|
|
|
ctx.fillStyle = '#1F2937'; |
|
ctx.fillRect(0, 0, canvas.width, canvas.height); |
|
|
|
|
|
ctx.strokeStyle = '#374151'; |
|
ctx.lineWidth = 0.5; |
|
|
|
for (let i = 0; i < tileCount; i++) { |
|
|
|
ctx.beginPath(); |
|
ctx.moveTo(i * gridSize, 0); |
|
ctx.lineTo(i * gridSize, canvas.height); |
|
ctx.stroke(); |
|
|
|
|
|
ctx.beginPath(); |
|
ctx.moveTo(0, i * gridSize); |
|
ctx.lineTo(canvas.width, i * gridSize); |
|
ctx.stroke(); |
|
} |
|
|
|
|
|
snake.forEach((segment, index) => { |
|
|
|
if (index === 0) { |
|
ctx.fillStyle = '#10B981'; |
|
} else { |
|
|
|
const hue = 120 - (index * 2); |
|
ctx.fillStyle = `hsl(${hue}, 80%, 50%)`; |
|
} |
|
|
|
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 1, gridSize - 1); |
|
|
|
|
|
if (index === 0) { |
|
ctx.fillStyle = 'white'; |
|
|
|
if (direction === 'right') { |
|
ctx.fillRect((segment.x * gridSize) + 12, (segment.y * gridSize) + 4, 4, 4); |
|
ctx.fillRect((segment.x * gridSize) + 12, (segment.y * gridSize) + 12, 4, 4); |
|
} else if (direction === 'left') { |
|
ctx.fillRect((segment.x * gridSize) + 4, (segment.y * gridSize) + 4, 4, 4); |
|
ctx.fillRect((segment.x * gridSize) + 4, (segment.y * gridSize) + 12, 4, 4); |
|
} else if (direction === 'up') { |
|
ctx.fillRect((segment.x * gridSize) + 4, (segment.y * gridSize) + 4, 4, 4); |
|
ctx.fillRect((segment.x * gridSize) + 12, (segment.y * gridSize) + 4, 4, 4); |
|
} else if (direction === 'down') { |
|
ctx.fillRect((segment.x * gridSize) + 4, (segment.y * gridSize) + 12, 4, 4); |
|
ctx.fillRect((segment.x * gridSize) + 12, (segment.y * gridSize) + 12, 4, 4); |
|
} |
|
} |
|
}); |
|
|
|
|
|
ctx.fillStyle = '#EF4444'; |
|
ctx.beginPath(); |
|
const centerX = (food.x * gridSize) + (gridSize / 2); |
|
const centerY = (food.y * gridSize) + (gridSize / 2); |
|
const radius = (gridSize / 2) - 2; |
|
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2); |
|
ctx.fill(); |
|
|
|
|
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; |
|
ctx.beginPath(); |
|
ctx.arc(centerX - 3, centerY - 3, 3, 0, Math.PI * 2); |
|
ctx.fill(); |
|
} |
|
|
|
function createFood() { |
|
food = { |
|
x: Math.floor(Math.random() * tileCount), |
|
y: Math.floor(Math.random() * tileCount) |
|
}; |
|
|
|
|
|
for (let i = 0; i < snake.length; i++) { |
|
if (snake[i].x === food.x && snake[i].y === food.y) { |
|
createFood(); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
function changeDirection(e) { |
|
|
|
switch (e.keyCode) { |
|
case 37: |
|
if (direction !== 'right') nextDirection = 'left'; |
|
break; |
|
case 38: |
|
if (direction !== 'down') nextDirection = 'up'; |
|
break; |
|
case 39: |
|
if (direction !== 'left') nextDirection = 'right'; |
|
break; |
|
case 40: |
|
if (direction !== 'up') nextDirection = 'down'; |
|
break; |
|
} |
|
} |
|
|
|
function gameOver() { |
|
gameRunning = false; |
|
clearInterval(gameLoop); |
|
|
|
|
|
if (score > highScore) { |
|
highScore = score; |
|
highScoreElement.textContent = highScore; |
|
localStorage.setItem('snakeHighScore', highScore); |
|
} |
|
|
|
finalScoreElement.textContent = score; |
|
gameOverlay.classList.remove('hidden'); |
|
} |
|
|
|
|
|
drawGame(); |
|
}); |
|
</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=Ibrokhim1263/https-huggingface-co-ibrokhim1263" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |