人工智能作业-五子棋AI算法的改进方法_第1页
人工智能作业-五子棋AI算法的改进方法_第2页
人工智能作业-五子棋AI算法的改进方法_第3页
人工智能作业-五子棋AI算法的改进方法_第4页
人工智能作业-五子棋AI算法的改进方法_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、人工智能作业制作拥有强大AI五子棋的过程分为十四步,让我来步步介绍。第一步,了解禁手规则做一个五子棋的程序,自然对五子棋需要有足够的了解,现在默认大家现在和我研究五子棋之前了解是一样多的。以这个为基础,介绍多数人不大熟悉的方面。五子棋的规则实际上有两种:有禁手和无禁手。由于无禁手的规则比较简单,因此被更多人所接受。其实,对于专业下五子棋的人来说,有禁手才是规则。所以,这里先对宥禁手”进行一下简单介绍:五子棋中先手必胜”已经得到了论证, 类似花月定式”和浦月定式”, 很多先手必胜下法虽然需要大量的记忆,但高手确能做到必胜。所以五子棋的规则进行了优化,得到了宥禁手”五子棋。五子棋中,黑棋必然先行。

2、因此宥禁手”五子棋竞技中对黑棋有以下禁手”限制:土三禁”黑棋下子位置同时形成两个以上的三;四四禁”黑棋下子位置同时形成两个以上的四;长连禁”六子以上的黑棋连成一线。黑棋如下出禁手则马上输掉棋局。不过如果连五,与禁手”同时出现这时禁手”是无效的。所以对于黑棋只有冲四活三(后面会有解释)是无解局面。反观白棋则多了一种获胜方式,那就是逼迫黑棋必定要下在禁点。为了迎合所有玩家,五子棋自然需要做出两个版本,或者是可以进行禁手上的控制。第二步,实现游戏界面这里,我制作了一个简单的界面,但是,对于人机对弈来说,绝对够用。和很多网上的精美界面相比,我的界面也许略显粗糙,但,开发速度较高,仅用了不到半天时间。下

3、面我们简单看下界面的做法。界面我采用了WPF,表现层和逻辑层完全分开,前台基本可以通过拖拽完成布局,这里就不做过多介绍。根据界面截图简单介绍1处实际上市两个渐变Label的拼接,2、3是两个label,4、5实际上是两个Button但是没有做事件响应。通过按钮6、7、8、9的控制,修改label和Button的Content属性。也许有人会奇怪,为什么Button会丝毫看出不出有Button的影子,这里战友whrxiao写过一个Style如下这里我们把这个Style称为Stylel。界面逻辑上,将是否开始、是否禁手和是否电脑先行作为两个全局变量的布尔型值,通过设置和判断bool型值进行逻辑上的

4、控制。中间的棋盘是个canvas,一个15*15的Grid放?茜Button并将每个Button应用Stylel开始时候透明度设为0,也就是根本看不到,在下棋的时候改变Button的背景和透明度,实现落子的效果,因为Grid的位置关系,所以可看起来好像是下在横竖的交线处。第三步,进行输赢判断:因为规则不同,无禁手”和有禁手”的输赢判断自然不同。先看无禁手:这个比较简单,遍历每个位置,然后从这个位置开始,分别判断它的四个方向:即横、竖、左上到右下、左下到右上。每个方向从中间点开始,往两边数连子数,然后将两个方向的连字数加和再加一(中间的棋子)。如果得到大于等于5,那么就说明下子方赢棋。对于有禁手

5、的五子棋,输赢判断还需要判断禁手,禁手的判定较为复杂。将待判断点放入黑棋子。然后搜索待判断点周边棋盘;还原棋盘;利用搜索结果依次对各方向进行分析,判断黑棋放入后所产生的棋型是否形成长连或形成某种四连或三连的的棋型。若形成长连,判定为禁手,返回长连禁手标识。若形成某种四连或三连的棋型,该棋型统计数加1,再对下一个方向进行判断,直到各个方向分析结束。若四连棋型或三连棋型的统计数大于1,则返回为禁手。其余情况返回非禁手。第四步:构造棋型估分宥禁手”规则比较复杂,涉及到比较多下棋方面的技巧,而且对算法的思路没有丝毫影响,所以下面我们主要考虑无禁手规则下的AI设计。若设计好无禁手AI,只需要让AI执黑时

