document.addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const scoreEl = document.getElementById('score'); const bestScoreEl = document.getElementById('best-score'); const playBtn = document.getElementById('play-btn'); const startScreen = document.getElementById('start-screen'); const settingsBtn = document.getElementById('settings-btn'); const closeSettingsBtn = document.getElementById('close-settings-btn'); const settingsBackdrop = document.getElementById('settings-backdrop'); const difficultyBtns = document.getElementById('difficulty-btns'); const themeToggle = document.getElementById('theme-toggle'); const gridSize = 20; canvas.width = 600; canvas.height = 600; let snake = [{ x: 15, y: 15 }]; let food = {}; let direction = 'right'; let score = 0; let bestScore = 0; let speed = 200; let gameInterval; let levelThreshold = 5; let gameRunning = false; let isGamePausedBySettings = false; // --- Settings --- function openSettings() { if (gameRunning) { clearInterval(gameInterval); isGamePausedBySettings = true; } settingsBackdrop.classList.remove('hidden'); } function closeSettings() { if (isGamePausedBySettings) { gameInterval = setInterval(update, speed); isGamePausedBySettings = false; } settingsBackdrop.classList.add('hidden'); } function setDifficulty(difficulty) { switch (difficulty) { case 'easy': speed = 250; break; case 'normal': speed = 150; break; case 'hard': speed = 75; break; } document.querySelectorAll('.difficulty-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.difficulty === difficulty); }); localStorage.setItem('snakeDifficulty', difficulty); } function applyTheme(theme) { document.body.classList.toggle('light-theme', theme === 'light'); document.body.classList.toggle('dark-theme', theme === 'dark'); themeToggle.checked = theme === 'dark'; localStorage.setItem('snakeTheme', theme); draw(); } settingsBtn.addEventListener('click', openSettings); closeSettingsBtn.addEventListener('click', closeSettings); settingsBackdrop.addEventListener('click', (e) => { if (e.target === settingsBackdrop) { closeSettings(); } }); difficultyBtns.addEventListener('click', (e) => { if (e.target.classList.contains('difficulty-btn')) { setDifficulty(e.target.dataset.difficulty); } }); themeToggle.addEventListener('change', () => { applyTheme(themeToggle.checked ? 'dark' : 'light'); }); const savedDifficulty = localStorage.getItem('snakeDifficulty') || 'normal'; setDifficulty(savedDifficulty); const savedTheme = localStorage.getItem('snakeTheme') || 'dark'; applyTheme(savedTheme); bestScore = localStorage.getItem('snakeBestScore') || 0; bestScoreEl.textContent = bestScore; // --- Game Logic --- function placeFood() { food = { x: Math.floor(Math.random() * (canvas.width / gridSize)), y: Math.floor(Math.random() * (canvas.height / gridSize)) }; for (let segment of snake) { if (segment.x === food.x && segment.y === food.y) { placeFood(); return; } } } function draw() { const canvasBg = getComputedStyle(document.body).getPropertyValue('--canvas-bg').trim(); ctx.fillStyle = canvasBg; ctx.fillRect(0, 0, canvas.width, canvas.height); snake.forEach((segment, index) => { const hue = (segment.x * 5 + segment.y * 5) % 360; ctx.fillStyle = `hsl(${hue}, 100%, 50%)`; if (index === 0) { ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize, gridSize); } else { ctx.fillRect(segment.x * gridSize + 1, segment.y * gridSize + 1, gridSize - 2, gridSize - 2); } }); const foodX = food.x * gridSize; const foodY = food.y * gridSize; const foodRadius = gridSize / 2.5; ctx.save(); ctx.shadowColor = '#A020F0'; ctx.shadowBlur = 20; const gradient = ctx.createRadialGradient( foodX + foodRadius, foodY + foodRadius, foodRadius * 0.1, foodX + foodRadius, foodY + foodRadius, foodRadius ); gradient.addColorStop(0, '#E0B0FF'); gradient.addColorStop(1, '#8A2BE2'); ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(foodX + gridSize / 2, foodY + gridSize / 2, foodRadius, 0, 2 * Math.PI); ctx.fill(); ctx.restore(); } function update() { if (!gameRunning) return; const head = { ...snake[0] }; 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 >= canvas.width / gridSize || head.y < 0 || head.y >= canvas.height / gridSize) { return gameOver(); } for (let i = 1; i < snake.length; i++) { if (head.x === snake[i].x && head.y === snake[i].y) { return gameOver(); } } snake.unshift(head); if (head.x === food.x && head.y === food.y) { score++; scoreEl.textContent = score; placeFood(); if (score % levelThreshold === 0) { increaseSpeed(); } } else { snake.pop(); } draw(); } function increaseSpeed() { clearInterval(gameInterval); speed = Math.max(50, speed * 0.9); gameInterval = setInterval(update, speed); } function gameOver() { clearInterval(gameInterval); isGamePausedBySettings = false; if (score > bestScore) { bestScore = score; localStorage.setItem('snakeBestScore', bestScore); bestScoreEl.textContent = bestScore; } startScreen.style.display = 'flex'; startScreen.querySelector('h1').textContent = 'Game Over'; playBtn.textContent = 'Play Again'; gameRunning = false; } function startGame() { snake = [{ x: 15, y: 15 }]; direction = 'right'; score = 0; scoreEl.textContent = score; gameRunning = true; isGamePausedBySettings = false; startScreen.style.display = 'none'; const currentDifficulty = localStorage.getItem('snakeDifficulty') || 'normal'; setDifficulty(currentDifficulty); placeFood(); clearInterval(gameInterval); gameInterval = setInterval(update, speed); } playBtn.addEventListener('click', startGame); document.addEventListener('keydown', e => { const key = e.key; if ((key === 'ArrowUp' || key.toLowerCase() === 'w') && direction !== 'down') direction = 'up'; if ((key === 'ArrowDown' || key.toLowerCase() === 's') && direction !== 'up') direction = 'down'; if ((key === 'ArrowLeft' || key.toLowerCase() === 'a') && direction !== 'right') direction = 'left'; if ((key === 'ArrowRight' || key.toLowerCase() === 'd') && direction !== 'left') direction = 'right'; }); draw(); });