Files
2026-05-28 18:44:58 +03:00

136 lines
3.8 KiB
HTML

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Змейка</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #1a1a2e;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
font-family: 'Segoe UI', sans-serif;
color: #eee;
}
h1 {
margin-bottom: 10px;
font-size: 2.2em;
color: #0f0;
text-shadow: 0 0 20px #0f0;
}
#score { font-size: 1.3em; margin-bottom: 10px; }
canvas {
border: 2px solid #0f0;
border-radius: 8px;
box-shadow: 0 0 30px rgba(0, 255, 0, 0.3);
}
#message {
margin-top: 15px;
font-size: 1.1em;
color: #aaa;
}
</style>
</head>
<body>
<h1>🐍 Змейка</h1>
<div id="score">Счёт: 0</div>
<canvas id="game" width="400" height="400"></canvas>
<div id="message">Стрелки для управления | Пробел — рестарт</div>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const size = 20;
const cols = canvas.width / size;
const rows = canvas.height / size;
let snake, food, dir, nextDir, score, gameOver, interval;
function init() {
snake = [{ x: 10, y: 10 }];
dir = { x: 1, y: 0 };
nextDir = { x: 1, y: 0 };
score = 0;
gameOver = false;
scoreEl.textContent = 'Счёт: 0';
placeFood();
clearInterval(interval);
interval = setInterval(update, 120);
}
function placeFood() {
while (true) {
food = { x: Math.floor(Math.random() * cols), y: Math.floor(Math.random() * rows) };
if (!snake.some(s => s.x === food.x && s.y === food.y)) break;
}
}
function update() {
if (gameOver) return;
dir = { ...nextDir };
const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
if (head.x < 0 || head.x >= cols || head.y < 0 || head.y >= rows ||
snake.some(s => s.x === head.x && s.y === head.y)) {
gameOver = true;
draw();
return;
}
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score += 10;
scoreEl.textContent = 'Счёт: ' + score;
placeFood();
} else {
snake.pop();
}
draw();
}
function draw() {
ctx.fillStyle = '#16213e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
snake.forEach((s, i) => {
const g = ctx.createRadialGradient(s.x * size + size/2, s.y * size + size/2, 0, 0, 0, size);
g.addColorStop(0, i === 0 ? '#0f0' : '#0a0');
g.addColorStop(1, i === 0 ? '#0a0' : '#050');
ctx.fillStyle = g;
ctx.fillRect(s.x * size + 1, s.y * size + 1, size - 2, size - 2);
});
ctx.fillStyle = '#ff0040';
ctx.shadowColor = '#ff0040';
ctx.shadowBlur = 15;
ctx.beginPath();
ctx.arc(food.x * size + size/2, food.y * size + size/2, size/2 - 2, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
if (gameOver) {
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ff0040';
ctx.font = 'bold 36px Segoe UI';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width/2, canvas.height/2 - 10);
ctx.fillStyle = '#fff';
ctx.font = '20px Segoe UI';
ctx.fillText('Счёт: ' + score, canvas.width/2, canvas.height/2 + 25);
}
}
document.addEventListener('keydown', e => {
const key = e.key;
if (key === 'ArrowUp' && dir.y !== 1) nextDir = { x: 0, y: -1 };
if (key === 'ArrowDown' && dir.y !== -1) nextDir = { x: 0, y: 1 };
if (key === 'ArrowLeft' && dir.x !== 1) nextDir = { x: -1, y: 0 };
if (key === 'ArrowRight' && dir.x !== -1) nextDir = { x: 1, y: 0 };
if (key === ' ') init();
e.preventDefault();
});
init();
</script>
</body>
</html>