console
<!DOCTYPE html>
<html>
<head>
<title>智能贪吃蛇</title>
<style>
body { display: flex; flex-direction: column; align-items: center; background-color: #2c3e50; font-family: Arial; margin: 0; min-height: 100vh; }
#game-container { position: relative; margin-top: 20px; }
#gameCanvas { border: 3px solid #ecf0f1; border-radius: 5px; background-color: #34495e; }
.control-btn { position: absolute; padding: 15px 30px; font-size: 1.2em; background-color: #27ae60; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s; transform: translate(-50%, -50%); z-index: 1; }
#autoBtn { top: 50%; left: 50%; }
#restartBtn { display: none; top: 60%; left: 50%; }
#score { color: #ecf0f1; font-size: 2em; margin: 20px 0; }
.game-over { color: #e74c3c !important; }
</style>
</head>
<body>
<div id="score">得分: 0</div>
<div id="game-container">
<canvas id="gameCanvas" width="400" height="400"></canvas>
<button id="autoBtn" class="control-btn">开始自动游戏</button>
<button id="restartBtn" class="control-btn">重新开始</button>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const autoBtn = document.getElementById('autoBtn');
const restartBtn = document.getElementById('restartBtn');
const GRID_SIZE = 20;
const TILE_COUNT = canvas.width / GRID_SIZE;
const GAME_SPEED = 100;
let snake = [];
let food = { x: 0, y: 0 };
let dx = 0;
let dy = 0;
let score = 0;
let lastTime = 0;
let isPlaying = false;
let isPaused = false;
let gameOver = false;
let isAutoMode = false;
function initGame() {
snake = [{ x: 10, y: 10 }];
dx = 1;
dy = 0;
score = 0;
scoreElement.textContent = `得分: ${score}`;
scoreElement.classList.remove('game-over');
generateFood();
isPlaying = true;
isPaused = false;
gameOver = false;
isAutoMode = true;
}
function generateFood() {
do {
food.x = Math.floor(Math.random() * TILE_COUNT);
food.y = Math.floor(Math.random() * TILE_COUNT);
} while (snake.some(s => s.x === food.x && s.y === food.y));
}
function autoMove() {
const safeDirs = getSafeDirections();
if (safeDirs.length === 0) return;
const foodDirs = safeDirs.filter(dir =>
canReachFood(snake[0].x + dir.dx, snake[0].y + dir.dy)
);
if (foodDirs.length > 0) {
foodDirs.sort((a, b) => {
const distA = Math.abs(snake[0].x + a.dx - food.x) +
Math.abs(snake[0].y + a.dy - food.y);
const distB = Math.abs(snake[0].x + b.dx - food.x) +
Math.abs(snake[0].y + b.dy - food.y);
return distA - distB;
});
dx = foodDirs[0].dx;
dy = foodDirs[0].dy;
return;
}
const spaceDirs = safeDirs.map(dir => ({
dir,
space: calculateSpace(snake[0].x + dir.dx, snake[0].y + dir.dy)
})).sort((a, b) => b.space - a.space);
dx = spaceDirs[0].dir.dx;
dy = spaceDirs[0].dir.dy;
}
function calculateSpace(x, y) {
const visited = new Set();
const queue = [{x, y}];
const virtualSnake = [{x, y}, ...snake.slice(0, -1)];
while (queue.length > 0) {
const current = queue.shift();
const key = `${current.x},${current.y}`;
if (visited.has(key)) continue;
visited.add(key);
for (const dir of [{dx:1,dy:0}, {dx:-1,dy:0}, {dx:0,dy:1}, {dx:0,dy:-1}]) {
const next = {
x: current.x + dir.dx,
y: current.y + dir.dy
};
if (next.x >= 0 && next.x < TILE_COUNT &&
next.y >= 0 && next.y < TILE_COUNT &&
!virtualSnake.some(s => s.x === next.x && s.y === next.y) &&
!visited.has(`${next.x},${next.y}`)) {
queue.push(next);
}
}
}
return visited.size;
}
function canReachFood(x, y) {
const visited = new Set();
const queue = [{x, y}];
const virtualSnake = [{x, y}, ...snake.slice(0, -1)];
while (queue.length > 0) {
const current = queue.shift();
if (current.x === food.x && current.y === food.y) return true;
const key = `${current.x},${current.y}`;
if (visited.has(key)) continue;
visited.add(key);
for (const dir of [{dx:1,dy:0}, {dx:-1,dy:0}, {dx:0,dy:1}, {dx:0,dy:-1}]) {
const next = {
x: current.x + dir.dx,
y: current.y + dir.dy
};
if (next.x >= 0 && next.x < TILE_COUNT &&
next.y >= 0 && next.y < TILE_COUNT &&
!virtualSnake.some(s => s.x === next.x && s.y === next.y) &&
!visited.has(`${next.x},${next.y}`)) {
queue.push(next);
}
}
}
return false;
}
function getSafeDirections() {
return [
{dx:1, dy:0}, {dx:-1, dy:0},
{dx:0, dy:1}, {dx:0, dy:-1}
].filter(dir => {
if (dir.dx === -dx && dir.dy === -dy) return false;
const newX = snake[0].x + dir.dx;
const newY = snake[0].y + dir.dy;
if (newX < 0 || newX >= TILE_COUNT ||
newY < 0 || newY >= TILE_COUNT) return false;
return !snake.slice(0, -1).some(s =>
s.x === newX && s.y === newY
);
});
}
function gameLoop(timestamp) {
if (!isPlaying) return;
const delta = timestamp - lastTime;
if (delta > GAME_SPEED) {
lastTime = timestamp;
if (!isPaused && !gameOver) {
if (isAutoMode) autoMove();
const newHead = {
x: snake[0].x + dx,
y: snake[0].y + dy
};
if (newHead.x < 0 || newHead.x >= TILE_COUNT ||
newHead.y < 0 || newHead.y >= TILE_COUNT) {
endGame('撞墙了!');
return;
}
if (snake.some(s => s.x === newHead.x && s.y === newHead.y)) {
endGame('咬到自己了!');
return;
}
snake.unshift(newHead);
if (newHead.x === food.x && newHead.y === food.y) {
score += 10;
scoreElement.textContent = `得分: ${score}`;
generateFood();
} else {
snake.pop();
}
}
draw();
}
requestAnimationFrame(gameLoop);
}
function endGame(msg) {
gameOver = true;
isPlaying = false;
scoreElement.textContent = `${msg} 得分: ${score}`;
scoreElement.classList.add('game-over');
restartBtn.style.display = 'block';
}
function draw() {
ctx.fillStyle = '#34495e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#f1c40f';
ctx.fillRect(food.x*GRID_SIZE, food.y*GRID_SIZE, GRID_SIZE-2, GRID_SIZE-2);
snake.forEach((seg, i) => {
const centerX = seg.x*GRID_SIZE + GRID_SIZE/2;
const centerY = seg.y*GRID_SIZE + GRID_SIZE/2;
if (i === 0) {
ctx.save();
ctx.translate(centerX, centerY);
let angle = 0;
if (dx === 1) angle = 0;
else if (dx === -1) angle = Math.PI;
else if (dy === -1) angle = -Math.PI/2;
else if (dy === 1) angle = Math.PI/2;
ctx.rotate(angle);
const grad = ctx.createLinearGradient(-8, -8, 8, 8);
grad.addColorStop(0, '#ff0000');
grad.addColorStop(1, '#cc0000');
ctx.beginPath();
ctx.moveTo(8, 0);
ctx.lineTo(-8,8);
ctx.lineTo(-8,-8);
ctx.closePath();
ctx.fillStyle = grad;
ctx.fill();
ctx.restore();
} else {
const hue = (i*35)%360;
const grad = ctx.createRadialGradient(centerX, centerY, GRID_SIZE/4, centerX, centerY, GRID_SIZE/2);
grad.addColorStop(0, `hsl(${hue},100%,70%)`);
grad.addColorStop(1, `hsl(${hue},100%,40%)`);
ctx.beginPath();
ctx.arc(centerX, centerY, GRID_SIZE/2-2, 0, Math.PI*2);
ctx.fillStyle = grad;
ctx.fill();
}
});
if (isPaused) {
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '30px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(' 游戏暂停(空格键继续)', canvas.width/2, canvas.height/2);
}
}
autoBtn.addEventListener('click', () => {
autoBtn.style.display = 'none';
restartBtn.style.display = 'none';
initGame();
gameLoop(0);
});
restartBtn.addEventListener('click', () => {
autoBtn.style.display = 'none';
restartBtn.style.display = 'none';
initGame();
gameLoop(0);
});
document.addEventListener('keydown', (e) => {
if (!isPlaying) return;
if (e.key === ' ') {
e.preventDefault();
if (!gameOver) isPaused = !isPaused;
}
});
draw();
autoBtn.style.display = 'block';
</script>
</body>
</html>