[转载]十四步实现拥有强大AI的五子棋游戏

又是本人一份人工智能作业……首先道歉,从Word贴到Livewrter,好多格式没了,也没做代码高亮……大家凑活着看……想做个好的人机对弈的五子棋,可以说需要考虑的问题还是很多的,我们将制作拥有强大AI五子棋的过程分为十四步,让我来步步介绍。

第一步,了解禁手规则

做一个五子棋的程序,自然对五子棋需要有足够的了解,现在默认大家现在和我研究五子棋之前了解是一样多的。以这个为基础,介绍多数人不大熟悉的方面。五子棋的规则实际上有两种:有禁手和无禁手。由于无禁手的规则比较简单,因此被更多人所接受。其实,对于专业下五子棋的人来说,有禁手才是规则。所以,这里先对“有禁手”进行一下简单介绍:

五子棋中“先手必胜”已经得到了论证,类似“花月定式”和“浦月定式”,很多先手必胜下法虽然需要大量的记忆,但高手确能做到必胜。所以五子棋的规则进行了优化,得到了 “有禁手”五子棋。五子棋中,黑棋必然先行。因此“有禁手”五子棋竞技中对黑棋有以下“禁手”限制:“三三禁”:黑棋下子位置同时形成两个以上的三;“四四禁”:黑棋下子位置同时形成两个以上的四;“长连禁”:六子以上的黑棋连成一线。黑棋如下出“禁手“则马上输掉棋局。不过如果“连五”与“禁手”同时出现这时“禁手”是无效的。所以对于黑棋只有冲四活三(后面会有解释)是无解局面。反观白棋则多了一种获胜方式,那就是逼迫黑棋必定要下在禁点。

为了迎合所有玩家,五子棋自然需要做出两个版本,或者是可以进行禁手上的控制。

 

第二步,实现游戏界面

这里,我制作了一个简单的界面,但是,对于人机对弈来说,绝对够用。和很多网上的精美界面相比,我的界面也许略显粗糙,但,开发速度较高,仅用了不到半天时间。下面我们简单看下界面的做法。

界面我采用了WPF,表现层和逻辑层完全分开,前台基本可以通过拖拽完成布局,这里就不做过多介绍。根据界面截图简单介绍

clip_image002

1处实际上市两个渐变Label的拼接,2、3是两个label,4、5实际上是两个Button,但是没有做事件响应。通过按钮6、7、8、9 的控制,修改label和Button的Content属性。也许有人会奇怪,为什么Button会丝毫看出不出有Button的影子,这里战友whrxiao写过一个Style如下

 1 <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
 2 <Setter Property="Template">
 3 <Setter.Value>
 4 <ControlTemplate TargetType="{x:Type Button}">
 5 <Grid>
 6 <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True"/>
 7 </Grid>
 8 </ControlTemplate>
 9 </Setter.Value>
10 </Setter>
11 </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越大,用dep和得到的得分相乘,得到搜索节点的得分,再进行以上算法,进一步提高AI的智能。

 

第八步:应用α-β剪枝,提高AI速度

在搜索博弈树的过程中,实际上搜索有很多点是多余的,例如下图

clip_image012

图中,方形框节点是该AI走,圆形框节点是该人走.比如C节点,它需要从E和F当中选取最大的值。目前已经得出E为2,当搜索F节点时,因为F是人走的节点,那么F需要从K L M中选取最小的,因为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的五子棋游戏即可诞生!

这篇文章,让VAllen受益良多,正准备做个高人工智能的五子棋 for Windows 8。

本文转载自:http://www.cnblogs.com/Blog_SivenZhang/archive/2010/06/13/1757677.html

转载于:https://www.cnblogs.com/VAllen/articles/WuZiQiStep14Ai.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

爬虫项目(二)---采集从03月02号以来的世界各国疫情数据

该内容出自黑马程序员教程 采集从03月02号以来的世界各国疫情数据 步骤&#xff1a; Ⅰ&#xff0c;重构项目(一)的代码&#xff0c;以提高扩展性 把功能封装到一个类中每一个小功能变成一个方法通过run方法启动爬虫 import requests import re import json from bs4 impor…

【原创】StreamInsight查询系列(二十)——查询模式之检测间隙事件

上篇文章介绍了查询模式中如何检测异常事件&#xff0c;这篇博文将介绍StreamInsight中如何检测间隙事件。 测试数据准备 为了方便测试查询&#xff0c;我们首先准备一个静态的测试数据源&#xff1a;// 创建数据源&#xff0c;要注意的是4:16和4:30之间存在的事件间隙 var sou…

【数据结构基础应用】【查找和排序算法】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、顺序查找2、折半查找3、直接插入排序4、选择排序5、冒泡排序6、希尔排序7、快速排序8、堆排序9、排序算法性能比较10、所有算法的code&#xff08;C语言&#xff09;前言 本章总结查找和排序算法&#xff1a;顺序查找、折…

爬虫项目(三)---采集最近一日全国各省疫情数据

该内容出自黑马程序员教程 采集最近一日全国各省疫情数据 当然&#xff0c;数据来源仍然是丁香园新型冠状病毒肺炎疫情实时动态首页 url&#xff1a;https://ncov.dxy.cn/ncovh5/view/pneumonia 思路&#xff1a;首先需要先确定全国各省疫情数据的位置 全国各省份的疫情数据…

计算机专业博士后排名,排名丨计算机专业领域TOP10,性价比超高!

原标题&#xff1a;排名丨计算机专业领域TOP10&#xff0c;性价比超高&#xff01;相信各位家长、同学已经看过太多专业的排名&#xff0c;我问过很多理科生将来想学什么专业&#xff0c;听到频率最高的还是计算机专业。似乎大家都知道&#xff0c;学计算机是比较挣钱的&#x…

