一字棋游戏设计-极大极小搜索

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixingstudio/article/details/7228205

1.     问题定义

一字棋游戏,包括两个选手。用户可以在一个3*3的棋盘上任意的选择空闲的位置拜访棋子,最早在水平方向上,或者垂直方向上或者对角线方向上形成三子一线者获胜。棋盘如图1所示。这里我们实现的是用户和计算机进行对弈。本程序要实现的是让计算机可以自动的根据当前棋局计算下一步对自己最有利的走步,尽可能的朝着可以让计算机获胜的方向走步。需要采用极大极小搜索算法。

图1.一字棋棋盘

2.     技术现状

目前一字棋作为各个高校的人工智能教学的典型范例,在技术方面一般都采用极大极小搜索算法,理论层面比较成熟,有很多资料可以参考。大部分教学资料的一字棋都采用C++语言编写,因为本人对C++语言不是很熟悉,更不熟悉C++语言的界面操作,所以在这里的所有实现都通过C#来实现。在刘峡壁老师编著的《人工智能导论——方法与系统》[1]一书中,对一字棋算法的极大极小搜索有着比较详细的介绍,本系统的实现,主要参照了刘峡壁老师一书中对一字棋算法的介绍。

3.     本文采用方法

3.1  极大极小搜索

在本博弈算法中,采用极大极小搜索[2]方法搜索计算机的下一步走步方法,每一次走步的时候以计算机当前所面对的棋局状态作为根顶点,生成一棵有限深度的博弈子树,然后从该博弈子树的叶结点向上回溯,确定在根顶点处的当前最好的策略,找到一条当前最好行动的边。在生成博弈子树的过程中,计算机己方对应的走步状态的节点称为MAX节点,对应的对手走步的节点成为MIN节点。

设计的棋局状态的估价函数为,当前棋盘下,在整个棋盘中己方可以三子一线的个数减去对方可以三字一线的个数。假设当前状态下己方三字一线的个数为E(MAX),对方的三字一线的个数为E(MIN),则当前棋局的估价函数为: e= E(MAX)- E(MIN)。对于MAX节点,MAX具有主动权,可以从中选择对自己最好的走步,既估值最大的走步。因此MAX顶点的子节点之间是“或”的关系,MAX顶点的倒退值应该取其子节点估值的极大值;MIN顶点,主动权掌握在MIN手中,为了取胜,MAX需要做最坏的打算,应该考虑到MIN会选择对自己最好的,从而对MAX最差的走步,既估值最小的走步,因此MIN顶点的子节点之间是“与”的关系,MIN顶点的倒推值应该取子节点的最小值。

关于当前棋局的估值计算,请参考图2。图2是一个棋局状态,通过统计其中的不同棋子可以实现的三点一线的个数来计算当前棋局的估值。从图2中可以看出,叉子的棋子当前可以三子一线的个数为3,分别为横行第三行,竖列第一列,还有次对角线。圆圈棋子的当前可以三点一线的个数为2,分别为横行第一行,竖列第三列。然后可以得知,对于计算机(计算机持有叉子棋子),当前棋局的估值为3-2=1。

  

图2. 一个棋局状态

一个一字棋的走步搜索过程如图3所示。

图3. 一字棋搜索过程

3.2  α-β剪枝

在极大极小搜索过程中,首先生成博弈子树,然后计算各个顶点的估值,这一过程中,生成子树和计算顶点的估值是彼此分离的,在本程序中,构造博弈子树采用递归的方法构造,在递归退出的过程中,由子节点的估值计算返回节点的估值。但是极大极小搜索仍然会搜索很多不必要的分支,为了减少搜索的次数,本系统实现了α-β剪枝[3]

根据倒推结果,在非叶子节点的向下分支中,剪掉那些目前尚未扩展,但是无论其是否扩展都不会改变非叶子节点倒推值的分支。为了判断分支的扩展是否会影响非叶子节点的倒推结果,需要计算MAX顶点的倒退值的最小辩解,称为α值,以及MIN节点倒推值的最大边界,称为β值。由于MAX顶点的倒推值总是其子节点的最大值,因此如果MAX子节点的估值已经确定小于MAX顶点的α值,则无论该子节点下的为扩展分支情况如何,都不会影响MAX顶点的估值。这样就不必搜索这个子节点一下的所有分支。同理,如果MIN节点的子节点的估值已经确定大于β值,则不必再搜索该顶点下的为扩展分析,可以进行剪枝。

