Windows的扫雷游戏有这么一个秘密:在你第一次点击游戏区中的方块时,出现的绝对不会是地雷。
实践是检验真理的唯一标准,我用IDA拆了winmine.exe,版本是5.1.2600.0。下面我只简要介绍分析的结果,不再特别列出晦涩的汇编代码。
扫雷游戏的游戏区数据放在一个庞大的一维BYTE数组中,这个变量的名字是rgBlk。并且,用于获取指定坐标信息的公式是:
C++代码
LPBYTE pbPos = &rgBlk[y * 32 + x];
注意,这里的x和y是以1为起始索引的;换句话说,rgBlk[0]~rgBlk[31]是不被使用的。另外,对于最大的游戏区(30*24)来说,rgBlk的大小是足够可以胜任的。
那么下面我们的实验开始,在调试器中检查rgBlk的内存。为了显示的简洁明了,我选择了初级9*9的区域。
在上图中我对db输出的结果做了处理,其中白色背景的被选定文本区域是游戏区的有效内存数据,灰色的文字也代表了无效(横坐标大于9)的数据。红色的8f代表随机生成的地雷,1f代表这个方块还未被翻开。
接下来我故意打开第2行第1列的方块,并继续检查游戏区的数据,如下图。
如你所见,这个方块并不是雷,它的数据变成了42。与此同时,第1行第1列的方块却变成了8f,也就是地雷的数据。
好了,下面再打开第1行第1列的方块,游戏结束。最后的结果表示,地雷的位置和内存中的数据是一样的。
最后当然是揭秘环节。在玩家点击了一个含有地雷的方块时,游戏会做出如下判断:
如果游戏已经进行(不是第一次点击),则引爆地雷,游戏结束。
如果本次点击为第一次点击,则从左上角开始依次搜索,直到找到一个不是地雷的方块,并将其修改为地雷。原有的地雷方块做无雷处理。
上面的逻辑是在StepSquare函数中实现的,有兴趣的可以详细了解一下逆向后的代码。
从用户体验的角度来讨论的话,这么做的原因很简单:如果不特殊处理的话,从概率学的角度上来讲,那种每次玩扫雷都会被第一个方块炸死的衰人还是会有的。