首页 > 人工智能 > 人工智能 > 贪吃蛇游戏走法最优解探讨

附件下载
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>经典贪吃蛇</title>
<style>
body {
font-family: 'Arial', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
margin: 0;
padding: 20px;
}
.game-container {
position: relative;
margin-top: 20px;
}
#game-board {
border: 3px solid #333;
border-radius: 5px;
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
background-color: #f0f0f0;
}
.score-panel {
display: flex;
justify-content: space-between;
width: 400px;
margin-bottom: 10px;
font-size: 18px;
font-weight: bold;
}
.controls {
margin-top: 20px;
text-align: center;
}
button {
padding: 10px 20px;
margin: 0 5px;
background: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
button:hover {
background: #45a049;
transform: translateY(-2px);
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.8);
color: white;
padding: 20px 40px;
border-radius: 10px;
text-align: center;
display: none;
}
</style>
</head>
<body>
<h1>经典贪吃蛇游戏</h1>
<div>
<div>得分: <span id="score">0</span></div>
<div>最高分: <span id="high-score">0</span></div>
<div>用时: <span id="etimes">0 m 0 s</span></div>
</div>
<div>
<canvas id="game-board" width="400" height="400"></canvas>
<div id="game-over">
<h2>游戏结束!</h2>
<p>你的得分: <span id="final-score">0</span></p>
<button id="restart-btn">重新开始</button>
</div>
</div>
<div>
<p>使用方向键或WASD控制蛇的移动</p>
<button id="start-btn">开始游戏</button>
<button id="pause-btn">暂停游戏</button>
</div>
<script>
const canvas = document.getElementById('game-board');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('score');
const etimesDisplay = document.getElementById('etimes');
const highScoreDisplay = document.getElementById('high-score');
const finalScoreDisplay = document.getElementById('final-score');
const gameOverDisplay = document.getElementById('game-over');
const startBtn = document.getElementById('start-btn');
const pauseBtn = document.getElementById('pause-btn');
const restartBtn = document.getElementById('restart-btn');
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
highScoreDisplay.textContent = highScore;
let snake = [];
let food = {};
let direction = 'right';
let nextDirection = 'right';
let gameSpeed = 5;
let gameLoop;
let isPaused = false;
let isRunning = false;
let dateb = new Date();
let datee = new Date();
//静默执行: YES NO
let silence = 'NO';
function initGame() {
snake = [
{x: 5, y: 10},
{x: 4, y: 10},
{x: 3, y: 10}
];
direction = 'right';
nextDirection = 'right';
score = 0;
scoreDisplay.textContent = score;
generateFood();
isPaused = false;
isRunning = true;
gameOverDisplay.style.display = 'none';
}
function generateFood() {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
// 确保食物不会生成在蛇身上
for (let segment of snake) {
if (segment.x === food.x && segment.y === food.y) {
return generateFood();
}
}
}
function drawGame() {
// 清空画布
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制蛇
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0 ? '#4CAF50' : '#8BC34A';
ctx.fillRect(snake[i].x * gridSize, snake[i].y * gridSize, gridSize-1, gridSize-1);
}
// 绘制食物
ctx.fillStyle = '#FF5722';
ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize-1, gridSize-1);
}
function updateGame() {
if (isPaused) return;
/*
最优策略:
1、在每个时间点,计算头和食物和尾保持通路且路径无交叉的所有路径
2、选择1结果集中的最短路径
3、重新生成食物后 若头和食物和尾之间无通路可走 则继续走第一步的路径 直到满足条件后循环继续1和2步
算法:
1、动态规划 ???
2、手工设计路径
*/
//console.log('direction=',direction,'food.x=',food.x,'food.y=',food.y,'head.x=',snake[0].x,'head.y=',snake[0].y,'snake.length=',snake.length);
if ( direction === 'right' ) {
if ( snake[0].x === tileCount-1) {
nextDirection = 'down';
}
}
else if ( direction === 'left' ) {
if ( snake[0].x === 1 && snake[0].y === tileCount-1 ) {
nextDirection = 'left';
}
else if ( snake[0].x === 0 && snake[0].y === tileCount-1 ) {
nextDirection = 'up';
}
else if ( snake[0].x === 1 ) {
nextDirection = 'down';
}
}
else if ( direction === 'up' ) {
if ( snake[0].x === 0 && snake[0].y === 0 ) {
nextDirection = 'right';
}
}
else if ( direction === 'down' ) {
if ( snake[0].x === tileCount-1 && snake[0].y === tileCount-1 ) {
nextDirection = 'left';
}
else if ( snake[0].x === 1 ) {
//when tail like C : nextDirection = 'right';
if ( snake[snake.length-1].x === 0 || snake[snake.length-1].y === 0 || snake[snake.length-1].y === tileCount-1 ) {
if ( food.x >= 1 && food.x <= tileCount-1 && food.y >= snake[0].y ) {
if ( food.y === snake[0].y ) {
nextDirection = 'right';
}
else {
if ( food.y % 2 === 1 ) {
if ( food.y === snake[0].y + 1) {
nextDirection = 'right';
}
}
}
}
else {
//nextDirection = 'right';
if ( snake[0].y === tileCount-2 ) {
nextDirection = 'right';
}
else {
if ( snake.length >= 200 ) {
if ( snake[0].y % 2 === 0 ) {
nextDirection = 'right';
}
}
}
}
}
else {
if ( snake.length >= 80 ) {
if ( snake[0].y % 2 === 0 ) {
nextDirection = 'right';
}
}
else {
if ( food.x >= 1 && food.x <= tileCount-1 && food.y >= snake[0].y ) {
if ( food.y === snake[0].y ) {
nextDirection = 'right';
}
else {
if ( food.y % 2 === 1 ) {
if ( food.y === snake[0].y + 1) {
nextDirection = 'right';
}
}
}
}
else {
//nextDirection = 'right';
if ( snake[0].y === tileCount-2 ) {
nextDirection = 'right';
}
}
}
}
}
else if ( snake[0].x === tileCount-1 ) {
////when tail like C : nextDirection = 'left';
if ( snake[snake.length-1].x === 0 || snake[snake.length-1].y === 0 || snake[snake.length-1].y === tileCount-1 ) {
if ( food.x >= 1 && food.x <= tileCount-1 && food.y >= snake[0].y ) {
if ( food.y === snake[0].y ) {
nextDirection = 'left';
}
else {
if ( food.y % 2 === 0 ) {
if ( food.y === snake[0].y + 1) {
nextDirection = 'left';
}
}
}
}
else {
//nextDirection = 'left';
if ( snake[0].y === tileCount-1 ) {
nextDirection = 'left';
}
else {
if ( snake.length >= 200 ) {
if ( snake[0].y % 2 === 1 ) {
nextDirection = 'left';
}
}
}
}
}
else {
if ( snake.length >= 80 ) {
if ( snake[0].y % 2 === 1 ) {
nextDirection = 'left';
}
}
else {
if ( food.x >= 1 && food.x <= tileCount-1 && food.y >= snake[0].y ) {
if ( food.y === snake[0].y ) {
nextDirection = 'left';
}
else {
if ( food.y % 2 === 0 ) {
if ( food.y === snake[0].y + 1) {
nextDirection = 'left';
}
}
}
}
else {
//nextDirection = 'left';
if ( snake[0].y === tileCount-1 ) {
nextDirection = 'left';
}
}
}
}
}
}
if ( direction !== nextDirection ) {
if ( silence === 'NO' ) {
console.log('direction=',direction,'food.x=',food.x,'food.y=',food.y,'head.x=',snake[0].x,'head.y=',snake[0].y,'snake.length=',snake.length);
}
}
// 更新蛇的方向
direction = nextDirection;
// 计算蛇头新位置
const head = {x: snake[0].x, y: snake[0].y};
switch (direction) {
case 'up':
head.y--;
break;
case 'down':
head.y++;
break;
case 'left':
head.x--;
break;
case 'right':
head.x++;
break;
}
// 检查碰撞
if (
head.x < 0 || head.x >= tileCount ||
head.y < 0 || head.y >= tileCount ||
checkCollision(head)
) {
gameOver();
return;
}
// 移动蛇
snake.unshift(head);
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
score++;
scoreDisplay.textContent = score;
// 检查通关
if ( score === 397 ) {
gameOver();
return;
}
generateFood();
// 输出用时
datee = new Date();
datee.getTime();
let diff = datee - dateb;
//console.log('dateb=',dateb,'datee=',datee,'diff=',diff);
etimesDisplay.textContent = Math.floor((diff/1000) / 60) + ' m ' + Math.floor((diff/1000) % 60) + ' s';
// 随着分数增加加快游戏速度
if (score % 5 === 0 && gameSpeed > 50) {
gameSpeed -= 10;
clearInterval(gameLoop);
gameLoop = setInterval(gameStep, gameSpeed);
}
} else {
snake.pop();
}
}
function checkCollision(head) {
for (let i = 0; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
return true;
}
}
return false;
}
function gameOver() {
clearInterval(gameLoop);
isRunning = false;
if (score > highScore) {
highScore = score;
highScoreDisplay.textContent = highScore;
localStorage.setItem('snakeHighScore', highScore);
}
finalScoreDisplay.textContent = score;
gameOverDisplay.style.display = 'block';
}
function gameStep() {
updateGame();
//drawGame();
if ( silence === 'NO' ) {
drawGame();
}
}
function startGame() {
if (isRunning) return;
dateb = new Date();
dateb.getTime();
initGame();
gameLoop = setInterval(gameStep, gameSpeed);
}
function togglePause() {
if (!isRunning) return;
isPaused = !isPaused;
pauseBtn.textContent = isPaused ? '继续游戏' : '暂停游戏';
}
// 事件监听
document.addEventListener('keydown', (e) => {
if (!isRunning) return;
switch (e.key) {
case 'ArrowUp':
case 'w':
case 'W':
if (direction !== 'down') nextDirection = 'up';
break;
case 'ArrowDown':
case 's':
case 'S':
if (direction !== 'up') nextDirection = 'down';
break;
case 'ArrowLeft':
case 'a':
case 'A':
if (direction !== 'right') nextDirection = 'left';
break;
case 'ArrowRight':
case 'd':
case 'D':
if (direction !== 'left') nextDirection = 'right';
break;
case ' ':
togglePause();
break;
}
});
startBtn.addEventListener('click', startGame);
pauseBtn.addEventListener('click', togglePause);
restartBtn.addEventListener('click', startGame);
// 初始绘制
drawGame();
</script>
</body>
</html>
后缀改成html后执行 最优策略暂时没研究明白,附件用的 if else 法,通关时间3分多
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/28692050/viewspace-3107770/ ,如需转载,请注明出处,否则将追究法律责任。