snake-frenzy / index.html
andreaschandra's picture
Add 2 files
4f89150 verified
<!DOCTYPE html>
<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>