游戏“尼姆棋”的算法讨论
游戏“尼姆棋”是这样的玩法:棋盘是8格X8格的样子,分“红”,“黑”双方,每一列各有一红,一黑两个棋子,这两个棋子的位置是随机生成的,但是生成时有一个规则:红棋子在上方,黑棋子在下方。每局游戏开始时随机会产生一局棋,双方一次只许走一个棋子,黑棋先走,每次只能将棋子走在同一列中,只许前进,不许后退,不许不走,也不能超越对手棋子的位置。轮到对手走时也同样。双方轮流走子,谁取得最后一次走子权算赢,换句话说,谁最后无子可走算输。
算法分析如下(本文探讨人机对战):
本文的一些提法简单介绍如下
本文所说的“剩余几列”、“几列”是指有几列还可以走棋,其余几列已经无法走棋了。
本文所说的“格数”是指某一列可以走的棋格的数量,即可以走的最大步数。
本文所说的1,2,3,4,5,6指一列可以走的格数。
ID:根据算法推导之后需要走的某一列(0<= ID <= 7)即棋盘上的从左数第几列。
步长(step):根据算法推导之后某列需要走的步数(1 <= step <= 6)。
首先从最简单的情况分析,
剩余一列时最简单:电脑找到格数不为零的一列,返回这一列的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中的数据,如果可以,这就是赢棋的形势,也就是说电脑将某一列走完就可以将棋局转换为“输棋棋局”而最终取得胜利;否则这个棋局就是一个“输棋棋局”,将这个“输棋棋局”存入结构数组falsearray,再进行下一组数据的检测。如果无法使棋局减少一列而转换为“输棋棋局”那么推导方式类似于剩余三列时的步骤,由此可以推导出剩余四列时的输棋形式。
剩余五列,六列,七列,八列时的“输棋棋局”推导方式也类似于剩余四列时的情况.处理方法称之为“算法二”
电脑实际走棋的时候,对于某种棋局,首先检测棋局是否满足“算法一”可以处理的情况,“算法一”无法处理时,将该棋局进行转化,也就是走某一列,走的步长是i,1<=i<=6,然后考察该棋局是否属于结构数组falsearray,属于的话电脑就找到了取得胜利得走法,否则的话电脑只好随机走棋.
由算法二推导出来的“输棋棋局”共有196种,看到这里您一定会发现一个有趣的现象:剩余列数是偶数时,格数两两相等的棋局(这样的棋局实际上是“输棋棋局”,而剩余列数是奇数时不存在格数两两相等的棋局)数量有208种,加上196种“输棋棋局”只是一个很少的数字-404种,而尼姆棋的实际棋局数量(2995种)则会大的多(这两种情况只占棋局总数的约13%).那么先走棋的一方遇到“输棋棋局”和“算法一”处理后的棋局的可能性就是很小的,结果就是:先走的棋一方占便宜!但是人机对战时即便人先走棋,人取胜的可能性还是很小的,因为人脑使用“算法一”,“算法二”进行处理的速度和准确性都不如电脑,尤其是剩余列数较大的时候,比如剩余七列,八列的情况,开始的时候人很有可能是随意走棋的,电脑则不是.
算法分析如下(本文探讨人机对战):
本文的一些提法简单介绍如下
本文所说的“剩余几列”、“几列”是指有几列还可以走棋,其余几列已经无法走棋了。
本文所说的“格数”是指某一列可以走的棋格的数量,即可以走的最大步数。
本文所说的1,2,3,4,5,6指一列可以走的格数。
ID:根据算法推导之后需要走的某一列(0<= ID <= 7)即棋盘上的从左数第几列。
步长(step):根据算法推导之后某列需要走的步数(1 <= step <= 6)。
首先从最简单的情况分析,
剩余一列时最简单:电脑找到格数不为零的一列,返回这一列的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中的数据,如果可以,这就是赢棋的形势,也就是说电脑将某一列走完就可以将棋局转换为“输棋棋局”而最终取得胜利;否则这个棋局就是一个“输棋棋局”,将这个“输棋棋局”存入结构数组falsearray,再进行下一组数据的检测。如果无法使棋局减少一列而转换为“输棋棋局”那么推导方式类似于剩余三列时的步骤,由此可以推导出剩余四列时的输棋形式。
剩余五列,六列,七列,八列时的“输棋棋局”推导方式也类似于剩余四列时的情况.处理方法称之为“算法二”
电脑实际走棋的时候,对于某种棋局,首先检测棋局是否满足“算法一”可以处理的情况,“算法一”无法处理时,将该棋局进行转化,也就是走某一列,走的步长是i,1<=i<=6,然后考察该棋局是否属于结构数组falsearray,属于的话电脑就找到了取得胜利得走法,否则的话电脑只好随机走棋.
由算法二推导出来的“输棋棋局”共有196种,看到这里您一定会发现一个有趣的现象:剩余列数是偶数时,格数两两相等的棋局(这样的棋局实际上是“输棋棋局”,而剩余列数是奇数时不存在格数两两相等的棋局)数量有208种,加上196种“输棋棋局”只是一个很少的数字-404种,而尼姆棋的实际棋局数量(2995种)则会大的多(这两种情况只占棋局总数的约13%).那么先走棋的一方遇到“输棋棋局”和“算法一”处理后的棋局的可能性就是很小的,结果就是:先走的棋一方占便宜!但是人机对战时即便人先走棋,人取胜的可能性还是很小的,因为人脑使用“算法一”,“算法二”进行处理的速度和准确性都不如电脑,尤其是剩余列数较大的时候,比如剩余七列,八列的情况,开始的时候人很有可能是随意走棋的,电脑则不是.
...全文
相关推荐
聪明模式尼姆游戏.py
聪明的尼姆游戏,Python代码的实现 尼姆游戏是个著名的游戏,有很多变种玩法。两个玩家轮流从一堆物品中拿走一部分。在每一步中,玩家可以自由选择拿走多少物品,但是必须至少拿走一个并且最多只能拿走一半物品,然后轮到下一个玩家。拿走最后一个物品的玩家输掉游戏。 在聪明模式中,计算机每次拿走足够多的物品使得堆的大小是2 的幂次方减1——也就是3,7,15,31 或63。除了堆的大小已经是2 的幂次方减1,在其他情况下这样走都是符合游戏规则的。在那种情况下,计算机就按游戏规则随机拿走一些。 编写程序,模拟聪明版本的尼姆游戏。
尼姆算法库
尼姆算法库
人工智能课程尼姆游戏代码
人工智能客的尼姆游戏代码,仅供参考。自己防雷同吧
基于qt实现的三连棋(井字棋)游戏(含AI算法)
基于qt实现的三连棋,包括初始界面,有人机对战和双人对战,人机对战采用min-max搜索来实现,代码量较小,适合新手学习。
基本博弈论(巴什博弈+威佐夫博弈+尼姆博弈+一些自己的注释)
大家一起学~免费啦~


