日升家园

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 497|回复: 2

中国象棋软件引擎审局揭秘系列1

[复制链接]

864

主题

1936

帖子

2191

积分

理事长

Rank: 9Rank: 9Rank: 9

UID
39
精华
0
威望
3
贡献值
24
金币
21325
在线时间
1237 小时
注册时间
2013-8-28
最后登录
2021-7-19

优秀版主论坛元老

QQ
发表于 2013-10-25 14:01:16 | 显示全部楼层 |阅读模式
编者注:

fenon,原名应为叶剑锋,棋海无涯界面作者,曾获世界奥林匹克大赛第四名。

中国象棋软件的点滴
      从2004年, 走在世界的前列, 编写象棋软件开始, 到2006年, 学习研究并改写全新的引擎, 已经是三年多的时间,。这三年, 是一个技术/思维从粗糙到成熟的过程, 获益良多。           这段时间写了很多文章, 转载在各大网站/论坛上, 可惜已经如风中的铃声, 消失在时间中, 现在摘录的一些, 作为一种回忆, 保存起来。
    在编写象棋软件的期间, 得到象棋世家创始人Poor的帮助, 得到朋友raylau和face的支持, 谢谢他们。在编写象棋软件的期间, 认识了纵马奔流, 象棋奇兵, 棋天大圣, 还有现在的新起之秀象棋旋风, 天机, 象眼, 是他们让我在一次又一次的自我挑战中,不断完善自己.             回想起自己所编写的软件战胜旧一代棋软巨头的时候, 我也明白我的程序, 总有一天也会被淘汰, 消失在人们的眼中, 这是历史发展的必然, 自然会有更好更新的软件, 出现在人们眼前。感谢所有为中国象棋软件做出努力的人们, 愿棋软常青。
      2006.12.4 08:20 作者:fenon
      人工智能系列--nullmove
        Nullmove 实战剖析

            int attackpieces = (side==RED?(Rattackpieces) : (Battackpieces));

              int nulldepth = CtrlNullEx ? 4 : 3;

            if (CtrlNullmove
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& !NullVerify <BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& !InChk[ply]<BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& !mate_threat <BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& attackpieces&#160; &#160;&#160; &#160;&#160;&#160;> 0<BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& !avoid_donull<BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& depth>=2<BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& (depth-nulldepth<=0 || zEval(side, ply, 0)>=beta)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;&& do_null) <BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ // 控制nullmove的危险程度

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;null_score = -zSearch(-beta, -beta + 1, 1 - side, depth -
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;nulldepth, ply + 1, false/*do_null = false*/);

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;if (null_score >= beta)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;{
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; {
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;bool verifyok = false;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;// null-move verify
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (depth < nulldepth)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;verifyok = true;
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;else
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;NullVerify = true;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;if (zSearch(beta-1, beta, side,
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;depth-nulldepth+1, ply, false) >= beta)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; verifyok = true;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;NullVerify = false;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;///*
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;if (verifyok) {
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; StoreHash();
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;}
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;//*/
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (verifyok)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;return (null_score);
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; }
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;}

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;else if (null_score == -vMate + ply + nulldepth)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; mate_threat = 1;
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}

&#160; &#160;&#160; &#160;nullmove是一种有损剪枝, 会带来一些不准确的结果, 所以控制nullmove的危险程度是很有必要的

&#160; &#160;&#160; &#160;在何种条件下可以进行nullmove? 我的实现算法中, 采取了以下几种限制
&#160; &#160;&#160; &#160;1. 攻击子>0
&#160; &#160;&#160; &#160;2. depth>=2 最后一层直接剪掉是非常危险的
&#160; &#160;&#160; &#160;3. 不被将军
&#160; &#160;&#160; &#160;4. 上一层已经做了nullmove, 本层就不再做了
&#160; &#160;&#160; &#160;5. 当校验nullmove是否正确时,不再使用nullmove
&#160; &#160;&#160; &#160;6. 当同样的盘面,以前曾经使用过nullmove,并且发现被对方杀死时(参考crafty)
&#160; &#160;&#160; &#160;7. 层数比nulldepth要大, 或者优势很大时 (参考fruit)
&#160; &#160;&#160; &#160;8. avoid_donull发生在如果保存在hash表中有记录,但是却不能返回结果时

&#160; &#160;&#160; &#160;其中7是相当好的一种限制条件, 比通过子力的多少来进行限制有效得多

&#160; &#160;&#160; &#160;因为nullmove的危险性, 需要测试nullmove结果是否正确, 通过统计, 子力多的时候, 基本不会出现verify失败的结果,
&#160; &#160;&#160; &#160;这多少也印证了crafty中, 采用子力多少进行nullmove限制的原因

&#160; &#160;&#160; &#160;nullmove的verify是通过层数多一层的搜索来校验的方法来校验, 所以
&#160; &#160;&#160; &#160;depth小于nulldepth时,可以认为不需要校验
&#160; &#160;&#160; &#160;(考虑这个时候计算的结果跟nullmove搜索结果的比较)

&#160; &#160;&#160; &#160;nullmove搜索的结果可以保存重用(参考crafty)

&#160; &#160;&#160; &#160;这里对象眼nullmove的实现, 提几点建议

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; if (Search.bNullMove
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&& !bNoNull <BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&& !mvsLast.bcCheck <BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&& Tree.pos.NullMoveOkay()<BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&& nDepth>1<BR>&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&& (nDepth-NULL_DEPTH-1<=0 ||
&#160; &#160;&#160; &#160;Tree.pos.Evaluate(vlAlpha, vlBeta)>=vlBeta)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; {
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;Tree.pos.MakeMove(0);
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;vl = -SearchFull(Tree, -vlBeta, 1 - vlBeta, nDepth
&#160; &#160;&#160; &#160;- NULL_DEPTH - 1, NO_NULL);
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;Tree.pos.UndoMakeMove();
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (vl >= vlBeta)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;if (Tree.pos.NullMoveSafe() ||
&#160; &#160;&#160; &#160;nDepth<=NULL_DEPTH || SearchFull(Tree, vlBeta - 1, vlBeta, nDepth -
&#160; &#160;&#160; &#160;NULL_DEPTH, NO_NULL) >= vlBeta)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;{
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; return FAIL_SOFT ? vl : vlBeta;
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;}
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;else if (vl == nThisPly + 2 - MATE_VALUE)
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;bMateThreat = TRUE;
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; }

&#160; &#160;&#160; &#160;其中的关键是(nDepth-NULL_DEPTH-1<=0 || Tree.pos.Evaluate(vlAlpha,
&#160; &#160;&#160; &#160;vlBeta)>=vlBeta), 这样可以避免很多危险的情况, 使用了nullmove启发剪枝!
                                                                                                   作者:fenon
在攻与防的對立统一中寻求突破...
回复

使用道具 举报

1

主题

42

帖子

42

积分

一星会员

Rank: 1

UID
1775
精华
0
威望
0
贡献值
0
金币
117
在线时间
5 小时
注册时间
2014-4-9
最后登录
2014-4-22
QQ
发表于 2014-4-11 13:15:17 | 显示全部楼层
这个真看不懂
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|日升家园 ( 蜀ICP备18009257号 )

GMT+8, 2024-5-17 17:47 , Processed in 0.063177 second(s), 27 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表