基于bfs算法的自动寻路贪吃蛇(js+canvas)

本文介绍了如何使用BFS算法优化贪吃蛇游戏中的路径查找,包括广度优先搜索策略和追尾及虚拟蛇的概念。通过确保蛇头与蛇尾之间的通路,避免了蛇撞墙的问题,同时通过优先远离食物的策略进一步优化了蛇的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

演示:

演示地址:

源码:https://github.com/zm2001/aiSnake.git

aiSnake.zip - 蓝奏云

准备工作:

首先自己找一个可操控的js+canvas的贪吃蛇小游戏,网上相关代码很多不做过多介绍。

BFS算法(核心):

广度优先算法(Breadth-First-Search),简称BFS。

BFS算法从问题的初始状态(起点)出发,搜索起点周围的节点直到目标结点。可参考b站教程:BFS广搜解决迷宫问题_哔哩哔哩_bilibili

相关代码:地图map是一个二维数组,其中0表示可移动的空位,1表示蛇身。start是起始节点;end为目标节点。

首先把食物的坐标放入队列,只要队列非空就把队头出队,然后把蛇头周围的4个点放入队列,不断地循环操作,直到覆盖整个地图。在遍历过程中应该注意:1.蛇的身体和墙不能访问。2.为了提高效率,有重合的格子如果被访问过就不用再访问了。当整个循环结束后,我们就获得了蛇头与食物之间的最短路径。

// BFS路径查找函数

function bfs(map, start, end) {

    // queue 用于存储待处理的节点

    const queue = [];

    // visited 用于记录已经访问过的节点。

    const visited = new Set();

    // directions 包含四个可能的移动方向:上、下、左、右,每个方向表示为一个对象 {x, y}。

    const directions = [{ x: 0, y: -1 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 1, y: 0 }];



    // 将起点 start 放入队列 queue 中,并初始化一个空路径 path,然后将起点坐标标记为已访问,

    // 存入集合 visited 中。这个路径 path 是一个数组,用于记录从起点到当前节点的路径。

    queue.push({ node: start, path: [] });

    visited.add(`${start.x},${start.y}`);



    while (queue.length > 0) {

        // 从队列 queue 中取出队首节点 {node, path},其中 node 表示当前节点,

        // path 表示从起点到当前节点的路径。

        const { node, path } = queue.shift();



        // 检查当前节点 node 是否为终点 end,如果是,则说明找到了最短路径,直接返回 path。

        if (node.x === end.x && node.y === end.y) {

            return [...path, end]

        }

        // 遍历四个可能的移动方向,分别计算下一个节点的坐标 nextX 和 nextY。

        // 检查下一个节点是否在地图内(坐标在合法范围内)且未被访问过(visited 集合中没有记录)

        // 且不是蛇身(map[nextY][nextX] !== 1)。

        // 如果满足上述条件,将下一个节点 {x: nextX, y: nextY} 加入队列 queue,同时将当前路径

        // path 的拷贝(使用 path: [...path, node])加入到新节点的路径中,并将下一个节点坐标标

        // 记为已访问。

        for (const dir of directions) {

            const nextX = node.x + dir.x;

            const nextY = node.y + dir.y;

            const nextPos = `${nextX},${nextY}`;



            if (

                nextX >= 0 &&

                nextX < map[0].length &&

                nextY >= 0 &&

                nextY < map.length &&

                !visited.has(nextPos) &&

                map[nextY][nextX] !== 1

            ) {

                queue.push({ node: { x: nextX, y: nextY }, path: [...path, node] });

                visited.add(nextPos);

            }

        }

    }

    return [];

}

此时我们让小蛇去吃食物,但是当我们运行一段时间过后会发现小蛇很快就撞墙:

仔细研究会发现当小蛇吃完食物后并不能找到蛇头与食物之间的路径,此时小蛇会陷入迷茫状态,一直沿着一个方向冲过去。因此我们需要优化策略让小蛇安全的去吃食物。

追尾+虚拟蛇探路:

      首先我们应该为“安全”定义,什么是安全状态?我们发现蛇头移动一格对应蛇尾的位置就移出一格,所以只要蛇头能跟着蛇尾走那么就是安全状态,因此当小蛇吃完食物之后能检查到蛇头与蛇尾之间有通路即为安全。

通过以上结论,我们可以在小蛇找不到吃食物的路径时,让小蛇跟着蛇尾走。当小蛇可以吃到食物时,先创建一个虚拟蛇模拟小蛇使用bfs算法沿着最短路径去吃掉食物,当虚拟蛇吃完食物之后可以找到蛇头与蛇尾之间的通路时,此时吃完为安全状态,于是我们再派真实的小蛇去吃食物。反之当虚拟蛇吃完食物之后发现找不到连接尾部的通路时,此时吃完为不安全状态,我们让真蛇跟着蛇尾走一步。直到虚拟蛇找到安全的状态再让真实的小蛇移动。

以下是伪代码:


我们根据这个策略进行优化后可以看到,小蛇虽然不会死但是一直围绕蛇尾转圈:

优先远离食物追尾:

我们再次分析会发现当蛇头沿着蛇尾走时会在路上留下许多空隙,这些空隙导致蛇头并不能找到一个安全的路径去吃食物,因此我们还需要去优化蛇头跟着蛇尾走的策略。我们应当在蛇头去找蛇尾时尽量不与蛇身产生空隙。

因此让真蛇去找自己的尾巴时需要满足两点:

1.在蛇头的四周找一个空位,蛇头走到这个空位后任然可以找到连接尾部的通路。

2.找到所有满足条件1的情况下,优先选择距离食物最远的一格。(计算两个点之间的曼哈顿距离)

当我们优化完策略之后:

