Spaces:
Running
Running
<!-- this was mostly written by Claude-3.5-Sonnet btw --> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Snake Game (WASD Controls, Larger Area, Obstacles, Slower Enemies)</title> | |
<style> | |
body { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
height: 100vh; | |
margin: 0; | |
background-color: #f0f0f0; | |
} | |
canvas { | |
border: 2px solid #333; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="gameCanvas" width="800" height="800"></canvas> | |
<script> | |
const canvas = document.getElementById('gameCanvas'); | |
const ctx = canvas.getContext('2d'); | |
const gridSize = 20; | |
const tileCount = canvas.width / gridSize; | |
let snake = [ | |
{x: 20, y: 20}, | |
]; | |
let food = {x: 15, y: 15}; | |
let dx = 0; | |
let dy = 0; | |
let score = 0; | |
let obstacles = []; | |
let enemies = []; | |
let enemyMoveCounter = 0; | |
function generateObstacles() { | |
obstacles = []; | |
for (let i = 0; i < 20; i++) { | |
obstacles.push({ | |
x: Math.floor(Math.random() * tileCount), | |
y: Math.floor(Math.random() * tileCount) | |
}); | |
} | |
} | |
function generateEnemies() { | |
enemies = []; | |
for (let i = 0; i < 3; i++) { | |
enemies.push({ | |
x: Math.floor(Math.random() * tileCount), | |
y: Math.floor(Math.random() * tileCount) | |
}); | |
} | |
} | |
generateObstacles(); | |
generateEnemies(); | |
function drawGame() { | |
clearCanvas(); | |
moveSnake(); | |
moveEnemies(); | |
drawSnake(); | |
drawFood(); | |
drawObstacles(); | |
drawEnemies(); | |
checkCollision(); | |
drawScore(); | |
} | |
function clearCanvas() { | |
ctx.fillStyle = 'white'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
} | |
function moveSnake() { | |
const head = {x: snake[0].x + dx, y: snake[0].y + dy}; | |
snake.unshift(head); | |
if (head.x === food.x && head.y === food.y) { | |
score++; | |
generateFood(); | |
} else { | |
snake.pop(); | |
} | |
} | |
function moveEnemies() { | |
enemyMoveCounter++; | |
if (enemyMoveCounter >= 2.5) { // Move enemies every 2.5 game loops (60% slower) | |
enemyMoveCounter = 0; | |
enemies.forEach(enemy => { | |
const head = snake[0]; | |
if (enemy.x < head.x) enemy.x++; | |
else if (enemy.x > head.x) enemy.x--; | |
if (enemy.y < head.y) enemy.y++; | |
else if (enemy.y > head.y) enemy.y--; | |
}); | |
} | |
} | |
function drawSnake() { | |
ctx.fillStyle = 'green'; | |
snake.forEach(segment => { | |
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 2, gridSize - 2); | |
}); | |
} | |
function drawFood() { | |
ctx.fillStyle = 'red'; | |
ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2); | |
} | |
function drawObstacles() { | |
ctx.fillStyle = 'gray'; | |
obstacles.forEach(obstacle => { | |
ctx.fillRect(obstacle.x * gridSize, obstacle.y * gridSize, gridSize - 2, gridSize - 2); | |
}); | |
} | |
function drawEnemies() { | |
ctx.fillStyle = 'purple'; | |
enemies.forEach(enemy => { | |
ctx.fillRect(enemy.x * gridSize, enemy.y * gridSize, gridSize - 2, gridSize - 2); | |
}); | |
} | |
function generateFood() { | |
do { | |
food.x = Math.floor(Math.random() * tileCount); | |
food.y = Math.floor(Math.random() * tileCount); | |
} while (isCollision(food) || isOnSnake(food) || isOnEnemy(food)); | |
} | |
function isCollision(pos) { | |
return obstacles.some(obstacle => obstacle.x === pos.x && obstacle.y === pos.y); | |
} | |
function isOnSnake(pos) { | |
return snake.some(segment => segment.x === pos.x && segment.y === pos.y); | |
} | |
function isOnEnemy(pos) { | |
return enemies.some(enemy => enemy.x === pos.x && enemy.y === pos.y); | |
} | |
function checkCollision() { | |
const head = snake[0]; | |
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) { | |
resetGame(); | |
return; | |
} | |
for (let i = 1; i < snake.length; i++) { | |
if (head.x === snake[i].x && head.y === snake[i].y) { | |
resetGame(); | |
return; | |
} | |
} | |
if (isCollision(head) || isOnEnemy(head)) { | |
resetGame(); | |
} | |
} | |
function resetGame() { | |
snake = [{x: 20, y: 20}]; | |
food = {x: 15, y: 15}; | |
dx = 0; | |
dy = 0; | |
score = 0; | |
enemyMoveCounter = 0; | |
generateObstacles(); | |
generateEnemies(); | |
generateFood(); | |
} | |
function drawScore() { | |
ctx.fillStyle = 'black'; | |
ctx.font = '20px Arial'; | |
ctx.fillText(`Score: ${score}`, 10, 30); | |
} | |
document.addEventListener('keydown', changeDirection); | |
function changeDirection(event) { | |
const key = event.key.toLowerCase(); | |
const goingUp = dy === -1; | |
const goingDown = dy === 1; | |
const goingRight = dx === 1; | |
const goingLeft = dx === -1; | |
if (key === 'a' && !goingRight) { | |
dx = -1; | |
dy = 0; | |
} | |
if (key === 'w' && !goingDown) { | |
dx = 0; | |
dy = -1; | |
} | |
if (key === 'd' && !goingLeft) { | |
dx = 1; | |
dy = 0; | |
} | |
if (key === 's' && !goingUp) { | |
dx = 0; | |
dy = 1; | |
} | |
} | |
function gameLoop() { | |
drawGame(); | |
setTimeout(gameLoop, 100); | |
} | |
gameLoop(); | |
</script> | |
</body> | |
</html> |