本文介绍一种健壮、可复用的 TypeScript 方法,用于统计 10×10 网格中某单元格周围(8 邻域)值为 2 的相邻格子数量,特别解决边界越界导致计数错误的问题。写扫雷类游戏的时候,经常要处理一个经典需求:给定一个坐标 (rowIndex, colIndex),快速统计它周围8个方向(上下左
本文介绍一种健壮、可复用的 TypeScript 方法,用于统计 10×10 网格中某单元格周围(8 邻域)值为 2 的相邻格子数量,特别解决边界越界导致计数错误的问题。
写扫雷类游戏的时候,经常要处理一个经典需求:给定一个坐标 (rowIndex, colIndex),快速统计它周围8个方向(上下左右加四个对角线)里值为2的格子个数——2一般代表冲击波。如果你一开始想到的是手写 if 条件逐个判断rowIndex-1、colIndex+1……那恭喜你,马上就会踩到边界处理的坑。网格边缘和角落的漏判、越界访问导致的 undefined 比较错误,几乎是新手必跳的陷阱。
其实有一个更优雅的思路:先安全提取所有邻域元素,再做过滤计数。这不仅让代码可读性翻倍,也从根本上解决了边界问题。下面直接上干货。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
先看这个 getNeighbors 函数,它通过清晰的范围计算,自动跳过自身位置和越界索引:
function getNeighbors( rowIndex: number, colIndex: number, source: Array [], radius: number = 1,): Array { const neighbors: T[] = []; if (!source.length) return neighbors; const numRows = source.length; const numCols = source[0].length; // 预计算有效行列范围,避免重复条件判断 const minRow = Math.max(0, rowIndex - radius); const maxRow = Math.min(numRows - 1, rowIndex + radius); const minCol = Math.max(0, colIndex - radius); const maxCol = Math.min(numCols - 1, colIndex + radius); for (let r = minRow; r <= maxRow; r++) { for (let c = minCol; c <= maxCol; c++) { // 跳过中心点自身 if (r !== rowIndex || c !== colIndex) { neighbors.push(source[r][c]); } } } return neighbors;}
这个实现的好处很明显:
Math.max/min 卡住循环范围,不管网格多大都不用担心。radius 参数可以自由调整——比如要统计3×3区域内所有2,直接传 radius=2 就行。 让返回数组类型和源数组保持一致,省去类型断言。提取到邻域列表后,算冲击波个数就只剩一行代码了:
const bombCount = getNeighbors(rowIndex, colIndex, grid) .filter(value => value === 2) .length;
注意:在 React 这类响应式环境里,要确保
grid是最新状态——比如在useCallback的依赖项里包含grid,防止闭包抓到旧值。
如果你的场景明确限定为标准的8邻域,并且对执行效率有极致要求(比如每帧调用成千上万次),那可以用硬编码偏移 + 可选链(.)的方案。零循环,时间复杂度 O(1):
function get8Neighbors(rowIndex: number, colIndex: number, grid: Array []): T[] { return [ grid[rowIndex - 1]?.[colIndex - 1], grid[rowIndex - 1]?.[colIndex], grid[rowIndex - 1]?.[colIndex + 1], grid[rowIndex]?.[colIndex - 1], grid[rowIndex]?.[colIndex + 1], grid[rowIndex + 1]?.[colIndex - 1], grid[rowIndex + 1]?.[colIndex], grid[rowIndex + 1]?.[colIndex + 1], ].filter((v): v is T => v !== undefined); // 类型守卫,剔除 undefined}// 使用const bombCount = get8Neighbors(rowIndex, colIndex, grid).filter(v => v === 2).length;
这个版本利用可选链安全访问所有8个位置,filter 一步剔除 undefined,语义明确,性能拉满。
| 方案 | 适用场景 | 边界安全性 | 可读性 | 扩展性 |
|---|---|---|---|---|
| 通用 getNeighbors | 主流推荐,兼顾鲁棒与清晰 | 完美 | 高 | 支持任意 radius |
| 硬编码 get8Neighbors | 极致性能要求、固定 8 邻域 | 安全(依赖 .) | 中等(偏移需记忆) | 固化 |
不管选哪个方案,有几点值得养成习惯:
grid 状态被正确依赖;grid,保持函数纯净。把邻域逻辑抽象出来之后,“边缘计数不准确”这个困扰基本就彻底告别了。扫雷的核心逻辑,可以写得既可靠又优雅。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述