<think>好的,我现在需要帮助用户解决在JavaScript中实现贪吃蛇自动寻路算法的问题。首先,我需要理解用户的需求。用户提到了参考的引用内容,其中涉及到DFS、BFS以及遇到的路径寻找问题,比如死循环或无法找到路径的情况。因此,我需要结合这些信息,提供一个有效的解决方案。 首先,我应该回顾用户提供的参考资料。引用[1]提到之前用DFS算法但存在局限性,引用[2]讨论了DFS的优化问题,引用[3]则提到BFS算法在吃完食物后可能无法找到路径的情况。这说明用户可能尝试过DFS和BFS,但遇到了问题,需要更好的算法或优化方法。 接下来,我需要确定常用的自动寻路算法。除了DFS和BFS,还有A*算法,它在路径寻找中效率较高,适合这种场景。另外,用户的问题可能涉及到路径的动态变化,比如蛇的身体会不断变长,路径需要实时更新。 然后,考虑如何实现BFS算法BFS可以找到最短路径,但需要处理蛇头移动后的身体阻挡问题。需要确保路径中存在有效路径,并且在移动过程中动态更新障碍物(即蛇的身体)。如果BFS找不到路径,可能需要一个备用策略,比如沿墙移动或寻找最长路径,避免蛇被困住。 另外,用户提到的DFS的问题,比如无法吃满全屏,可能是因为DFS倾向于深度搜索,容易陷入局部最优,而BFS更适合寻找最短路径,但需要处理状态记录,防止重复搜索。引用[3]中提到BFS吃完食物后找不到路径,可能是因为蛇的身体成为障碍,导致路径被阻断。这时候可能需要优化路径策略,比如在找不到路径时让蛇绕路或安全移动。 最后,我需要将算法JavaScript实现,并结合贪吃蛇游戏逻辑,比如使用Canvas绘制,处理键盘事件,更新蛇的位置等。代码需要结构清晰,注释明确,方便用户理解和调整。 总结步骤: 1. 确定使用BFS作为主要寻路算法,因为它能找到最短路径。 2. 处理蛇身体的动态障碍,每次移动更新障碍物位置。 3. 当BFS找不到路径时,使用备用策略(如远离食物,寻找安全路径)。 4. 实现JavaScript代码,包括游戏循环、绘制和算法部分。 5. 测试算法,确保蛇能有效找到路径,避免死循环。 可能遇到的问题包括路径计算的效率,以及如何处理蛇身动态变化带来的障碍。需要确保BFS队列的有效管理,避免内存泄漏或性能问题。另外,蛇的移动速度和算法的执行频率需要协调,防止延迟。</think>以下是用JavaScript实现贪吃蛇自动寻路算法的分步解决方案: ### 一、基础算法选择 推荐使用BFS(广度优先搜索)算法作为核心寻路方法,因其能保证找到最短路径且避免DFS的局限性[^3]。基础实现步骤如下: 1. **创建地图模型**: ```javascript const gridSize = 20; // 每个网格的像素尺寸 const map = Array(gridSize).fill().map(() => Array(gridSize).fill(0)); // 0=空 1=障碍 ``` 2. **BFS核心算法**: ```javascript function bfs(start, target, obstacles) { const queue = [[start]]; const visited = new Set([`${start.x},${start.y}`]); while (queue.length > 0) { const path = queue.shift(); const current = path[path.length-1]; if (current.x === target.x && current.y === target.y) { return path; } // 四个方向探索 const directions = [{x:1,y:0}, {x:-1,y:0}, {x:0,y:1}, {x:0,y:-1}]; for (const dir of directions) { const next = {x: current.x + dir.x, y: current.y + dir.y}; const key = `${next.x},${next.y}`; if (!isValid(next) || visited.has(key) || obstacles.has(key)) continue; visited.add(key); queue.push([...path, next]); } } return null; // 无可用路径 } ``` ### 二、路径优化策略 针对引用[3]中提到的吃完食物后找不到路径问题,采用双重检测机制: 1. **预判路径可行性**: ```javascript function canReach(snakeHead, food, snakeBody) { // 临时移除蛇尾进行路径检测 const tempBody = new Set([...snakeBody].slice(0, -1)); return bfs(snakeHead, food, tempBody) !== null; } ``` 2. **安全移动策略**: 当无法找到路径时,选择使蛇头远离食物的移动方向: ```javascript function findSafeMove(snakeHead, food) { const dx = snakeHead.x - food.x; const dy = snakeHead.y - food.y; return (Math.abs(dx) > Math.abs(dy)) ? (dx > 0 ? 'RIGHT' : 'LEFT') : (dy > 0 ? 'DOWN' : 'UP'); } ``` ### 三、完整实现框架 ```javascript class AutoSnake { constructor() { this.snake = [{x:10, y:10}]; this.food = this.generateFood(); this.direction = 'RIGHT'; } update() { const path = this.findPath(); if (path && path.length >= 2) { this.direction = this.getDirection(path[0], path[1]); } this.moveSnake(); } findPath() { const obstacles = new Set( this.snake.slice(1).map(p => `${p.x},${p.y}`) ); let path = bfs(this.snake[0], this.food, obstacles); if (!path || !this.willPathBeValid(path)) { return this.findSafePath(); } return path; } getDirection(current, next) { const dx = next.x - current.x; const dy = next.y - current.y; return dx === 1 ? 'RIGHT' : dx === -1 ? 'LEFT' : dy === 1 ? 'DOWN' : 'UP'; } } ``` ### 四、性能优化技巧 1. **方向优先级调整**:根据当前运动方向优先检测同向路径 2. **路径缓存**:对已探索的有效路径建立缓存机制 3. **增量更新**:只更新蛇身移动后的变化区域
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值