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 - Mobile Friendly</title> | |
<style> | |
body { | |
margin: 0; | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
min-height: 100vh; | |
background: #1a1a1a; | |
font-family: 'Arial', sans-serif; | |
touch-action: none; | |
} | |
.game-container { | |
position: relative; | |
padding: 20px; | |
border-radius: 15px; | |
background: #2a2a2a; | |
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); | |
margin-bottom: 20px; | |
} | |
#gameCanvas { | |
border-radius: 10px; | |
background: #333; | |
} | |
.score { | |
position: absolute; | |
top: -40px; | |
left: 0; | |
color: #fff; | |
font-size: 20px; | |
font-weight: bold; | |
} | |
.controls { | |
display: none; | |
margin-top: 20px; | |
gap: 10px; | |
} | |
.mobile-controls { | |
display: grid; | |
grid-template-columns: repeat(3, 1fr); | |
gap: 10px; | |
margin-top: 20px; | |
} | |
.control-btn { | |
width: 60px; | |
height: 60px; | |
border: none; | |
border-radius: 50%; | |
background: #4CAF50; | |
color: white; | |
font-size: 24px; | |
cursor: pointer; | |
transition: all 0.2s ease; | |
touch-action: manipulation; | |
} | |
.control-btn:active { | |
transform: scale(0.9); | |
background: #45a049; | |
} | |
#up { | |
grid-column: 2; | |
} | |
#down { | |
grid-column: 2; | |
grid-row: 2; | |
} | |
#left { | |
grid-column: 1; | |
grid-row: 2; | |
} | |
#right { | |
grid-column: 3; | |
grid-row: 2; | |
} | |
#startBtn { | |
padding: 12px 30px; | |
font-size: 16px; | |
background: linear-gradient(135deg, #4CAF50, #45a049); | |
color: white; | |
border: none; | |
border-radius: 25px; | |
cursor: pointer; | |
transition: transform 0.3s ease; | |
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3); | |
} | |
.game-over { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background: rgba(0, 0, 0, 0.9); | |
color: white; | |
padding: 20px 40px; | |
border-radius: 10px; | |
text-align: center; | |
display: none; | |
backdrop-filter: blur(5px); | |
} | |
@media (max-width: 600px) { | |
.mobile-controls { | |
display: grid; | |
} | |
} | |
.programmer-name { | |
position: fixed; | |
bottom: 20px; | |
left: 50%; | |
transform: translateX(-50%); | |
color: rgba(255, 255, 255, 0.8); | |
font-size: 18px; | |
font-family: 'Courier New', monospace; | |
text-transform: uppercase; | |
letter-spacing: 5px; | |
cursor: pointer; | |
transition: all 0.5s ease; | |
animation: float 3s ease-in-out infinite; | |
} | |
.programmer-name:hover { | |
color: #4CAF50; | |
letter-spacing: 8px; | |
text-shadow: 0 0 10px rgba(76, 175, 80, 0.8); | |
} | |
@keyframes float { | |
0%, 100% { | |
transform: translateX(-50%) translateY(0); | |
} | |
50% { | |
transform: translateX(-50%) translateY(-10px); | |
} | |
} | |
.name-effect { | |
position: fixed; | |
width: 20px; | |
height: 20px; | |
background: rgba(76, 175, 80, 0.5); | |
border-radius: 50%; | |
pointer-events: none; | |
animation: ripple 1s ease-out; | |
} | |
@keyframes ripple { | |
0% { | |
transform: scale(0); | |
opacity: 1; | |
} | |
100% { | |
transform: scale(2); | |
opacity: 0; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="game-container"> | |
<div class="score">Score: <span id="score">0</span></div> | |
<canvas id="gameCanvas" width="400" height="400"></canvas> | |
<div class="game-over" id="gameOver"> | |
<h2>Game Over!</h2> | |
<p>Final Score: <span id="finalScore">0</span></p> | |
<button id="startBtn">Play Again</button> | |
</div> | |
</div> | |
<div class="mobile-controls"> | |
<button class="control-btn" id="up">↑</button> | |
<button class="control-btn" id="left">←</button> | |
<button class="control-btn" id="right">→</button> | |
<button class="control-btn" id="down">↓</button> | |
</div> | |
<div class="programmer-name" id="programmerName">Ahmad Reza Anami</div> | |
<script> | |
const canvas = document.getElementById('gameCanvas'); | |
const ctx = canvas.getContext('2d'); | |
const scoreElement = document.getElementById('score'); | |
const finalScoreElement = document.getElementById('finalScore'); | |
const gameOverElement = document.getElementById('gameOver'); | |
const startBtn = document.getElementById('startBtn'); | |
const controlButtons = { | |
up: document.getElementById('up'), | |
down: document.getElementById('down'), | |
left: document.getElementById('left'), | |
right: document.getElementById('right') | |
}; | |
const gridSize = 20; | |
const tileCount = canvas.width / gridSize; | |
let snake = []; | |
let food = {}; | |
let goldenFood = null; | |
let dx = gridSize; | |
let dy = 0; | |
let score = 0; | |
let gameLoop; | |
let goldenFoodInterval; | |
let growth = 0; | |
let gameSpeed = 100; | |
function initGame() { | |
snake = [ | |
{ x: 5 * gridSize, y: 5 * gridSize }, | |
{ x: 4 * gridSize, y: 5 * gridSize } | |
]; | |
spawnFood(); | |
score = 0; | |
growth = 0; | |
goldenFood = null; | |
scoreElement.textContent = score; | |
dx = gridSize; | |
dy = 0; | |
gameSpeed = 100; | |
gameOverElement.style.display = 'none'; | |
clearInterval(gameLoop); | |
clearInterval(goldenFoodInterval); | |
gameLoop = setInterval(update, gameSpeed); | |
goldenFoodInterval = setInterval(spawnGoldenFood, 5000); | |
} | |
function spawnFood() { | |
food = { | |
x: Math.floor(Math.random() * tileCount) * gridSize, | |
y: Math.floor(Math.random() * tileCount) * gridSize | |
}; | |
if (snake.some(segment => segment.x === food.x && segment.y === food.y)) { | |
spawnFood(); | |
} | |
} | |
function spawnGoldenFood() { | |
if (!goldenFood && Math.random() < 0.2) { | |
let newFood; | |
do { | |
newFood = { | |
x: Math.floor(Math.random() * tileCount) * gridSize, | |
y: Math.floor(Math.random() * tileCount) * gridSize | |
}; | |
} while (snake.some(s => s.x === newFood.x && s.y === newFood.y) || | |
(newFood.x === food.x && newFood.y === food.y)); | |
goldenFood = newFood; | |
setTimeout(() => { | |
goldenFood = null; | |
}, 1000); | |
} | |
} | |
function update() { | |
let head = { | |
x: (snake[0].x + dx + canvas.width) % canvas.width, | |
y: (snake[0].y + dy + canvas.height) % canvas.height | |
}; | |
if (snake.some(segment => segment.x === head.x && segment.y === head.y)) { | |
gameOver(); | |
return; | |
} | |
snake.unshift(head); | |
if (head.x === food.x && head.y === food.y) { | |
score += 10; | |
growth += 1; | |
scoreElement.textContent = score; | |
spawnFood(); | |
gameSpeed = Math.max(50, gameSpeed - 2); | |
clearInterval(gameLoop); | |
gameLoop = setInterval(update, gameSpeed); | |
} else if (goldenFood && head.x === goldenFood.x && head.y === goldenFood.y) { | |
score += 20; | |
growth += 2; | |
scoreElement.textContent = score; | |
goldenFood = null; | |
} | |
if (growth > 0) { | |
growth--; | |
} else { | |
snake.pop(); | |
} | |
draw(); | |
} | |
function draw() { | |
ctx.fillStyle = '#333'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
snake.forEach((segment, index) => { | |
const gradient = ctx.createLinearGradient( | |
segment.x, segment.y, | |
segment.x + gridSize, segment.y + gridSize | |
); | |
gradient.addColorStop(0, index === 0 ? '#4CAF50' : '#45a049'); | |
gradient.addColorStop(1, index === 0 ? '#45a049' : '#3d8b40'); | |
ctx.fillStyle = gradient; | |
ctx.fillRect(segment.x + 1, segment.y + 1, gridSize - 2, gridSize - 2); | |
}); | |
ctx.fillStyle = '#ff4444'; | |
ctx.shadowColor = 'rgba(255, 68, 68, 0.5)'; | |
ctx.shadowBlur = 10; | |
ctx.beginPath(); | |
ctx.arc(food.x + gridSize/2, food.y + gridSize/2, gridSize/2 - 2, 0, Math.PI * 2); | |
ctx.fill(); | |
if (goldenFood) { | |
ctx.fillStyle = '#FFD700'; | |
ctx.shadowColor = 'rgba(255, 215, 0, 0.5)'; | |
ctx.beginPath(); | |
ctx.arc(goldenFood.x + gridSize/2, goldenFood.y + gridSize/2, gridSize/2 - 1, 0, Math.PI * 2); | |
ctx.fill(); | |
} | |
ctx.shadowBlur = 0; | |
} | |
function gameOver() { | |
clearInterval(gameLoop); | |
clearInterval(goldenFoodInterval); | |
gameOverElement.style.display = 'block'; | |
finalScoreElement.textContent = score; | |
} | |
// Control handlers | |
function handleDirectionChange(newDx, newDy) { | |
if ((dx === 0 && newDx !== 0) || (dy === 0 && newDy !== 0)) { | |
dx = newDx; | |
dy = newDy; | |
} | |
} | |
// Keyboard controls | |
document.addEventListener('keydown', (e) => { | |
switch(e.key) { | |
case 'ArrowUp': handleDirectionChange(0, -gridSize); break; | |
case 'ArrowDown': handleDirectionChange(0, gridSize); break; | |
case 'ArrowLeft': handleDirectionChange(-gridSize, 0); break; | |
case 'ArrowRight': handleDirectionChange(gridSize, 0); break; | |
} | |
}); | |
// Mobile controls | |
Object.entries(controlButtons).forEach(([direction, button]) => { | |
const handlePress = (e) => { | |
e.preventDefault(); | |
switch(direction) { | |
case 'up': handleDirectionChange(0, -gridSize); break; | |
case 'down': handleDirectionChange(0, gridSize); break; | |
case 'left': handleDirectionChange(-gridSize, 0); break; | |
case 'right': handleDirectionChange(gridSize, 0); break; | |
} | |
}; | |
button.addEventListener('touchstart', handlePress); | |
button.addEventListener('mousedown', handlePress); | |
}); | |
startBtn.addEventListener('click', initGame); | |
initGame(); | |
// Add interactive name effect | |
const programmerName = document.getElementById('programmerName'); | |
programmerName.addEventListener('click', (e) => { | |
// Create ripple effect | |
const ripple = document.createElement('div'); | |
ripple.classList.add('name-effect'); | |
ripple.style.left = `${e.clientX - 10}px`; | |
ripple.style.top = `${e.clientY - 10}px`; | |
document.body.appendChild(ripple); | |
// Remove ripple after animation | |
setTimeout(() => { | |
ripple.remove(); | |
}, 1000); | |
// Change text temporarily | |
const originalText = programmerName.textContent; | |
programmerName.textContent = "❤️ Snake Master ❤️"; | |
programmerName.style.color = '#ff4444'; | |
programmerName.style.textShadow = '0 0 15px rgba(255, 68, 68, 0.8)'; | |
setTimeout(() => { | |
programmerName.textContent = originalText; | |
programmerName.style.color = 'rgba(255, 255, 255, 0.8)'; | |
programmerName.style.textShadow = 'none'; | |
}, 2000); | |
}); | |
// Add mouse move effect | |
document.addEventListener('mousemove', (e) => { | |
const x = e.clientX; | |
const y = e.clientY; | |
const rect = programmerName.getBoundingClientRect(); | |
const dist = Math.sqrt( | |
Math.pow(x - (rect.left + rect.width / 2), 2) + | |
Math.pow(y - (rect.top + rect.height / 2), 2) | |
); | |
const maxDist = 200; | |
const scale = 1 + (1 - Math.min(dist / maxDist, 1)) * 0.2; | |
programmerName.style.transform = `translateX(-50%) scale(${scale})`; | |
}); | |
</script> | |
</body> | |
</html> |