6、坚决不下到禁手点,就可以很快构造有禁手的AI。虽然这种方式没有利用有禁手规则下的技巧,但这些技巧只需要修改下面所讲到的估分函数即可。我们可以将五子棋的连珠可以分为以下几种:成5:即构成五子连珠活4:即构成两边均不被拦截的四子连珠。死4:一边被拦截的四子连珠活3:两边均不被拦截的三字连珠死3:一边被拦截的三字连珠活2:两边均不被拦截的二子连珠死2:一边被拦截的二子连珠单子:四周无相连棋子根据五子棋的技巧,可以将五子棋的棋型用连珠进行分类,分类过后我们按照威力给每种棋型打分。因为五子棋一次只落一子,因此很容易理解,双活三和三活三的威力是一样的,类似情况不多做解释。程序中,我以100分为满分,对棋型

7、进行了以下打分:成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打分,并将得到的分数加一。然后,假设玩家落子在该点,为玩家打分,然后将所有的分值汇总。取最高分作为这个位置的估分,接下来就是取分数最高的位置下棋了。位置估分”,下棋的时候,既可以考虑到自己攻击对手,又能

8、考虑到对对手的防御,可以说,很多时候可以顶上考虑两步的AI。作实验,从网上下载了一个用博弈做的AI,和位置估分”对下,结果是一胜一负。谁先子,谁赢得胜利。而且一步估分毫无疑问是最快的,即使遍历所有位置,也能很快的做出决策。第六步:应用博弈树,提高AI智能做五子棋的博弈,自然会用到博弈树,这里我说下自己的思路。在对弈中,根据下一步由谁来走,AI对任何一个局面根据前面估分方法给出一个分数,我们把这个估分方法汇总成一个评估函数,并返回分值。据此来选才i下一步的走法。由于人和AI是轮流落子,可以将人的估分也算入,并将前面加负号。那么,估值越大表明对AI越有利,估分越小则表明对AI越不利。那么每次AI选

9、择都是从它可能的走法树的某层节点,返回评估值中最大点。而用户总是从走法树的某层节点中选择最小点,从而形成一棵极大极小搜索树,然后根据深度优先搜索,可以最后得到固定搜索深度下的一个最好的走法。我做了下试验,单纯应用博弈树,可以在100ms之内让AI考虑完整的两步,由于组合爆炸,当需要考虑三步的时候,就需要6s左右,4步就需要1分钟。拿两步来和一步估分作比较,虽然比较慢,但是确实有了一定智能。第七步:考虑层数,提高AI智能上面的设计对于返回值是统一处理的,但是,层数是个很重要的信息.因为下棋时如果能2步获胜,不应选择4步获胜。对于输的棋型层数就更重要,AI必须尽可能拖延输的时间,就有更大的可能让A

10、I化险为夷。这样,可以通过设置一个dep值。深度约浅,dep越大,用dep和得到的得分相乘,得到搜索节点的得分,再进行以上算法,进一步提高AI的智能。第八步:应用a-3剪枝,提高AI速度在搜索博弈树的过程中,实际上搜索有很多点是多余的,例如下图图中,方形框节点是该AI走,圆形框节点是该人走.比如C节点,它需要从E和F当中选取最大的值。目前已经得出E为2,当搜索F节点时,因为F是人走的节点,那么F需要从KLM中选取最小的,因为K已经是1,也就是说F=1,那么L,M就不需要搜索,因此就发生了a剪枝。然后看A节点,该人走了,需要从C和D中选取最小值,因为C节点是2,而G是7,那么D至少是7。因此,D

11、的其他节点不必再考虑,就发生如上图所示的3剪枝。总结上面规律,我们可以得到剪枝方法如下:当前为AI下棋节点:a剪枝:如果当前节点的值不比父节点的前兄弟节点的大值大,则舍弃此节点。3剪枝:如果当前节点子节点的值不比当前节点的前兄弟节点中的最小值小,则舍弃该子节点和该子节点的所有后兄弟节点。当前为用户下棋节点:a剪枝:如果当前节点的某子节点的值不比当前节点的前兄弟节点中的最大值大,则舍弃该子节点和该子节点的所有后兄弟节点。3剪枝:如果当前节点的子节点的值不比当前的父节点的前兄弟节点中的最小值小则舍弃此节点。经过a-3剪枝,可以极大的减少搜索的数量,很多时候,能把几十亿的搜索数量,缩小到几亿,那么,

12、就可以把搜索深度增1。第九步:应用下棋范围,提高AI速度当前节点的子节点的数量和排列顺序对于搜索的速度起着至关重要的影响。根据五子棋的特点,可以产生一个棋面搜索范围。记录当前棋面所有棋子的最左最右最上最下点构成的矩形,我们认为下一步棋的位置不会脱离这个框3步以上。这样在棋子较少的时候,搜索节点的数量大大减少。可以将AI的速度提高一倍左右。第十步:利用棋型得分,提高AI速度因为每种下法都对应一种得分,所以,可以每次只考虑当前得分前十的节点进行下一步搜索,大大减少了搜索范围,可以进一步增加搜索的深度。第十一步:利用置换表,提高AI速度我们一般用递归的方法实现博弈树,但是,递归的效率是低的,而且很明

