五子棋AI算法的改进方法讲解

……首先道歉,从Word贴到Livewrter,好多格式没了,也没
……大家凑活着看……想做个好的人机对弈的五子棋,可以说需要考虑的问题还
AI五子棋的过程分为十四步,让我来步步介绍。

自然对五子棋需要有足够的了解,现在默认大家现在和我研究五子棋
以这个为基础,介绍多数人不大熟悉的方面。五子棋的规则实际上有
“有禁手”进行一下简单介绍:
“先手必胜”已经得到了论证,类似“花月定式”和“浦月定式”,很多先手必胜下法虽
但高手确能做到必胜。所以五子棋的规则进行了优化,得到了 “有禁手”
五子棋中,黑棋必然先行。因此“有禁手”五子棋竞技中对黑棋有以下“禁手”限制:“三
”:黑棋下子位置同时形成两个以上的三;“四四禁”:黑棋下子位置同时形成两个以上的
“长连禁”:六子以上的黑棋连成一线。黑棋如下出“禁手“则马上输掉棋局。不过如果“连
”与“禁手”同时出现这时“禁手”是无效的。所以对于黑棋只有冲四活三(后面会有解释)




WPF,表现层和逻辑层完全分开,前台基本可以通过拖拽完成布局,这里就

处实际上市两个渐变Label的拼接,2、3是两个label,4、5实际上是两个Button,
6、7、8、9 的控制,修改label和Button的Content
Button会丝毫看出不出有Button的影子,这里战友
写过一个Style如下











Style称为Style1。界面逻辑上,将是否开始、是否禁手和是否电脑先行
bool型值进行逻辑上的控制。中间的棋盘
canvas,一个15*15的Grid放满Button并将每个Button应用Style1开始时候
0,也就是根本看不到,在下棋的时候改变Button的背景和透明度,实现落子
Grid的位置关系,所以可看起来好像是下在横竖的交线处。

