上一篇博客介绍了本项目总体情况, 这一篇来介绍一下我实现的自动扫雷 AI 算法. 本 AI 胜率比网上最高胜率的 AI 差 0.5% 左右. 不过本 AI 也不是没有优势, 它运算速度很快 (强行有优势 (ˉ▽ ̄~)
), 平均 42 毫秒可以扫完一局 Win XP 规则下的专家难度.
这篇博客会介绍一下我的思路和踩过的坑, 也会列出一些关于胜率的数据. 希望能够帮助其他萌新入个门. 项目已经开源, 代码也写了注释, 链接放在文章最后.
先再次把最终成品的 AI 胜率等指标罗列一下:
指标 | Win XP 规则测试结果 | Win 7 规则测试结果 |
---|---|---|
测试版本 | Win XP 规则, 专家难度 | Win 7 规则, 专家难度 |
测试局数 | 50,0000 局 | 50,0000 局 |
胜率 | 39.68% | 52.45% |
运行总耗时 | 21275 秒 | 33136 秒 |
每局平均耗时 | 42 毫秒 | 66 毫秒 |
胜局的每局平均耗时 | 57 毫秒 | 68 毫秒 |
注1: 测试 Win XP 规则是在半夜运行的, 电脑除了扫雷没有在运行其他进程; 而测试 Win 7 规则时我同时还在拿电脑办公, 导致 Win 7 规则下耗时比 XP 多. 而实际上 Win XP 与 Win 7 应该运行时间差不多.
注2: 测试使用的扫雷程序是自己复现的 Win XP 与 Win 7 规则, 而非模拟鼠标点击原版扫雷窗口. 因为那样太慢了. 网上有大佬已经测试过, Win XP, Win 7 原版扫雷并无影响地雷分布的隐藏规则. 所以若真要在原版上测试, 结果应该不会有较大偏差.
我在网上找到的胜率最高的扫雷 AI 是 ztxz16 大佬写的, Win XP 版本胜率 40.07%, Win 7 版本胜率 52.98%. 我的做法也是参考了 ztxz16 大佬的算法 (最强扫雷 AI 算法详解 + 源码分享 - ztxz16). B 站的 _黄歪歪 应该也是他, 也发了些关于扫雷 AI 的视频. 膜一波.
不过 ztxz16 大佬的这个项目偏试验性, 我顶着没有注释的压力把大佬开源的源码看了一遍, 看得出来有些地方他应该是懒得优化, 最终 AI 速度不是很快. 我本来想自己测试一遍他的 AI, 但跑了一晚上也只跑出来了几千局还是几万局 (捂脸).
该交代的差不多都交代完了, 下面开始讲我的做法.
高胜率低耗时自动扫雷 AI 算法
术语与定义
统一一下一些操作的术语方便后续描述. 我不是扫雷圈的, 很多术语可能不知道, 所以如果以下操作本来就有自己的中文名称, 提醒我我会改过来的!
操作 | 术语 |
---|---|
鼠标左键, 那个点开格子的操作 | 挖掘 |
鼠标右键, 那个放置小红旗的操作 | 插旗 |
鼠标双键或中键, 那个自动检测周围八格是否可挖掘的操作 | 检查周围 |
空白的, 没点过的格子 | 未知格 |
已知的有数字 (包括 0) 的格子 (已挖开且不是雷的格子) | 数字格 |
另外,
- 格子坐标 ( x , y ) (x, y) (x,y) 表示 x x x 行 y y y 列, 下标从 0 0 0 开始.
- N N N 和 M M M 为棋盘行数与列数, S = N × M S = N \times M S=N×M 表示格子总数.
- 以下所涉及的 “胜率”, 除非另外注明, 均为 Win XP 规则专家难度下的胜率.
第一步: 开局挖哪
开局第一步点击不同的地方也是会影响胜率的. 根据 ztxz16 大佬的测试 (地表最强扫雷AI • 编程探索扫雷极限胜率 - _黄歪歪), 开局 Win XP 挖掘 ( 0 , 0 ) (0, 0) (0,0), Win 7 挖掘 ( 2 , 2 ) (2, 2) (2,2) (或对称的另外三个角落) 胜率最高.
我自己也对 Win XP 与 Win7 大致测了一下 ( 0 , 1 ) (0, 1) (0,1), ( 1 , 0 ) (1, 0) (1,0), ( 1 , 1 ) (1, 1) (1,1) 等几个位置, 确实如此, 胜率会下降 1% ~ 2% 左右.
![]() |
![]() |
---|---|
Win XP 开局 ( 0 , 0 ) (0, 0) (0,0) | Win 7 开局 ( 2 , 2 ) (2, 2) (2,2) |
不过至于为什么是角落胜率比中心高, 我猜可能是角落有更多可套用减法公式的场景 (减法公式在下一节有讲). 减法公式本质就是根据相邻两个数字格的一侧去推断另一侧. 而边缘的格子天然已知靠边的一侧无雷, 因而可以更容易地推断出另一侧雷的个数.
第二步: 基于定义与定式
用到了两个基本公式或定式:
第一, 仅基于一格判断: 即根据目标数字格的数字与其周围八格 (角落的话不满八格) 的状态, 判断该数字格周围八格的未知格是不是全为雷或全不为雷. 这是扫雷最基本的公式. 其逻辑其实就是鼠标左右双键检查周围的逻辑.
第二, 基于相邻两格: 即使用减法公式.
如图, M M M, N N N 为两个相邻数字格, 两者上下各有一块互相影响的公共区域 P P P, Q Q Q, 左右则分别有互不影响的两翼 A A A, B B B. 记地雷数量为 C C C, 显然地雷数量 C C C 符合如下等式:
{ C A r o u n d M = C A + C P + C Q C A r o u n d N = C B + C P + C Q \begin{cases} C_{AroundM} = C_A + C_P + C_Q \\ C_{AroundN} = C_B + C_P + C_Q \end{cases} { CAroundM=CA+CP+CQCAroundN=CB+CP+CQ
两式相减即得减法公式:
C A r o u n d M − C A r o u n d N = C A − C B C_{AroundM} - C_{AroundN} = C_A - C_B CAroundM−CAro