﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-saha-文章分类-棋类AI</title><link>http://www.cppblog.com/saha/category/21375.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 04 Jan 2019 14:04:42 GMT</lastBuildDate><pubDate>Fri, 04 Jan 2019 14:04:42 GMT</pubDate><ttl>60</ttl><item><title>中国象棋开发全记录</title><link>http://www.cppblog.com/saha/articles/171636.html</link><dc:creator>saha</dc:creator><author>saha</author><pubDate>Mon, 16 Apr 2012 09:44:00 GMT</pubDate><guid>http://www.cppblog.com/saha/articles/171636.html</guid><wfw:comment>http://www.cppblog.com/saha/comments/171636.html</wfw:comment><comments>http://www.cppblog.com/saha/articles/171636.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saha/comments/commentRss/171636.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saha/services/trackbacks/171636.html</trackback:ping><description><![CDATA[<p dir="ltr">最新版本地址：<a href="/Files/saha/CCS0.5.rar">/Files/saha/CCS0.5.rar</a><a href="/Files/saha/CCS0.3.rar"><br /></a>参考资料：<a href="http://www.sluijten.com/winglet/">http://www.sluijten.com/winglet/</a>&nbsp;&nbsp;&nbsp;<a href="http://www.frayn.net/beowulf/theory.html">http://www.frayn.net/beowulf/theory.html</a><br /><strong>Monday, April 16, 2012 总体框架设计<br /></strong>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 分离UI和AI算法，以利于以后不同平台的移植。其中AI算法部分做到平台无关性。<br />&nbsp;&nbsp; &nbsp;基本框架如下：<br /><br /><img height="423" alt="" src="http://www.cppblog.com/images/cppblog_com/saha/CChess/总体框架.jpg" width="551" /><br /><br /><br /><strong>Thursday, April 19, 2012简单的UI及棋子移动实现<br /></strong>设置棋盘为16*16的数组。冗余部分便于棋子出界判断。<br />红棋值从8-14，黑棋值从16-22.其余部分全部为0<br /><img height="347" alt="" src="http://www.cppblog.com/images/cppblog_com/saha/CChess/鼠标点击流程.jpg" width="378" /><br />基本UI实现，基本象棋类实现。<br /><br /><strong>Tuesday, April 24, 2012实现棋子规则<br /></strong>1. &nbsp; 实现各个棋子的走法规则，将照面规则暂时未实现。<br />2. &nbsp; 实现了将军判断函数<br />3. &nbsp; 支持了翻转功能<br />4. &nbsp; 实现一些细节。结束后不允许再走棋，重新初始化棋盘等。<br /><br /><strong>Thursday, April 26, 2012<br /></strong>重新实现棋子走法规则判断逻辑如下：<br /><img height="792" alt="" src="http://www.cppblog.com/images/cppblog_com/saha/CChess/走法规则.jpg" width="380" /><br /><br /><strong>Friday, May 11, 2012<br /></strong>1.基本的人工智能算法&nbsp;<strong>Alpha-Beta</strong><strong>搜索.</strong><br />其中 </p>
<p dir="ltr"></p>
<div style="display: inline! important">Alpha为搜索到的最好值，beta为对方的最坏值。一般起始时设置Alpha为负无穷，beta为正无穷。<br />之所以在递归内部要传入-beta和-alpha是为了反应对方走棋，及简化代码。即表示我方的最好值与对方的最坏值的对应关系。<br />
<p>&nbsp;</p>
<blockquote dir="ltr" style="margin-right: 0px">
<p>&nbsp;</p></blockquote></div>
<blockquote></blockquote>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-weight: normal; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->int&nbsp;AlphaBeta(int&nbsp;depth,&nbsp;int&nbsp;alpha,&nbsp;int&nbsp;beta)&nbsp;<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(depth&nbsp;==&nbsp;0)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Evaluate();&nbsp;//如果是第一层返回局面估计<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;GenerateLegalMoves();&nbsp;//产生当前的所有下一步走法<br />&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(MovesLeft())&nbsp;//排序并遍历每一步走法<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MakeNextMove();&nbsp;//走一步<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//递归搜索<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;val&nbsp;=&nbsp;-AlphaBeta(depth&nbsp;-&nbsp;1,&nbsp;-beta,&nbsp;-alpha);&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UnmakeMove();&nbsp;&nbsp;//回退该步<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//如果比最坏情况还坏，则返回最坏情况<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(val&nbsp;&gt;=&nbsp;beta)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;beta;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//如果比最好情况差，则保持它为目前探索到的最好情况<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(val&nbsp;&gt;&nbsp;alpha)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alpha&nbsp;=&nbsp;val;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　　<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;alpha;&nbsp;&nbsp;//返回目前找到的最好情况，其对应的走法即是搜索到的最好着法<br />}</div>
<p><img height="348" alt="" src="http://www.cppblog.com/images/cppblog_com/saha/CChess/alpha-beta.jpg" width="707" border="0" longdesc="" /></p>
<p>从根局面开始，默认的Alpha和Beta值分别为己方被杀和对方被杀的分值，递归搜索己方所有走法。<br />当一个走法返回的估计值A1&gt;=Beta时，认为该节点是杀棋,则己方必选择该走法，因此其余节点不用再搜索，只要保存该走法并返回，称为beta截断。<br />当一个走法A1&lt;=Alpha时，认为该节点比己方所能接受的最差结果更差，因此不保留该走法(这里无法做截断)。<br />当一个走法A1&gt;Alpha &amp;&amp; A1&lt;Beta时，认为该节点是主变例节点(PV <span class="Apple-style-span" style="font: medium Simsun">Principal Variation</span>)，需要保留该走法，并将Alpha设为A1，并继续探索剩余节点。<br />对于对方走棋时，只要将上下限取反调用同一alpha-beta函数即可。<br />2.增加自动测试功能，能电脑vs电脑测试<br />3.增加不同的电脑级别，以搜索层数划分<br /></p>
<div>
<div style="padding-right: 0px; display: inline! important; padding-left: 0px; padding-bottom: 0px; padding-top: 0px">4.增加悔一步棋的功能<br />V0.1版本完成</div></div>
<p>&nbsp;</p>
<blockquote>
<p>&nbsp;</p>
<div></div></blockquote>
<p><strong>Wednesday, May 12, 2012<br />1.</strong>增加声音开关功能<br /><strong>2.</strong>悔棋功能，最多支持悔512步<br /><strong>V0.2</strong>版本完成<br /><br /><strong>Wednesday, May 30, 2012<br />1.</strong>空着向前裁剪<br />空着裁剪的含义就是如果当前的局势优势巨大，即使不走这一步棋让对方走还是能产生beta截断，相当于少搜索了一层，能大大缩短搜索的时间。<br />存在的一个问题是在残局阶段，当存在无棋可走的情况下，使用空着恰恰避开了这种问题，从而导致估计不准确，因此在残局时需要进行判断来决定是否使用空着裁剪，一种流行的简单做法是到了残局就不进行空着裁剪。<br /><strong>2.</strong>静态搜索</p>
<p>相对稳定的局面称为&#8220;安静&#8221;(Quiet)或&#8220;寂静&#8221;(Quiescent)的局面，它们需要通过&#8220;静态搜索&#8221;(Quiescence Search)来达到。<br />目前采用简单的策略即只考虑所有的吃子走法来进行搜索。</p>
<p><strong>3.</strong>局面检验<strong>Zobrist</strong>键值<br />Zobrist用一个整型数值来代表一个棋的局面，目前采用的做法是对于每一个棋盘位置的14种情况(7种红棋，7种黑棋)产生14*256个随机数，每一个棋局是所有存在棋子的棋盘位置的随机数异或而得到的一个整型值(称为Zobrist键值)，据前人经验这种表示方法出现冲突的概率非常小。这种表示方法的另一个好处是去除一个棋子和添加一个棋子都是异或操作(由于异或的异或得到原来的值)，利用Zobrist键值可以做重复局面判断，长将判断。<br />V0.3版本完成<br /></p>
<p dir="ltr" style="margin-right: 0px">&nbsp;<strong>Thursday, June 14, 2012<br /></strong>1.修改Zobrist键值从原来的32位无符号变为int64（据前人经验32位存在冲突有很多争论，而64位虽然仍存在冲突的局面，<span class="Apple-style-span" style="font-size: 10pt; line-height: normal; font-family: Simsun">但是实战中这种情况非常罕见</span>）。<br />2.修改重复局面检查函数，从以前的把所有历史局面全部检查到现在的只检查最近6步，以减少不必要的检查。<br />3.增加主变例搜索优化。即对当前搜索存在PV走法时，对以后的每一个节点搜索的搜索上下限先设为[-Alpha-1, -Alpha],如果当前节点的值比pv值要大则重新对该节点进行正常搜索，否则丢弃该节点进行下一个节点的搜索。据统计可以提供10%的搜索效率，在程序中直接使用没有明显的变化。。。</p>
<p dir="ltr" style="margin-right: 0px">4.增加置换表。<br />基本思想：每一个搜索过的局面都记录他的平均值，以避免以后搜索到同一局面时的重复计算工作。<br />使用zobrist-hash值记录每个搜索过的局面。一个置换表的元素包括以下结构：<br /></p>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000">&nbsp;&nbsp; &nbsp;CCSINT64&nbsp;ulZobristKey; //hash-key<br />&nbsp;&nbsp;&nbsp;&nbsp;CCSUINT32&nbsp;depth&nbsp;:&nbsp;</span><span style="color: #000000">8</span><span style="color: #000000">; &nbsp; //搜索深度<br />&nbsp;&nbsp;&nbsp;&nbsp;CCSUINT32&nbsp;flags&nbsp;:&nbsp;</span><span style="color: #000000">2</span><span style="color: #000000">; &nbsp; &nbsp;//标志，分为PV,BETA,ALPHA3种<br />&nbsp;&nbsp;&nbsp;&nbsp;CCSUINT32&nbsp;mvBest&nbsp;:&nbsp;</span><span style="color: #000000">22</span><span style="color: #000000">; &nbsp;//该局面下的最佳走法<br />&nbsp;&nbsp;&nbsp;&nbsp;CCSINT32&nbsp;nValue; &nbsp; &nbsp;//该局面下的估计值</span></div>其中flags3种标志代表不同含义。<br />pv表示主变例搜索的结果，即这时的nValue是个精确值，可以直接使用<br />BETA表示该局面的估计值至少为nValue。<br />ALPHA表示该局面的估计值最多为nValue。<br />在alpha-beta搜索中的使用如下：<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(HASH_PV&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;pItem</span><span style="color: #000000">-&gt;</span><span style="color: #000000">flags)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;nValue;&nbsp;&nbsp;<br />}<br /></span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(HASH_BETA&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;pItem</span><span style="color: #000000">-&gt;</span><span style="color: #000000">flags&nbsp;</span><span style="color: #000000">&amp;&amp;</span><span style="color: #000000">&nbsp;nValue&nbsp;</span><span style="color: #000000">&gt;=</span><span style="color: #000000">&nbsp;nBeta)&nbsp;<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;nBeta;<br />}&nbsp;<br /></span><span style="color: #0000ff">else</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(HASH_ALPHA&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;pItem</span><span style="color: #000000">-&gt;</span><span style="color: #000000">flags&nbsp;</span><span style="color: #000000">&amp;&amp;</span><span style="color: #000000">&nbsp;nValue&nbsp;</span><span style="color: #000000">&lt;=</span><span style="color: #000000">&nbsp;nAlpha)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;nAlpha;<br />}&nbsp;&nbsp;<br /></span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;VALUEUNKNOWN;</span></div>是否正确还有待测试。<br />使用置换表时除了判断zobrist-hash值是否相同外,还要判断当前走棋方是否与记录中的走棋方一致,不一致不能替换.<br /><p>&nbsp;Friday, July 27, 2012<br />修改置换表的使用方式<br />去除输棋后的不文明用语<br />在腾讯游戏中进行测试，使用中等水平在1000分以下玩家中胜率95%+</p><img src ="http://www.cppblog.com/saha/aggbug/171636.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saha/" target="_blank">saha</a> 2012-04-16 17:44 <a href="http://www.cppblog.com/saha/articles/171636.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>