// Snake Game Logic (function () { 'use strict'; const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const scoreEl = document.getElementById('score'); const highScoreEl = document.getElementById('high-score'); const restartBtn = document.getElementById('restartBtn'); const GRID_SIZE = 20; // size of each cell in pixels const COLS = canvas.width / GRID_SIZE; const ROWS = canvas.height / GRID_SIZE; let snake, direction, nextDirection, food, score, highScore, gameOver, gameLoop; highScore = Number(localStorage.getItem('snakeHighScore')) || 0; highScoreEl.textContent = highScore; // Initialize a new game state function init() { snake = [ { x: 10, y: 10 }, { x: 9, y: 10 }, { x: 8, y: 10 } ]; direction = { x: 1, y: 0 }; nextDirection = { x: 1, y: 0 }; score = 0; gameOver = false; scoreEl.textContent = score; placeFood(); if (gameLoop) clearInterval(gameLoop); gameLoop = setInterval(update, 100); } // Place food at a random empty cell function placeFood() { let newFood; do { newFood = { x: Math.floor(Math.random() * COLS), y: Math.floor(Math.random() * ROWS) }; } while (snake.some(seg => seg.x === newFood.x && seg.y === newFood.y)); food = newFood; } // Update game state each tick function update() { if (gameOver) return; direction = nextDirection; const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y }; // Wall collision if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS) { return endGame(); } // Self collision if (snake.some(seg => seg.x === head.x && seg.y === head.y)) { return endGame(); } snake.unshift(head); // Eat food if (head.x === food.x && head.y === food.y) { score += 10; scoreEl.textContent = score; if (score > highScore) { highScore = score; highScoreEl.textContent = highScore; localStorage.setItem('snakeHighScore', highScore); } placeFood(); } else { snake.pop(); } draw(); } // Draw everything on the canvas function draw() { // Background ctx.fillStyle = '#16213e'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Grid lines (subtle) ctx.strokeStyle = 'rgba(78, 204, 163, 0.08)'; for (let i = 1; i < COLS; i++) { ctx.beginPath(); ctx.moveTo(i * GRID_SIZE, 0); ctx.lineTo(i * GRID_SIZE, canvas.height); ctx.stroke(); } for (let j = 1; j < ROWS; j++) { ctx.beginPath(); ctx.moveTo(0, j * GRID_SIZE); ctx.lineTo(canvas.width, j * GRID_SIZE); ctx.stroke(); } // Food ctx.fillStyle = '#e94560'; ctx.beginPath(); ctx.arc( food.x * GRID_SIZE + GRID_SIZE / 2, food.y * GRID_SIZE + GRID_SIZE / 2, GRID_SIZE / 2 - 2, 0, Math.PI * 2 ); ctx.fill(); // Snake snake.forEach((seg, i) => { ctx.fillStyle = i === 0 ? '#4ecca3' : '#3aa886'; ctx.fillRect( seg.x * GRID_SIZE + 1, seg.y * GRID_SIZE + 1, GRID_SIZE - 2, GRID_SIZE - 2 ); }); // Game over overlay if (gameOver) { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#e94560'; ctx.font = 'bold 32px sans-serif'; ctx.textAlign = 'center'; ctx.fillText('Game Over', canvas.width / 2, canvas.height / 2 - 10); ctx.fillStyle = '#eee'; ctx.font = '18px sans-serif'; ctx.fillText('Press Restart to play again', canvas.width / 2, canvas.height / 2 + 30); } } // End the game function endGame() { gameOver = true; clearInterval(gameLoop); draw(); } // Keyboard controls document.addEventListener('keydown', (e) => { const key = e.key.toLowerCase(); let newDir = null; if (key === 'arrowup' || key === 'w') newDir = { x: 0, y: -1 }; else if (key === 'arrowdown' || key === 's') newDir = { x: 0, y: 1 }; else if (key === 'arrowleft' || key === 'a') newDir = { x: -1, y: 0 }; else if (key === 'arrowright' || key === 'd') newDir = { x: 1, y: 0 }; if (!newDir) return; e.preventDefault(); // Prevent reversing direction if (newDir.x === -direction.x && newDir.y === -direction.y) return; nextDirection = newDir; }); restartBtn.addEventListener('click', init); // Start the game init(); })();