Spaces:
Running
Running
| ```javascript | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const scoreElement = document.getElementById('score'); | |
| const startBtn = document.getElementById('startBtn'); | |
| const resetBtn = document.getElementById('resetBtn'); | |
| const upBtn = document.getElementById('upBtn'); | |
| const downBtn = document.getElementById('downBtn'); | |
| const leftBtn = document.getElementById('leftBtn'); | |
| const rightBtn = document.getElementById('rightBtn'); | |
| // Game variables | |
| let snake = []; | |
| let food = {}; | |
| let direction = 'right'; | |
| let nextDirection = 'right'; | |
| let gameSpeed = 150; | |
| let score = 0; | |
| let gameInterval; | |
| let gameRunning = false; | |
| const gridSize = 20; | |
| const tileCount = canvas.width / gridSize; | |
| // Initialize game | |
| function initGame() { | |
| snake = []; | |
| for (let i = 3; i >= 0; i--) { | |
| snake.push({ x: i * gridSize, y: 0 }); | |
| } | |
| generateFood(); | |
| score = 0; | |
| scoreElement.textContent = score; | |
| direction = 'right'; | |
| nextDirection = 'right'; | |
| if (gameRunning) { | |
| clearInterval(gameInterval); | |
| gameInterval = setInterval(gameLoop, gameSpeed); | |
| } | |
| drawGame(); | |
| } | |
| // Generate food at random position | |
| function generateFood() { | |
| food = { | |
| x: Math.floor(Math.random() * tileCount) * gridSize, | |
| y: Math.floor(Math.random() * tileCount) * gridSize | |
| }; | |
| // Make sure food doesn't spawn on snake | |
| for (let segment of snake) { | |
| if (segment.x === food.x && segment.y === food.y) { | |
| return generateFood(); | |
| } | |
| } | |
| } | |
| // Main game loop | |
| function gameLoop() { | |
| moveSnake(); | |
| checkCollision(); | |
| drawGame(); | |
| } | |
| // Move the snake | |
| function moveSnake() { | |
| direction = nextDirection; | |
| // Calculate new head position | |
| const head = { x: snake[0].x, y: snake[0].y }; | |
| switch (direction) { | |
| case 'up': | |
| head.y -= gridSize; | |
| break; | |
| case 'down': | |
| head.y += gridSize; | |
| break; | |
| case 'left': | |
| head.x -= gridSize; | |
| break; | |
| case 'right': | |
| head.x += gridSize; | |
| break; | |
| } | |
| // Add new head | |
| snake.unshift(head); | |
| // Check if snake ate food | |
| if (head.x === food.x && head.y === food.y) { | |
| score++; | |
| scoreElement.textContent = score; | |
| generateFood(); | |
| // Increase speed every 5 points | |
| if (score % 5 === 0) { | |
| gameSpeed = Math.max(50, gameSpeed - 10); | |
| clearInterval(gameInterval); | |
| gameInterval = setInterval(gameLoop, gameSpeed); | |
| } | |
| } else { | |
| // Remove tail if no food eaten | |
| snake.pop(); | |
| } | |
| } | |
| // Check for collisions | |
| function checkCollision() { | |
| const head = snake[0]; | |
| // Wall collision | |
| if ( | |
| head.x < 0 || head.x >= canvas.width || | |
| head.y < 0 || head.y >= canvas.height | |
| ) { | |
| gameOver(); | |
| } | |
| // Self collision | |
| for (let i = 1; i < snake.length; i++) { | |
| if (head.x === snake[i].x && head.y === snake[i].y) { | |
| gameOver(); | |
| } | |
| } | |
| } | |
| // Game over | |
| function gameOver() { | |
| clearInterval(gameInterval); | |
| gameRunning = false; | |
| startBtn.textContent = 'Restart Game'; | |
| alert(`Game Over! Your score: ${score}`); | |
| } | |
| // Draw game elements | |
| function drawGame() { | |
| // Clear canvas | |
| ctx.fillStyle = '#111827'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw snake | |
| snake.forEach((segment, index) => { | |
| const gradient = ctx.createLinearGradient(segment.x, segment.y, segment.x + gridSize, segment.y + gridSize); | |
| if (index === 0) { | |
| // Head - brighter green | |
| gradient.addColorStop(0, '#4ade80'); | |
| gradient.addColorStop(1, '#22c55e'); | |
| } else { | |
| // Body - darker green | |
| gradient.addColorStop(0, '#16a34a'); | |
| gradient.addColorStop(1, '#15803d'); | |
| } | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(segment.x, segment.y, gridSize - 1, gridSize - 1); | |
| // Add eyes to head | |
| if (index === 0) { | |
| ctx.fillStyle = 'white'; | |
| // Right eye | |
| if (direction === 'right') { | |
| ctx.fillRect(segment.x + 12, segment.y + 5, 3, 3); | |
| ctx.fillRect(segment.x + 12, segment.y + 12, 3, 3); | |
| } | |
| // Left eye | |
| else if (direction === 'left') { | |
| ctx.fillRect(segment.x + 5, segment.y + 5, 3, 3); | |
| ctx.fillRect(segment.x + 5, segment.y + 12, 3, 3); | |
| } | |
| // Up eye | |
| else if (direction === 'up') { | |
| ctx.fillRect(segment.x + 5, segment.y + 5, 3, 3); | |
| ctx.fillRect(segment.x + 12, segment.y + 5, 3, 3); | |
| } | |
| // Down eye | |
| else if (direction === 'down') { | |
| ctx.fillRect(segment.x + 5, segment.y + 12, 3, 3); | |
| ctx.fillRect(segment.x + 12, segment.y + 12, 3, 3); | |
| } | |
| } | |
| }); | |
| // Draw food | |
| const foodGradient = ctx.createRadialGradient( | |
| food.x + gridSize/2, food.y + gridSize/2, 0, | |
| food.x + gridSize/2, food.y + gridSize/2, gridSize/2 | |
| ); | |
| foodGradient.addColorStop(0, '#ef4444'); | |
| foodGradient.addColorStop(1, '#dc2626'); | |
| ctx.fillStyle = foodGradient; | |
| ctx.beginPath(); | |
| ctx.arc(food.x + gridSize/2, food.y + gridSize/2, gridSize/2 - 1, 0, Math.PI * 2); | |
| ctx.fill(); | |
| } | |
| // Event listeners | |
| startBtn.addEventListener('click', () => { | |
| if (!gameRunning) { | |
| gameRunning = true; | |
| startBtn.textContent = 'Pause'; | |
| initGame(); | |
| gameInterval = setInterval(gameLoop, gameSpeed); | |
| } else { | |
| gameRunning = false; | |
| clearInterval(gameInterval); | |
| startBtn.textContent = 'Resume'; | |
| } | |
| }); | |
| resetBtn.addEventListener('click', () => { | |
| clearInterval(gameInterval); | |
| gameRunning = false; | |
| startBtn.textContent = 'Start Game'; | |
| initGame(); | |
| }); | |
| // Keyboard controls | |
| document.addEventListener('keydown', (e) => { | |
| switch (e.key) { | |
| case 'ArrowUp': | |
| if (direction !== 'down') nextDirection = 'up'; | |
| break; | |
| case 'ArrowDown': | |
| if (direction !== 'up') nextDirection = 'down'; | |
| break; | |
| case 'ArrowLeft': | |
| if (direction !== 'right') nextDirection = 'left'; | |
| break; | |
| case 'ArrowRight': | |
| if (direction !== 'left') nextDirection = 'right'; | |
| break; | |
| } | |
| }); | |
| // Touch controls | |
| upBtn.addEventListener('click', () => { | |
| if (direction !== 'down') nextDirection = 'up'; | |
| }); | |
| downBtn.addEventListener('click', () => { | |
| if (direction !== 'up') nextDirection = 'down'; | |
| }); | |
| leftBtn.addEventListener('click', () => { | |
| if (direction !== 'right') nextDirection = 'left'; | |
| }); | |
| rightBtn.addEventListener('click', () => { | |
| if (direction !== 'left') nextDirection = 'right'; | |
| }); | |
| // Initialize game state | |
| initGame(); | |
| }); | |
| ``` |