“无禁手”和“有禁手”的输赢判断自然不同。先看无禁手:这个比较简单,遍
每个方向从中间点开始,往两边数连子数,然后将两个方向的连字数加和再加一(中
5,那么就说明下子方赢棋。
输赢判断还需要判断禁手,禁手的判定较为复杂。将待判断点放入黑
然后搜索待判断点周边棋盘;还原棋盘;利用搜索结果依次对各方向进行分析,判断
若形成长连,判定
1,再对下一
直到各个方向分析结束。若四连棋型或三连棋型的统计数大于1,则返回


有禁手”规则比较复杂,涉及到比较多下棋方面的技巧,而且对算法的思路没有丝毫影响,
AI设计。若设计好无禁手AI,只需要让AI执黑时
AI。虽然这种方式没有利用有禁手规则下的


5:即构成五子连珠
4:即构成两边均不被拦截的四子连珠。
4:一边被拦截的四子连珠
3:两边均不被拦截的三字连珠
3:一边被拦

截的三字连珠
2:两边均不被拦截的二子连珠
2:一边被拦截的二子连珠

可以将五子棋的棋型用连珠进行分类,分类过后我们按照威力给每种棋
因此很容易理解,双活三和三活三的威力是一样的,类
100分为满分,对棋型进行了以下打分:
5, 100分
4、双死4、死4活3, 90分
3, 80分
3活3, 70分
4, 60分
3, 50分
2, 40分
3, 30分
2, 20分
2, 10分
0分
AI的基础,接下来就是一些博弈的方法了。
AI
对每步进行估分,程序中做如下工作:将每个
AI落子在该位置,用以上打分规则为AI打分,并将得到的分数加一。
假设玩家落子在该点,为玩家打分,然后将所有的分值汇总。取最高分作为这个位置
“位置估分”,下棋的时候,既可以考虑到自
AI。作实
AI,和“位置估分”对下,结果是一胜一负。谁先子,谁

AI智能
,根据下一步由谁
,AI对任何一个局面根据前面估分方法给出一个分数,我们把这个估分方法汇总成一个
AI是轮流落子,可以将人的
AI越有利,估分越小则表明对AI
AI选择都是从它可能的走法树的某层节点,返回评估值中最大点。而用
从而形成一棵极大极小搜索树,然后根据深度优
可以最后得到固定搜索深度下的一个最好的走法。我做了下试验,单纯应用博弈树,
100ms之内让AI考虑完整的两步,由于组合爆炸,当需要考虑三步的时候,就需
6s左右,4步就需要1分钟。拿两步来和一步估分作比较,虽然比较慢,但是确实有了

AI智能
,但是,层数是个很重要的信息.因为下棋时如果能2步
,不应选择4步获胜。对于输的棋型层数就更重要,AI必须尽可能拖延输的时间,就有
AI化险为夷。这样,可以通过设置一个dep值。深度约浅,dep越大,用
和得到的得分相乘,得到搜索节点的得分,再进行以上算法,进一步提高AI的智能。
α-β剪枝,提高AI速度

AI走,圆形框节点是该人走.比如C节点,它需要从E和F当中选取
E为2,当搜索F节点时,因为F是人走的节点,那么F需要从K L
中选取最小的,因为K已经是1,也就是说F<=1,那么L,M就不需要搜索,因此就
α剪枝。然后看A节点,该人走了,需要从C和D中选取最小值,因为C节点是2,
G是7,那么D至少是7。因此,D的其他节点不必再考虑,就发生如上图所示的β剪

AI下棋节点:
剪枝:如果当前节点的值不比父节点的前兄弟节点的大值大,则舍弃此节点。
剪枝:如果当前节点子节点的值不比当前节点的前兄弟节点中的最小值小,则舍弃该子节


剪枝:如果当前节点的某子节点的值不比当前节点的前兄弟节点中的最大值大,则舍弃该

剪枝:如果当前节点的子节点的值不比当前的父节点的

前兄弟节点中的最小值小则舍弃此

α-β剪枝,可以极大的减少搜索的数量,很多时候,能把几十亿的搜索数量,缩小到
1。
AI速度
根据五子棋的特
,可以产生一个棋面搜索范围。记录当前棋面所有棋子的最左最右最上最下点构成的矩形,
3步以上。这样在棋子较少的时候,搜索节点的
AI的速度提高一倍左右。
AI速度
所以,可以每次只考虑当前得分前十的节点进行下一步搜索,

AI速度

但是,递归的效率是低的,而且很明显,有很多重复搜
所以,我们可以用一个表,记录下所有搜索过节点的情况,然后只要遇到搜索到
就可以直接得到结果。置于这个“表”是什么,就是一个置换表,利用Zobrist算法,
Hash处理,使在表中查找的时间大大缩短,这样AI的速度又能提高一个数量级。
AI速度
AI的速度。我们
,每个线程包含着从第二层
,然后等所有线程结束后,将所有线程的结果进行汇总,选出最大


AI算法的固定性,所以一担玩家一次获胜,按照相同的走法,必然会再次获胜。但除
,一个局面很多时候没有绝对最好的走法。而是有一些都不错的走法,那
,然后随机选择它们中的一种走法,避免AI的走法的
.这样最简单的方法避免固定方法AI必输。
AI自学习,不再同一个地方犯错
,这样AI在下棋时还可能会重蹈覆辙。因此在每盘棋结束时,
AI输,则进行大于搜索深度的步数回退。可以把倒数为搜索深度数目的局面定为目标
,找到不会导出必败目标局面的局面。然后记录下这
AI就不会犯曾经犯过的错误,达到自学

AI的五子棋游戏即可诞生!
算法可简可繁,要看你对自己五子棋程序智能的要求, 人机对战的意思就是人和电脑
,也就是说电脑会思考如何下棋....其实这才是五子棋程序的核心.如果只实现人与人对战
,是一件很简单的事情,无非就是绘制棋盘,然后绘制下棋的效果,再写个下棋合法性判断,
....大概就搞定了....所以核心其实是人机对战的电脑那部分人工智能.这东西吧,可
,不过主要的几个设计要点就是搜索算法和估值算法,这两个是最主要的,还有
cpu的计算机多线程思考的设计....通过一些手段让电脑变
,例如利用一些遗传算法之类的让电脑具有学习能力,可以在失败中吸取
,开局库,历史启发之类的一大堆......但是总而言之,这一系列算法的设计没有一个标准,
,更快那就是好算法.国内有一个叫王晓春的写过一本叫
pc游戏编程( 人机博弈)>>的书,这是一本研究人机博弈程序很经典的书,书的后面还附了
,你可以参考一下.下面是csdn的下载地址,你也可以自己去搜一下.


采用了博弈树的方法,应用了剪枝和最大最
介绍五子棋程序的数据

结构、评分规则、胜负判断


关于盘面情况的表示,以链表形式表示当前盘面的情况,目的是可以允许用户进行悔棋、

其中Step结构的表示为:
int m; //m,n表示两个坐标值
int n;
char side; //side表示下子方


其中FIVE_MAX_LINE表示盘面最大的行数。
同时由于需要在递归搜索的过程中考虑时间和空间有效性,只找出就当前情况来说相对比
CountList来表示

其中类CBoardSituiton为:
每一步的列表
//机器所下的那一步
该种盘面状态所得到的分数

对于下子的重要性评分,需要从六个位置来考虑当前棋局的情况,分别为:-,|,/,\,//,\\
实际上需要考虑在这六个位置上某一方所形成的子的布局的情况,对于在还没有子的地方
主要是为了说明在这个地方下子的重要性程度,设定了一个简

基本的规则如下:
5, 如果是机器方的话给予100000分,如果是人方的话给予-100000 分;
4或者是双死4或者是死4活3,如果是机器方的话给予10000分,如果是
10000分;
3,如果是机器方的话给予5000分,如果是人方的话给予-5000 分;
3活3,如果是机器方的话给予1000分,如果是人方的话给予-1000 分;
4,如果是机器方的话给予500分,如果是人方的话给予-500分;
3,如果是机器方的话给予200分,如果是人方的话给予-200分;
2,如果是机器方的话给予100分,如果是人方的话给予-100分;
3,如果是机器方的话给予50分,如果是人方的话给予-50分;
2,如果是机器方的话给予10分,如果是人方的话给予-10分;
2,如果是机器方的话给予5分,如果是人方的话给予-5分;
2,如果是机器方的话给予3分,如果是人方的话给予-3分。
实际上对当前的局面按照上面的规则的顺序进行比较,如果满足某一条规则的话,就给该
然后退出规则的匹配。注意这里的规则是根据一般的下棋规律的一个总结,


实际上,是根据当前最后一个落子的情况来判断胜负的。实际上需要从四个位置判断,以
45度角和135度角的线,目的是看在这四个方向
如果是的话,就表示该盘棋局已经分出胜负。具


注意下面的核心的算法中的变量currentBoardSituation,表示当前机器最新的盘面情况,
表示第一层子节点可以选择的较好的盘面的集合。核心的算法如下:
-MAXINT; //对初始根节点的value赋值
该函数是根据当前的盘面情况来比较得到比较好的可以考虑的几个盘面的情况,可以根据
也就是说在第一层节点选择的时候采用贪婪算
直接找出相对分数比较高的几个形成第一层节点,目的是为了提高搜索速度和防止堆栈

* pBoard;
->value,max);
取value和pBoard