13、显,有很多重复搜索的节点,所以,我们可以用一个表,记录下所有搜索过节点的情况,然后只要遇到搜索到的节点,就可以直接得到结果。置于这个表”是什么,就是一个置换表,利用Zobrist算法,进彳THash处理,使在表中查找的时间大大缩短,这样AI的速度又能提高一个数量级。第十二步:利用多线程,提高AI速度我们其实可以利用多核技术,利用多个线程,让算法实现并行计算,提高AI的速度。我们在第一层用一个线程分配器把第二层的候选节点分配给多个线程,每个线程包含着从第二层一个候选节点开始的搜索,然后等所有线程结束后,将所有线程的结果进行汇总,选出最大值。并行的程序,可以将速度提高一倍左右。第十三步:利用随机化

14、算法,让确定方法不能必胜由于AI算法的固定性, 所以一担玩家一次获胜, 按照相同的走法, 必然会再次获胜。 但除了必杀招或者必防招,一个局面很多时候没有绝对最好的走法。而是有一些都不错的走法,那么可以把这些评分差不多走法汇集起来,然后随机选择它们中的一种走法,避免AI的走法的固定.这样最简单的方法避免固定方法AI必输。第十四步:让AI自学习,不再同一个地方犯错上面的算法还没有自学习的能力,这1AI在下棋时还可能会重蹈覆辙。因此在每盘棋结束时如果AI输,则进行大于搜索深度的步数回退。 可以把倒数为搜索深度数目的局面定为目标局面, 从倒数深度加一步局面进行预测,找到不会导出必败目标局面的局面。然后

15、记录下这个局面和前面的局面,并据此修改评分函数。这样AI就不会犯曾经犯过的错误,达到自学习的效果。做到以上十四步,一个拥有强大AI的五子棋游戏即可诞生!五子棋算法可简可繁,要看你对自己五子棋程序智能的要求,人机对战的意思就是人和电脑下,也就是说电脑会思考如何下棋.其实这才是五子棋程序的核心.如果只实现人与人对战的话,是一件很简单的事情,无非就是绘制棋盘,然后绘制下棋的效果,再写个下棋合法性判断胜负判断.大概就搞定了.所以核心其实是人机对战的电脑那部分人工智能.这东西吧,可以研究的很多,不过主要的几个设计要点就是搜索算法和估值算法,这两个是最主要的,还有提高电脑思考销率的方法就有多cpu的计算机

16、多线程思考的设计.通过一些手段让电脑变得更像人类棋手的,例如利用一些遗传算法之类的让电脑具有学习能力,可以在失败中吸取教训,开局库,历史启发之类的一大堆.但是总而言之,这一系列算法的设计没有一个标准,只要能让你的电脑下棋下的更聪明,更快那就是好算法.国内有一个叫王晓春的写过一本叫的书,这是一本研究人机博弈程序很经典的书,书的后面还附了一个五子棋的程序实例,你可以参考一下.下面是csdn的下载地址,你也可以自己去搜一下.http:/ 这里用变量当前搜索中可以选择的所有新的盘面情况对象的集合:CListCountList;其中类CBoardSituiton为:classCBoardSituatio

