游戏“尼姆棋”的算法讨论
游戏“尼姆棋”的算法讨论
游戏“尼姆棋”是这样的玩法:棋盘是8格X8格的样子,分“红”,“黑”双方,每一列各有一红,一黑两个棋子,这两个棋子的位置是随机生成的,但是生成时有一个规则:红棋子在上方,黑棋子在下方。每局游戏开始时随机会产生一局棋,双方一次只许走一个棋子,黑棋先走,每次只能将棋子走在同一列中,只许前进,不许后退,不许不走,也不能超越对手棋子的位置。轮到对手走时也同样。双方轮流走子,谁取得最后一次走子权算赢,换句话说,谁最后无子可走算输。
算法分析如下(本文探讨人机对战):
本文的一些提法简单介绍如下
本文所说的“剩余几列”、“几列”是指有几列还可以走棋,其余几列已经无法走棋了。
本文所说的“格数”是指某一列可以走的棋格的数量,即可以走的最大步数。
本文所说的1,2,3,4,5,6指一列可以走的格数。
ID:根据算法推导之后需要走的某一列(0&= ID &=
7)即棋盘上的从左数第几列。
步长(step):根据算法推导之后某列需要走的步数(1 &= step
首先从最简单的情况分析,
剩余一列时最简单:电脑找到格数不为零的一列,返回这一列的ID,然后返回该列的格数。
剩余两列时:仔细观察就可以发现,电脑要想取得胜利,只要始终使剩余两列的格数相等就可以,即这样的话电脑就总是走最后一步,从而获得胜利。
剩余两列的算法如下:
剩余两列时,若两列所剩的格不相等,如a列余x格,b列余y个格,如果x&y则移动a列(ID=a),否则移动b列(ID=b),步长为|x-y|
(step = abs(x-y))。目的是使对方所遇到的棋局两列相等,则计算机必赢。
如果剩余两列时两列的格数相等,那么电脑只好随机走棋,随机走棋不在此讨论。
剩余四,六,八列时,也进行类似判断和处理,这样的话电脑就总是走最后一步:
1.剩余四列时,先寻找格数相等的两列,找到后使格数不相等的另外两列格数相等。
2.剩余六列时,先寻找格数两两相等的四列,找到后使格数不相等的另外两列格数相等。
3.剩余八列时,先寻找格数两两相等的六列,找到后使格数不相等的另外两列格数相等。
4.剩余三列时,先寻找格数相等的两列,找到的话返回另一列的ID,step=该列的格数,也就是说使棋局转换为剩余两列时的情况,剩余五列,七列时也可进行类似的分析。
5.剩余五列时,先寻找格数两两相等的四列,找到的话返回另一列的ID,step=该列的格数。
6.剩余七列时,先寻找格数两两相等的六列,找到的话返回另一列的ID,step=该列的格数。
以上的棋局是最简单的情况也是比较特殊的棋局,它的处理方法称之为“算法一”。
现在分析比较一般的情况,即不满足以上条件的棋局。
剩余三列时(三个格数数互不相等),由人提出一个最初的“输棋棋局”,“输棋棋局”的意思是:电脑将某个棋局的形式走成(或者叫转换成)“输棋棋局”之后,无论对方如何走棋,电脑都可以使棋局的局面朝着电脑取胜的方向发展,从而最终取得胜利.
这个最初的“输棋棋局”是1,2,3.即剩余三列可以走棋,一列剩余一格,一列剩余两格,一列剩余三格(下文的阿拉伯数字也是同样的含义).我们仔细观察就可以发现,无论人怎么走棋,走完之后的棋局电脑都可以用“算法一”进行处理从而取得胜利.
“输棋棋局”先存入一个结构数组falsearray,然后将剩余格数从1,2,3变换至6,6,6(由于棋局1,2,3和3,2,1;3,1,2;1,3,2等棋局实质上并没有区别,所以在变换时,首先要检测这个棋局是否以前出现过,若出现过,就不予考虑)。
棋局开始变换,对于变换之后的棋局如,1,2,4,要检测是否可以通过使该三列中的任意一列减去任意一个数i,(1=&i&6,也就是走的格数,不得使列数减少)变换成结构数组falsearray中的数据。如果可以,这就是赢棋的形势,即电脑在某一列走i个格就可以将棋局转换为“输棋棋局”从而最终取得胜利;如果不可以那么这个棋局就是一个“输棋棋局”,将这个“输棋棋局”存入结构数组falsearray,再进行下一组数据的检测。由此可以推导出剩余三列时的输棋形式。
对于剩余四列时也进行类似的讨论(剩余格数从1,2,3,4变换至6,6,6,6),不过也有区别,比如棋局的形式是1,2,3,4.分析的时候首先要考虑是否可以使棋局减少一列(即将这四列中的某一列走完),从而使棋局转换为数组falsearray中的数据,如果可以,这就是赢棋的形势,也就是说电脑将某一列走完就可以将棋局转换为“输棋棋局”而最终取得胜利。如果无法使棋局减少一列而转换为“输棋棋局”那么推导方式类似于剩余三列时的步骤,由此可以推导出剩余四列时的输棋形式。
剩余五列,六列,七列,八列时的“输棋棋局”推导方式也类似于剩余四列时的情况.处理方法称之为“算法二”
电脑实际走棋的时候,对于某种棋局,首先检测棋局是否满足“算法一”可以处理的情况,“算法一”无法处理时,将该棋局进行转化,也就是走某一列,走的步长是i,1&=i&=6,然后考察该棋局是否属于结构数组falsearray,属于的话电脑就找到了取得胜利得走法,否则的话电脑只好随机走棋.
由算法二推导出来的“输棋棋局”共有196种,看到这里您一定会发现一个有趣的现象:剩余列数是偶数时,格数两两相等的棋局(这样的棋局实际上是“输棋棋局”,而剩余列数是奇数时不存在格数两两相等的棋局)数量有208种,加上196种“输棋棋局”只是一个很少的数字-404种,而尼姆棋的实际棋局数量(2995种)则会大的多(这两种情况只占棋局总数的约13%).那么先走棋的一方遇到“输棋棋局”和“算法一”处理后的棋局的可能性就是很小的,结果就是:先走的棋一方占便宜!但是人机对战时即便人先走棋,人取胜的可能性还是很小的,因为人脑使用“算法一”,“算法二”进行处理的速度和准确性都不如电脑,尤其是剩余列数较大的时候,比如剩余七列,八列的情况,开始的时候人很有可能是随意走棋的,电脑则不是。
如果您对这个算法程序感兴趣的话,可以向我索要相关算法的程序源代码和我自己编写的小游戏”尼姆棋“的程序源代码,我编写的"尼姆棋"游戏的源代码,使用的算法就是上边说的算法,界面可能非常简陋,仅仅实现了游戏的基本功能。如果您有不同的意见,欢迎一起讨论。
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。