->value中大的赋给根节点

找出那一个得到最高分的盘面
当前下子方改为人
其中对于Search函数的表示如下:实际上核心的算法是一个剪枝过程,其中在这个搜索过
(1)当前棋局情况;(2)当前的下子方,可以是机器(max)或者是
(min);(3)父节点的值oldValue;(4)当前的搜索深度depth。

TRUE)
{
if(mode==max)
value=select(value,search(successor
Board,min,value,depth+1),max);
else
value=select(value,search(successor
Board,max,value,depth+1),min);
}
return value;

这里goal(board)<>0表示已经可以分出胜负
}
注意这里的goal(board)函数是用来判断当前盘面是否可以分出胜负,而evlation(board)是对

下面是Select函数的介绍,这个函数的主要目的是根据 PlayerMode情况,即是机器还是用

&& mode==max)|| (a< b && mode==min)

在Windows操作系统下,用VC++实现了这个人机对战的五子棋程序。和国内许多只是
在智力上和时间有效性上都要
同时所讨论的方法和设计过程为用户设计其他的游戏(如象棋和围棋等)提

都要进行两步。第一步是优先级的计算,然后是打分。但是我在想能不能通
也就是说,我们能够从所打的分中间识别出优先
这使我想到了数学十进制中的位。反正我最主要是从四个方向进行判定,所以某些优先
我们在给其打分的时候,就将它的分数设为处在其优先级之上的十分之一。其实我
因为我最后选取最优位置的方法是累加法,算出分
这样的话,即使在四个方向上某较低优先级都出现了,但是即使全加起来
这样的话,如果出现优先级较高的情况,那么我们计算的结
变省去了一些不必要的分类,使得算法更直接,更


对于已经五子相连的情况,我们在此部分算法中不再实现,因为这个时候胜
checkboard类的judge的方法便可以了。这只不过是在电脑AI后
judge的代码复制过去罢了。
进而算出分数最高的位置。那么我在这里想运用试
然后看看相连的情况,然后打分,并将分数加至存
这样遍历完所有的空位的时候,我们便完成了打分工


Checker指针变量,用来保存相连的一串棋子
color用来保存棋子的颜色,count用来保存相连的棋子的个数,temptr1
temptr2用来保存两端的棋子指针。文中的我们是电脑哦!
所打

我们先赢,赢了再说
对方要赢,如果我们不能先赢,那么一定得先阻拦
双方均不会赢,如果我们能成活四,那么
1 000
双方均不会赢,100
则要先置棋,牵着敌方的鼻子走,因为下过本步棋之后敌方
0000
双方均不会赢,如果对方能成活四,那么
10
双方均不会赢,如果我们能成活三,则要
1 0000
双方

均不会赢,
1 000
双方均不会赢,
1 00
双方均不会赢,如果我们能成活二,则要
1 0
双方均不会赢,
1

对于敌方构不成威胁的情况,(即活四以下,但是这里我们把冲四
因为假设我们的分数是long型的,那么它的长度是32位,转换
位数也就是10位,因而我选了最大的分数为1000 000 000,这样刚好能满足要

但是这里有个小小的问题,就是在刚开始落子的时候,怎么落?我想这个时候我们可以
0了。当对方先下的时候,我们就
AI,这样接下来问题便简单了,因为一定会出现


相关文档
最新文档