17、n(CListStepList;/每一步的列表charFiveAreaFIVE_MAX_LINEFIVE_MAX_LINE;structStepmachineStep;机器所下的那一步doublevalue;/该种盘面状态所得到的分数二、评分规则对于下子的重要性评分,需要从六个位置来考虑当前棋局的情况,分别为:实际上需要考虑在这六个位置上某一方所形成的子的布局的情况,对于在还没有子的地方落子以后的当前局面的评分,主要是为了说明在这个地方下子的重要性程度,设定了一个简单的规则来表示当前棋面对机器方的分数。基本的规则如下:判断是否能成5,如果是机器方的话给予100000分,如果是人方的话给予-10

18、0000分;判断是否能成活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分;判

19、断是否能成双活2,如果是机器方的话给予10分,如果是人方的话给予-10分;判断是否能成活2,如果是机器方的话给予5分,如果是人方的话给予-5分;判断是否能成死2,如果是机器方的话给予3分,如果是人方的话给予-3分。实际上对当前的局面按照上面的规则的顺序进行比较,如果满足某一条规则的话,就给该局面打分并保存,然后退出规则的匹配。注意这里的规则是根据一般的下棋规律的一个总结,在实际运行的时候,用户可以添加规则和对评分机制加以修正。三、胜负判断实际上,是根据当前最后一个落子的情况来判断胜负的。实际上需要从四个位置判断,以该子为出发点的水平,竖直和两条分别为45度角和135度角的线,目的是看在这四个方

20、向CountList来表示-,|,/,/,是否最后落子的一方构成连续五个的棋子,如果是的话,就表示该盘棋局已经分出胜负。具体见下面的图示:四、搜索算法实现描述注意下面的核心的算法中的变量currentBoardSituation,表示当前机器最新的盘面情况CountList表示第一层子节点可以选择的较好的盘面的集合。核心的算法如下:voidMainDealFunction()(value=MAXINT;/对初始根节点的value赋值CalSeveralGoodPlace(currentBoardSituation,CountList);/该函数是根据当前的盘面情况来比较得到比较好的可以考虑的几

21、个盘面的情况,可以根据实际的得分情况选取分数比较高的几个盘面,也就是说在第一层节点选择的时候采用贪婪算法,直接找出相对分数比较高的几个形成第一层节点,目的是为了提高搜索速度和防止堆栈溢出。pos=CountList.GetHeadPosition();CBoardSituation*pBoard;for(i=0;ivalue=Search(pBoard,min,value,0);Value=Select(value,pBoardvalue,max);/取value和pBoardvalue中大的赋给根节点for(i=0;ivalue)找出那一个得到最高分的盘面(currentBoardSitua

22、tion=pBoard;PlayerMode=min;/当前下子方改为人Break;其中对于Search函数的表示如下:实际上核心的算法是一个剪枝过程,其中在这个搜索过程中相关的四个参数为:(1)当前棋局情况;(2)当前的下子方,可以是机器(max)或者是人(min);(3)父节点的值oldValue;(4)当前的搜索深度depth。doubleSearch(CBoardSituation&board,intmode,doubleoldvalue,intdepth)(CListm_DeepList;if(deptholdvalue)=TRUE)(if(mode=max)value=se

23、lect(value,search(successorBoard,min,value,depth+1),max);elsevalue=select(value,search(successorBoard,max,value,depth+1),min);)returnvalue;)elseif(goal(board)0)/这里goal(board)0表示已经可以分出胜负returngoal(board);elsereturnevlation(board);)注意这里的goal(board)函数是用来判断当前盘面是否可以分出胜负,而evlation(board)是对当前的盘面从机器的角度进行打分。

24、下面是Select函数的介绍,这个函数的主要目的是根据PlayerMode情况,即是机器还是用户来返回节点的应有的值。doubleSelect(doublea,doubleb,intmode)if(abmmmode=max)|(ab&mode=min)returna;elsereturnb;)五、小结在Windows操作系统下,用VC+实现了这个人机对战的五子棋程序。和国内许多只是采用规则或者只是采用简单递归而没有剪枝的那些程序相比,在智力上和时间有效性上都要好于这些程序。同时所讨论的方法和设计过程为用户设计其他的游戏(如象棋和围棋等)提供了一个参考。大部分算法中,都要进行两步。第一步

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

26、接,更容易理解。首先我们先来看一下置棋时的优先级分布以及我为其所指定的打分方案:首先声明的是,对于已经五子相连的情况,我们在此部分算法中不再实现,因为这个时候胜负已经分出,我们调用checkboard类的judge的方法便可以了。这只不过是在电脑AI后再将judge的代码复制过去罢了。我们的目的是给棋盘上的空位进行打分,进而算出分数最高的位置。那么我在这里想运用试探法,因为我觉得这样的话代码比较好写。所谓试探法就是,我们找出棋盘上的所有空位,然后我们假设这个空位上放置的是白棋,然后看看相连的情况,然后打分,并将分数加至存储该空位分数的变量;再假设这个空位上放置的是黑棋,然后再看相连的情况,然后

27、打分,并将分数加至存储该空位分数的变量;这样遍历完所有的空位的时候,我们便完成了打分工作,然后再比较空位的分数,选出分数最高的空位,该位置即为最优的位置。下面是一些情况的优先级排列:(假设我们是白棋)下列情况均为在该空位置棋之后:在实现的时候,我们还需要定义两个临时的Checker指针变量,用来保存相连的一串棋子前后的位置的状态。color用来保存棋子的颜色,count用来保存相连的棋子的个数,temptrl与temptr2用来保存两端的棋子指针。文中的我们是电脑哦!情况所打分数color=1&count=5我们先崛,赢了再说1000000000Color=0&count=5对方要赢,如果我们不能先赢,那么一定得先阻拦100000000Color=1&count=4&!temptr1&!temptr2双方均不会赢,如果我们能成活四,那么1000敌方必输,所以要先置棋0000Color=1&count=4&(!temptr1&temptr2)|(temptr1

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论