
本文介绍一种简洁、健壮的 Go 实现,通过边界安全的双重嵌套遍历,为扫雷游戏中的每个非雷格子统计其八邻域内 -1(代表地雷)的数量,避免越界访问与冗余条件判断。
本文介绍一种简洁、健壮的 go 实现,通过边界安全的双重嵌套遍历,为扫雷游戏中的每个非雷格子统计其八邻域内 `-1`(代表地雷)的数量,避免越界访问与冗余条件判断。
在实现控制台版扫雷(Minesweeper)时,一个核心任务是:为每个非雷单元格,准确计算其周围 8 个方向(上、下、左、右及四个对角线)中地雷(即值为 -1 的元素)的总数,并将该数字填入对应位置;而雷格子本身保持 -1 不变。直接为每个方向单独写 if 判断不仅代码冗长、易出错,更难以维护和扩展。
更优解是采用「以雷为中心」的逆向思维:不遍历每个空格去查邻居,而是遍历每个雷,主动对其所有合法邻格进行计数累加。这种方法逻辑清晰、复用性强,且天然规避了大量边界检查。
以下是完整、可直接集成的 Go 实现:
// 假设 ary 是 [][]int 类型的二维切片,row 和 col 分别为其行数与列数
for i := 0; i < row; i++ {
for j := 0; j < col; j++ {
if ary[i][j] == -1 { // 找到一颗地雷
// 遍历该雷的所有邻接位置 (k, l),含自身(但自身不累加)
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++ {
// 跳过雷自身,仅对非雷格子 +1
if ary[k][l] != -1 {
ary[k][l]++
}
}
}
}
}
}
// 辅助函数(需自行定义或使用 math.Max/Min,注意 int 类型)
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(0, i-1) 和 min(row-1, i+1) 确保 k 始终落在 [0, row-1] 内,同理 l 安全落在列范围内,彻底消除 index out of range panic。
- 语义明确:内层双循环枚举的是“以 (i,j) 为中心的 3×3 区域”,逻辑直观,易于验证正确性。
- 一次遍历完成全部更新:无需额外存储或二次扫描,空间复杂度 O(1),时间复杂度 O(m×n×9) ≈ O(m×n),实际性能优秀。
⚠️ 注意事项:
- 此算法原地修改原数组。若需保留原始雷分布用于后续逻辑(如点击揭示),建议先深拷贝一份用于计数,或分离“雷图”与“提示图”。
- 若数组中存在除 0 和 -1 外的其他初始值(如已标记旗子),需根据业务逻辑调整 if ary[k][l] != -1 的判断条件。
- max/min 函数在 Go 标准库 math 中仅支持 float64,生产环境推荐使用 golang.org/x/exp/constraints 或自定义 int 版本(如上所示)。
最终效果即如题所示:输入含 -1 的雷阵,输出每个空格被周围地雷“点亮”的数字,构成标准扫雷数字提示层——简洁、可靠、符合工程实践。










