Created
July 18, 2026 03:04
-
-
Save nsdevaraj/cd4aae7cac68f2acc6cb0e94ae5934bd to your computer and use it in GitHub Desktop.
Snak.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Classic Snake Game</title> | |
| <style> | |
| body { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| height: 100vh; | |
| margin: 0; | |
| background-color: #2c3e50; | |
| color: white; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| #score { | |
| font-size: 28px; | |
| font-weight: bold; | |
| margin-bottom: 20px; | |
| letter-spacing: 2px; | |
| } | |
| canvas { | |
| background-color: #34495e; | |
| border: 4px solid #ecf0f1; | |
| border-radius: 8px; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); | |
| } | |
| #instructions { | |
| margin-top: 15px; | |
| color: #bdc3c7; | |
| font-size: 14px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="score">SCORE: 0</div> | |
| <!-- The game board --> | |
| <canvas id="gameCanvas" width="400" height="400"></canvas> | |
| <div id="instructions">Use Arrow Keys to move. Refresh page to restart.</div> | |
| <script> | |
| const canvas = document.getElementById("gameCanvas"); | |
| const ctx = canvas.getContext("2d"); | |
| // Game settings | |
| const gridSize = 20; | |
| let score = 0; | |
| let gameInterval; | |
| // Snake configuration (starts in the middle of the board) | |
| let snake = [ | |
| { x: 200, y: 200 }, | |
| { x: 200, y: 220 }, | |
| { x: 200, y: 240 } | |
| ]; | |
| // Initial movement direction (moving UP) | |
| let dx = 0; | |
| let dy = -gridSize; | |
| // Food configuration | |
| let food = { x: 0, y: 0 }; | |
| // Randomly place food on the grid | |
| function placeFood() { | |
| food.x = Math.floor(Math.random() * (canvas.width / gridSize)) * gridSize; | |
| food.y = Math.floor(Math.random() * (canvas.height / gridSize)) * gridSize; | |
| // Ensure food doesn't spawn exactly where the snake currently is | |
| snake.forEach(part => { | |
| if (part.x === food.x && part.y === food.y) { | |
| placeFood(); | |
| } | |
| }); | |
| } | |
| // Draw individual snake blocks | |
| function drawSnakePart(snakePart, index) { | |
| ctx.fillStyle = index === 0 ? "#2ecc71" : "#27ae60"; // Head is slightly lighter | |
| ctx.strokeStyle = "#1e8449"; | |
| ctx.fillRect(snakePart.x, snakePart.y, gridSize, gridSize); | |
| ctx.strokeRect(snakePart.x, snakePart.y, gridSize, gridSize); | |
| } | |
| // Draw the food block | |
| function drawFood() { | |
| ctx.fillStyle = "#e74c3c"; | |
| ctx.strokeStyle = "#c0392b"; | |
| ctx.fillRect(food.x, food.y, gridSize, gridSize); | |
| ctx.strokeRect(food.x, food.y, gridSize, gridSize); | |
| } | |
| // Update snake's position based on velocity | |
| function moveSnake() { | |
| const head = { x: snake[0].x + dx, y: snake[0].y + dy }; | |
| snake.unshift(head); // Add new head | |
| // Check if snake ate the food | |
| if (head.x === food.x && head.y === food.y) { | |
| score += 10; | |
| document.getElementById("score").innerText = "SCORE: " + score; | |
| placeFood(); | |
| } else { | |
| snake.pop(); // Remove tail if no food eaten | |
| } | |
| } | |
| // Check for game-ending collisions | |
| function checkCollision() { | |
| const head = snake[0]; | |
| // 1. Wall collision | |
| if (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height) { | |
| return true; | |
| } | |
| // 2. Self collision (start checking from index 1, skip the head) | |
| for (let i = 1; i < snake.length; i++) { | |
| if (head.x === snake[i].x && head.y === snake[i].y) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // The main game loop | |
| function main() { | |
| if (checkCollision()) { | |
| clearInterval(gameInterval); // Stop the game loop | |
| // Draw Game Over text | |
| ctx.fillStyle = "rgba(0, 0, 0, 0.75)"; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| ctx.fillStyle = "white"; | |
| ctx.font = "bold 36px 'Segoe UI'"; | |
| ctx.textAlign = "center"; | |
| ctx.fillText("GAME OVER", canvas.width / 2, canvas.height / 2 - 10); | |
| ctx.font = "18px 'Segoe UI'"; | |
| ctx.fillText("Press F5 to Restart", canvas.width / 2, canvas.height / 2 + 30); | |
| return; | |
| } | |
| // Clear the canvas for the next frame | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Render everything | |
| drawFood(); | |
| moveSnake(); | |
| snake.forEach(drawSnakePart); | |
| } | |
| // Listen for keyboard arrow navigation | |
| document.addEventListener("keydown", (event) => { | |
| // Prevent default scrolling behavior when pressing arrows | |
| if(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.key)) { | |
| event.preventDefault(); | |
| } | |
| const goingUp = dy === -gridSize; | |
| const goingDown = dy === gridSize; | |
| const goingRight = dx === gridSize; | |
| const goingLeft = dx === -gridSize; | |
| // Update direction (preventing 180-degree self-reversals) | |
| if (event.key === "ArrowLeft" && !goingRight) { | |
| dx = -gridSize; | |
| dy = 0; | |
| } | |
| if (event.key === "ArrowUp" && !goingDown) { | |
| dx = 0; | |
| dy = -gridSize; | |
| } | |
| if (event.key === "ArrowRight" && !goingLeft) { | |
| dx = gridSize; | |
| dy = 0; | |
| } | |
| if (event.key === "ArrowDown" && !goingUp) { | |
| dx = 0; | |
| dy = gridSize; | |
| } | |
| }); | |
| // Start the game | |
| placeFood(); | |
| gameInterval = setInterval(main, 100); // Game speed (100ms per frame) | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment