
本文介绍在扫雷类游戏中,如何用简洁、健壮的方式遍历二维数组,为每个非雷格子统计其八邻域内地雷(-1)的数量,避免越界访问,提升代码可读性与可维护性。
本文介绍在扫雷类游戏中,如何用简洁、健壮的方式遍历二维数组,为每个非雷格子统计其八邻域内地雷(-1)的数量,避免越界访问,提升代码可读性与可维护性。
在实现控制台版扫雷时,一个核心逻辑是:对每个非地雷格子(即值不为 -1 的单元),统计其周围 8 个方向(上、下、左、右及四个对角线)中地雷的总数,并将该数字填入对应位置;而地雷格子本身保持 -1 不变。若采用对每个方向单独 if 判断的方式(如 ary[i-1][j-1]、ary[i-1][j]…),不仅代码冗长,更易因边界处理疏漏引发 panic(如索引负数或超出长度)。
更优解是以地雷为中心反向扩散更新:遍历整个二维数组,一旦发现 ary[i][j] == -1,就立即遍历其所有合法邻域坐标 (k, l),并对其中所有非地雷格子执行 ++ 操作。关键在于:如何安全、自动地限定邻域范围,无需手动写 8 个条件?
答案是使用 max(0, i-1) 和 min(row-1, i+1) 动态计算行索引上下界,同理处理列索引。这样无论当前地雷位于角落(如 (0,0))还是边缘(如 (row-1, col-2)),循环变量 k 和 l 始终落在 [0, row-1] 和 [0, col-1] 范围内,彻底规避越界风险。
以下是完整、可直接使用的 Go 实现:
// 假设 ary 是 [][]int 类型,row = len(ary), col = len(ary[0]) for i := 0; i < row; i++ { for j := 0; j < col; j++ { if ary[i][j] == -1 { // 发现地雷 // 遍历以 (i,j) 为中心的 3×3 邻域(含自身) for k := max(0, i-1); k <= min(row-1, i+1); k++ { for l := max(0, j-1); l <= min(col-1, j+1); l++ { // 跳过地雷自身,仅更新安全格子 if ary[k][l] != -1 { ary[k][l]++ } } } } } } // 辅助函数(Go 标准库暂未内置,需自行定义) func max(a, b int) int { if a > b { return a }; return b } func min(a, b int) int { if a < b { return a }; return b }
✅ 优势说明:
- 逻辑清晰:主流程“找雷→扩邻→累加”,符合直觉;
- 边界安全:max/min 封装确保所有 k, l 合法,零 panic 风险;
- 无重复计算:每个邻域格子仅被其周边地雷影响,天然满足扫雷规则;
- 易于扩展:如需支持六边形网格或自定义邻域形状,只需调整内层循环逻辑。
⚠️ 注意事项:
- 此算法修改原数组。若需保留原始布雷布局,应先深拷贝或使用独立结果数组;
- 时间复杂度为 O(mn × 9) ≈ O(mn),对常规棋盘(如 10×10 至 50×50)完全高效;
- 若后续需支持“点击展开空区”(flood fill),建议将计数逻辑封装为独立函数,便于复用。
通过这种以“源(地雷)驱动更新”的范式,我们用不到 15 行核心代码,替代了冗长易错的手动方向判断,显著提升了工程健壮性与算法表达力——这正是良好编程实践的典型体现。