js 命名规范

转载于:https://www.cnblogs.com/zjx2011/p/3165043.html

爬虫项目(四)---采集从01月22日以来全国各省疫情数据

采集从03月02日以来全国各省疫情数据 当然&#xff0c;数据来源仍然是丁香园新型冠状病毒肺炎疫情实时动态首页 url&#xff1a;https://ncov.dxy.cn/ncovh5/view/pneumonia 分析 确定01月22日以来全国各省疫情数据的URL 由项目(三)可以获取全国各省疫情数据点击可下载&…

纠错码trick和数据压缩trick

纠错码和压缩算法是同一枚硬币的两面。 两者都来自于对冗余的想法。 纠错码被视为向消息或文件中添加冗余的原则性方法。而压缩算法正好相反&#xff0c;他们会从消息或文件中移除冗余。 压缩和纠错并不是彼此抵消的&#xff0c;相反&#xff0c;好的压缩算法会移除抵消冗余&am…

常用算法总结(穷举法、贪心算法、递归与分治算法、回溯算法、数值概率算法)

博主联系方式&#xff1a; QQ:1540984562 QQ交流群&#xff1a;892023501 群里会有往届的smarters和电赛选手&#xff0c;群里也会不时分享一些有用的资料&#xff0c;有问题可以在群里多问问。 目录1、穷举法2、贪心算法3、递归与分治算法4、回溯算法5、数值概率算法1、穷举法…

工程师英语和计算机证书查询,点击进入国家硬件维修工程师证书查询网站

工程师证书查询网站人力资源社会保障部指定查询国家职业资格证书的唯一官方网站。涵盖全国各省市、各行业、各央企颁发的证书。电脑硬件维修工程师网上能查看国家工信部硬件维修工程师证书查询网址&#xff1a;http://www.ceiaec.org/index.htm工程师证书编号在网上怎么查询如果…

敏捷开发“松结对编程”系列之七:问题集之一

本文是“松结对编程”系列的第七篇。&#xff08;之一&#xff0c;之二&#xff0c;之三&#xff0c;之四&#xff0c;之五&#xff0c;之六&#xff0c;之七&#xff0c;之八&#xff09;刚刚参加完MPD 2011深圳站&#xff0c;在演讲中间及后来媒体采访&#xff0c;被问到了一…

C++中的sort函数对二维数组排序是按照什么准则?

遇到的一个疑惑&#xff0c;现记录如下&#xff1a; int main() {vector<vector<int>> envelopes { {5, 8},{6, 7},{6, 4},{2, 3},{8,9} };sort(envelopes.begin(), envelopes.end());for (int i 0;i < envelopes.size();i)cout << envelopes[i][0]<…

数学专业学计算机哪一行,计算数学

计算数学(一个理科专业)语音编辑锁定讨论上传视频计算数学是由数学、物理学、计算机科学、运筹学与控制科学等学科交叉渗透而形成的一个理科专业。中文名计算数学外文名Computational Mathematics所 属数学计算数学专业定义编辑语音计算数学也叫做数值计算方法或数值分析。主…

图片透视变换操作

由于照相机硬件设备本身的误差&#xff0c;可能会导致镜头畸变&#xff0c;从而导致照相机拍摄到的照片产生失真现象&#xff0c;此时可以通过透视变换去适当的校正。 大概的思路&#xff1a;在原图像上确定四个点&#xff0c;然后再新图像上也确定四个点&#xff0c;通过warp…

dp笔记:关于DP算法和滚动数组优化的思考

从网上总结了一些dp的套路以及对滚动数组的一些思考&#xff0c;现记录如下&#xff0c;希望以后回顾此类算法时会有所帮助。 目录1、DP算法经验1、DP算法核心&#xff1a;2、DP算法类别以及例题例1&#xff1a;三步问题例2&#xff1a;最小路径和例3&#xff1a;乘积最大子数组…

【C++ grammar】引用

1、引用就是另一个变量的别名 2、通过引用所做的读写操作实际上是作用与原变量上 引用方式&#xff1a; int x; int & rxx; or int x, &rxx;在C中&是取地址&#xff0c;在C中&放在一个变量的定义前&#xff0c;那就是引用 注意&#xff1a; 这种引用是错误的…

flash安全策略的理解

flash安全策略的理解 2011-06-25 01:48 11人阅读 评论(0) 收藏 举报 一直以来对flash的安全策略是一头雾水&#xff0c;什么安全沙箱&#xff0c;跨域策略文件一堆东西乱七八糟&#xff0c;搞不清楚。不过纠结到现在已经基本上理解了。 flash的安全问题在官方手册上有足够的解…

【C++ grammar】nullptr and Dynamic Memory Allocation (空指针和动态内存分配)

空指针 1.1. 0带来的二义性问题 C03中&#xff0c;空指针使用“0”来表示。0既是一个常量整数&#xff0c;也是一个常量空指针。C语言中&#xff0c;空指针使用(void *)0来表示有时候&#xff0c;用“NULL”来表示空指针(一种可能的实现方式是#define NULL 0) 1.2. C标准化委…

No module named ‘skimage.metrics‘在Anaconda3中的解决方法

1&#xff0c;进入Anaconda Prompt 2&#xff0c;进行安装&#xff1a; pip install scikit-image 3&#xff0c;若还是报错&#xff0c;尝试进行更新一下 pip install scikit-image --upgrade

【C++ grammar】数据类型转换、列表初始化

布尔类型 C语言在其标准化过程中引入了bool、true和false关键字&#xff0c;增加了原生数据类型来支持布尔数据。 布尔类型的大小&#xff08;所占的存储空间&#xff09;依赖于具体的编译器实现。也可以用 sizeof运算符得到其占用的空间 Conversion between bool and int 0…