const canvas = document.getElementById('snakeCanvas'); const ctx = canvas.getContext('2d'); const gridSize = 20; const tileSize = 100; const snakeSize = 60; const foodSize = 80; canvas.width = gridSize * tileSize; canvas.height = gridSize * tileSize; let snake = [{ x: 10, y: 10 }, { x: 10, y: 11 }, { x: 10, y: 12 }]; let direction = { x: 0, y: 0 }; let food = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) }; let score = 0; let gameOver = false; function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); // draw grid of checkerboard pattern for (let x = 0; x < gridSize; x++) { for (let y = 0; y < gridSize; y++) { ctx.fillStyle = (x + y) % 2 === 0 ? getComputedStyle(document.documentElement).getPropertyValue('--background-color') : getComputedStyle(document.documentElement).getPropertyValue('--secondary-background-color'); ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize); } } // Draw snake snake.forEach(segment => { let nextVec = { x: 0, y: 0 }; // if there is a segment after the current segment if (snake.indexOf(segment) < snake.length - 1) { const nextSegment = snake[snake.indexOf(segment) + 1]; nextVec.x = nextSegment.x - segment.x; nextVec.y = nextSegment.y - segment.y; } ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--primary-color'); if (nextVec.x === 0 && nextVec.y === 0) { ctx.fillRect( segment.x * tileSize + (tileSize - snakeSize) / 2, segment.y * tileSize + (tileSize - snakeSize) / 2, snakeSize, snakeSize ); } else if (nextVec.x > 0 || nextVec.y > 0) { ctx.fillRect( segment.x * tileSize + (tileSize - snakeSize) / 2, segment.y * tileSize + (tileSize - snakeSize) / 2, snakeSize + nextVec.x * (tileSize - snakeSize), snakeSize + nextVec.y * (tileSize - snakeSize) ); } else { ctx.fillRect( segment.x * tileSize + (tileSize - snakeSize) / 2 + nextVec.x * (tileSize - snakeSize), segment.y * tileSize + (tileSize - snakeSize) / 2 + nextVec.y * (tileSize - snakeSize), snakeSize + Math.abs(nextVec.x) * (tileSize - snakeSize), snakeSize + Math.abs(nextVec.y) * (tileSize - snakeSize) ); } }); // Draw food ctx.fillStyle = '#ff4d4d'; ctx.fillRect( food.x * tileSize + (tileSize - foodSize) / 2, food.y * tileSize + (tileSize - foodSize) / 2, foodSize, foodSize ); } function update() { if (gameOver) return; // Move snake const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y }; // Add new head snake.unshift(head); // Check for food collision if (head.x === food.x && head.y === food.y) { score += 10; // Increase score placeFood(); } else { snake.pop(); // Remove tail if no food eaten } // Check for wall collision if (head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize) { gameOver = true; return; } // Check for self collision for (let i = 1; i < snake.length; i++) { if (head.x === snake[i].x && head.y === snake[i].y) { gameOver = true; return; } } } function placeFood() { do { food.x = Math.floor(Math.random() * gridSize); food.y = Math.floor(Math.random() * gridSize); } while (snake.some(segment => segment.x === food.x && segment.y === food.y)); } function changeDirection(event) { switch (event.key) { case 'w': if (direction.y === 0) direction = { x: 0, y: -1 }; break; case 's': if (direction.y === 0) direction = { x: 0, y: 1 }; break; case 'a': if (direction.x === 0) direction = { x: -1, y: 0 }; break; case 'd': if (direction.x === 0) direction = { x: 1, y: 0 }; break; } } // Menu to start the game function menu() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--background-color'); ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.textAlign = 'center'; ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--text-color'); ctx.font = '200px Arial'; ctx.fillText('Snake Game', canvas.width / 2, canvas.height / 2); ctx.font = '100px Arial'; ctx.fillText('Press W/A/S/D to move', canvas.width / 2, canvas.height / 2 + 100); ctx.fillText('Click to start', canvas.width / 2, canvas.height / 2 + 200); canvas.addEventListener('click', startGame); } function gameLoop() { if (!gameOver) { update(); draw(); setTimeout(gameLoop, 100); } else { document.removeEventListener('keydown', changeDirection); document.getElementById('score').value = score; alert(`Game Over! Your score: ${score}`); menu(); } } function startGame() { snake = [{ x: 10, y: 10 }, { x: 10, y: 11 }, { x: 10, y: 12 }]; direction = { x: 1, y: 0 }; food = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) }; score = 0; gameOver = false; canvas.removeEventListener('click', startGame); document.addEventListener('keydown', changeDirection); gameLoop(); } menu();