在JavaScript扫雷游戏的开发过程中,边界单元格的逻辑处理是核心环节之一。扫雷游戏的核心玩法需要计算某个单元格周围8个相邻单元格的雷数,而位于棋盘边缘、角落的边界单元格没有完整的8个相邻单元格,如果处理不当就会出现数组越界或者逻辑错误。

传统边界处理逻辑的问题
很多初学者在处理相邻单元格遍历时,会针对上、下、左、右、四个角落分别写条件判断,比如判断当前单元格是不是第一行、是不是最后一行、是不是第一列、是不是最后一列,再决定遍历哪些相邻单元格。这种方式会产生大量重复的条件判断代码,不仅冗长,还容易在修改时出现遗漏。
举个常见的传统写法示例,假设棋盘是一个二维数组board,行数为rows,列数为cols,当前单元格坐标为(x, y),计算周围雷数的传统逻辑如下:
// 传统边界处理逻辑示例
function countMinesTraditional(board, x, y, rows, cols) {
let count = 0;
// 判断上方单元格
if (x > 0) {
if (board[x-1][y] === 'mine') count++;
// 左上
if (y > 0 && board[x-1][y-1] === 'mine') count++;
// 右上
if (y < cols-1 && board[x-1][y+1] === 'mine') count++;
}
// 判断下方单元格
if (x < rows-1) {
if (board[x+1][y] === 'mine') count++;
// 左下
if (y > 0 && board[x+1][y-1] === 'mine') count++;
// 右下
if (y < cols-1 && board[x+1][y+1] === 'mine') count++;
}
// 判断左侧单元格
if (y > 0 && board[x][y-1] === 'mine') count++;
// 判断右侧单元格
if (y < cols-1 && board[x][y+1] === 'mine') count++;
return count;
}
上面的代码虽然能正确运行,但是条件判断分散在各个位置,如果后续要修改相邻单元格的判定规则,需要改动多个地方,维护成本很高。
优化方案一:使用偏移量数组统一遍历
我们可以预先定义一个偏移量数组,包含所有8个相邻单元格相对于当前单元格的坐标偏移值,然后遍历这个数组,对每个偏移后的坐标做合法性判断,过滤掉超出边界的坐标即可。这种方式可以把所有遍历逻辑统一起来,不需要针对边界写特殊判断。
优化后的代码示例如下:
// 偏移量数组,对应上、下、左、右、左上、右上、左下、右下
const directions = [
[-1, 0], [1, 0], [0, -1], [0, 1],
[-1, -1], [-1, 1], [1, -1], [1, 1]
];
function countMinesOptimized(board, x, y, rows, cols) {
let count = 0;
// 遍历所有偏移量
for (let [dx, dy] of directions) {
const newX = x + dx;
const newY = y + dy;
// 判断坐标是否在合法范围内
if (newX >= 0 && newX < rows && newY >= 0 && newY < cols) {
if (board[newX][newY] === 'mine') {
count++;
}
}
}
return count;
}
这种方式把边界判断逻辑统一成了一次坐标范围校验,代码量减少了近一半,逻辑也更清晰,后续如果要调整相邻单元格的范围,只需要修改directions数组即可。
优化方案二:封装坐标合法性判断函数
如果项目中多处需要判断坐标是否合法,可以把坐标校验的逻辑封装成独立的函数,进一步提升代码的复用性。比如我们可以封装一个isValidCoord函数,专门用来判断坐标是否在棋盘范围内。
// 封装坐标合法性判断函数
function isValidCoord(x, y, rows, cols) {
return x >= 0 && x < rows && y >= 0 && y < cols;
}
function countMinesWithHelper(board, x, y, rows, cols) {
let count = 0;
for (let [dx, dy] of directions) {
const newX = x + dx;
const newY = y + dy;
if (isValidCoord(newX, newY, rows, cols) && board[newX][newY] === 'mine') {
count++;
}
}
return count;
}
这样如果在其他地方也需要判断坐标是否合法,直接调用isValidCoord函数即可,避免了重复写条件判断代码。
优化方案三:提前过滤无效偏移
如果某些场景下偏移量是动态生成的,还可以在遍历偏移量之前,先根据当前单元格的位置提前过滤掉不可能有效的偏移,减少不必要的判断。比如当前单元格在第一行,那么所有dx为-1的偏移都可以直接跳过,不需要再做坐标校验。
function countMinesPreFilter(board, x, y, rows, cols) {
let count = 0;
for (let [dx, dy] of directions) {
// 提前过滤无效偏移
if (x + dx < 0 || x + dx >= rows) continue;
if (y + dy < 0 || y + dy >= cols) continue;
if (board[x+dx][y+dy] === 'mine') {
count++;
}
}
return count;
}
这种方式在棋盘非常大、遍历频率很高的情况下,能减少少量的判断开销,进一步提升性能。
优化效果对比
我们把几种方案的特点整理成表格,方便对比选择:
| 方案 | 代码简洁度 | 可维护性 | 适用场景 |
|---|---|---|---|
| 传统条件判断 | 低 | 低 | 逻辑简单的临时小项目 |
| 偏移量数组遍历 | 高 | 高 | 大多数扫雷游戏开发场景 |
| 封装校验函数 | 中 | 极高 | 大型项目、多处需要坐标校验的场景 |
| 提前过滤偏移 | 中 | 中 | 高性能要求的大棋盘场景 |
注意事项
在优化边界逻辑时,需要注意几个细节:首先,坐标的合法性判断一定要包含等于0和小于最大行/列数的情况,避免出现漏判;其次,偏移量数组的定义要准确,不要遗漏或者多写相邻方向;最后,如果棋盘的数据结构不是二维数组,而是其他形式,需要调整坐标校验的逻辑,适配对应的数据结构。
通过以上几种优化方式,我们可以把原本冗长易错的边界单元格处理逻辑,改成简洁、易维护的代码,不仅减少了bug出现的概率,也提升了扫雷游戏的整体代码质量。
JavaScript扫雷游戏边界单元格逻辑优化修改时间:2026-07-05 18:21:31