Node.js迷宫校验时条件不满足仍向数组重复push元素致死循环问题
Node.js迷宫可达性校验死循环问题修复
问题背景
我正在使用Node.js开发一款终端迷宫游戏,随机生成的迷宫包含以下元素:
const hat = '^'; const hole = 'O'; const fieldCharacter = '░'; const pathCharacter = '*';
玩家需要从起点出发穿越迷宫找到帽子,迷宫全量数据存储在randomArr变量中。由于迷宫为随机生成,可能出现帽子被洞穴完全包围、玩家无法抵达的无解情况,因此需要实现前置可达性校验逻辑,判断当前生成的迷宫是否存在通关路径。
现有代码运行时会陷入无限循环:逻辑设计层面,仅当相邻格子为可通行的场地字符、且该格子未被访问过时,才会将对应坐标推入visited访问记录数组,但实际运行时相同坐标会被反复推入数组,最终触发死循环。
现有循环逻辑的起始部分代码如下:
while (true) { //checking if next move is a winning move if (randomArr?.[p[0]]?.[p[1] + 1] === hat || randomArr?.[p[0]]?.[p[1] - 1] === hat || randomArr?.[p[0] + 1]?.[p[1]] === hat || randomArr?.[p[0] - 1]?.[p[1]] === hat) { console.table(visited, visitedTwice, visited3x, visited4x, visited5x); console.log(`solution found, reached postion ${p}`) return randomArr; } //checking if neighbouring square hasn't been visited, if no, go there else if (randomArr?.[p[0]]?.[p[1] + 1] === fieldCharacter && !visited.includes([ p[0], p[1] + 1 ])) { p = [p[0], p[1] + 1]; visited.push([p[0], p[1]]); //console.log(visited.includes([ p[0], p[1] + 1 ]), 0); //console.log(visited); } else if (randomArr?.[p[0]]?.[p[1] - 1] === fieldCharacter && !visited.includes([ p[0], p[1] - 1 ])) { p = [p[0], p[1] - 1]; visited.push([p[0], p[1]]); //console.log(visited.includes([ p[0], p[1] - 1 ]), 1); //console.log(visited); } else if (randomArr?.[p[0] + 1]?.[p[1]] === fieldCharacter && !visited.includes([ p[0] + 1, p[1] ])) { p = [p[0] + 1, p[1]]; visited.push([p[0], p[1]]); //console.log(!visited.includes([ p[0] + 1, p[1] ]), 2); //console.log(visited); } else if (randomArr?.[p[0] - 1]?.[p[1]] === fieldCharacter && !visited.includes([ p[0] - 1, p[1] ])) { p = [p[0] - 1, p[1]]; visited.push([p[0], p[1]]); //console.log(!visited.includes([ p[0] - 1, p[1] ]), 3); //console.log(visited);
运行时代码会反复触发前两个else if分支,持续向visited数组中添加重复坐标。
问题根因
死循环由两个核心bug直接导致:
- 访问判断逻辑完全失效:JavaScript中
Array.prototype.includes判断数组这类引用类型时,比对的是内存地址,而非数组内存储的值。你每次写!visited.includes([p[0], p[1]+1])时,都新建了一个独立的数组对象,哪怕坐标值和visited里已存的完全一致,判断结果永远是false,相当于“未被访问过”的校验根本没生效。 - 遍历逻辑没有回溯能力:现有写法是单路径向前探索,遇到岔路只会按固定优先级选第一个可走的方向,走到死路后没有回退到上一个岔路口选择其他方向的机制,只会在相邻两个可通行格子之间来回移动。
修复方案
1. 替换访问记录存储结构
放弃用嵌套数组存坐标,将坐标序列化为x,y格式的字符串存入Set,判断是否访问过时直接比对字符串值,从根源解决引用类型判断失效的问题:
// 初始化访问集合,提前存入起点坐标 const visited = new Set(); const startX = 0, startY = 0; // 替换为实际的玩家起点坐标 visited.add(`${startX},${startY}`);
2. 改用标准BFS实现可达性检测
迷宫可达性是经典的图搜索问题,不要用裸while(true)硬写遍历逻辑,直接用广度优先搜索(BFS)实现,逻辑简单且不会出现死循环:
function checkMazeValid(randomArr, hat, fieldCharacter, hole, startPos = [0, 0]) { const rowCount = randomArr.length; const colCount = randomArr[0].length; const visited = new Set(); // 队列存储待遍历的坐标 const searchQueue = [startPos]; visited.add(`${startPos[0]},${startPos[1]}`); // 四个探索方向:上、下、左、右 const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; while (searchQueue.length > 0) { const [currentX, currentY] = searchQueue.shift(); // 遍历四个相邻格子 for (const [dx, dy] of directions) { const nextX = currentX + dx; const nextY = currentY + dy; // 越界直接跳过 if (nextX < 0 || nextX >= rowCount || nextY < 0 || nextY >= colCount) continue; const posKey = `${nextX},${nextY}`; // 已访问过直接跳过 if (visited.has(posKey)) continue; const cellValue = randomArr[nextX][nextY]; // 找到帽子,说明迷宫有解 if (cellValue === hat) return true; // 是可通行的空地,加入队列待遍历 if (cellValue === fieldCharacter) { visited.add(posKey); searchQueue.push([nextX, nextY]); } } } // 所有可通行区域遍历完都没找到帽子,迷宫无解 return false; }
如果要在原有代码逻辑上修改,除了替换Set做访问记录外,还需要补全回溯逻辑:走到死胡同(四个方向都无法通行)时,要回退到上一个坐标,不能停在原地反复判断相邻格子,否则必然出现来回横跳的问题。
内容的提问来源于stack exchange,提问作者chylinski
相关产品推荐
相关产品推荐