一个α-β剪枝的示例请看图4。

图4.α-β剪枝

在图4中,建立的搜索子树的起始点为S0,从S0开始搜索所有的可以的走步。S0为MAX节点,然后对应的下一层为MIN节点,然后MAX节点与MIN节点相互交错。在最后一层为MAX节点,每个节点的估值已经写在这个节点的下面。F节点为MIN节点,F节点的估值应该去子节点的最小值,F的子节点中最小值为K的估值4,所以F的估值为4。这样C节点为MAX节点,应该取子节点估值的最大值。G节点取子节点的最小值,G节点的子节点N的估值为1,所以不论G的其他的子节点的估值为何,G节点的估值都会小于等于1,这样,C节点取子节点估值的最大值,已经知道了G节点的估值肯定小于等于1,所以无论G节点的其他分支如何扩展,都不会影响C节点的现在的估值。所以这样就可以省去了G节点的其他的分支的扩展。其他的节点的剪枝同样的道理。

3.3  进化算法

由于一直没有想出如何用适合进化算法计算表示棋盘状态的数据结构,所以在采用进化算法[4-6]计算走步的时候,仍然采用了上面设计的棋盘状态表示。进化算法简单的采用了选择突变两个操作,没有选择交叉操作的原因也是因为没有合理的数据结构进行交叉运算。首先在当前的棋局状态下随机的生成一些走步,然后对这些走步以一定的概率进行突变,最后进行估值,选择估值最好的走步。

4.     实验结果

4.1  没有剪枝的实验结果

在没有剪枝的计算机搜索走步的过程中,根据搜索的深度,需要搜索的走步的数量非常多。第一次计算机搜索了400步,第二次计算机搜索了156步,第三次走步计算机搜索了40步。一个典型的走步过程如图5-7所示。从图7中可以看到计算机获胜。

图5. 下棋过程1

 

图6. 下棋过程2

图7. 下棋过程3

4.2  α-β剪枝搜索结果

带有α-β剪枝的搜索结果中,可以明显的看到搜索的次数很显著的减少了。图8-11展示了一个带有剪枝过程的搜索结果。在图11中可以看到计算机获胜。然而存在的问题是,添加α-β剪枝算法的极大极小搜索的效果不如上面直接的极大极小算法效果好,因为有一些棋局找到的不是最好的走步,这个应该和剪枝算法没有什么关系,而是程序写的不合理。

 

图10. 带有剪枝的搜索3

图11. 带有剪枝的搜索4

4.3  进化算法搜索

由于一直没有想出如何用适合进化算法计算表示棋盘状态的数据结构,所以在采用进化算法计算走步的时候,仍然采用了上面设计的棋盘状态表示。进化算法简单的采用了选择突变两个操作,没有选择交叉操作的原因也是因为没有合理的数据结构进行交叉运算。首先在当前的棋局状态下随机的生成一些走步,然后对这些走步以一定的概率进行突变,最后进行估值,选择估值最好的走步。

因为本程序中并没有写出进化算法的精华,也没有实现真正的进化算法,所以进化算法的效果非常的差。图12-14展示了进化算法的搜索。从这些图中可以看出,进化算法的搜索,往往不能找到最优解。

                                                                                 

图12. 进化算法1

5.     结论

通过以上实验结果,可以看出,极大极小搜索可以找到当前状态下的最优解之一,如果最优解同时有多个,则简单的选择第一个最优解。然而在极大极小的搜索过程中,可能会搜索很多无效的分支,这样就可以通过α-β剪枝方法剪去不必要的搜索分支,加快搜索速度。在本程序中,剪枝后与不剪枝的搜索速度没有什么差别,因为本程序中搜索的深度只有3层,而且棋盘非常小。如果是正规的五子棋盘或者象棋棋盘,那么剪枝就是非常有必要的了。否则搜索一个合适的走步的时间复杂度会非常高,而且也会带来空间的消耗,剪枝的时候,就减少了不必要分支的搜索,同时也就不用为这些不去搜索的分支建立搜索树中的节点,加快了搜索速度。进化算法本来可以进行搜索的优化,但是因为一直没有找到合理的用于进化算法的数据表现形式,所以这里的进化算法并没有展示出进化算的精髓,没有完全的体现进化算法的真正意义。

 

 

源程序下载地址:http://download.csdn.net/detail/weixingstudio/4040936

没有更多推荐了,返回首页