zoukankan      html  css  js  c++  java
  • 中国象棋引擎初学后记

      以前写过一个带AI的象棋程序,但是那时候上大二,写的代码奇乱无比,然后最近就抽空重写了一个,这次写的时候嫌在写界面太麻烦,就直接按照UCCI协议写个引擎,然后直接网上找个界面,简单、粗暴、有效,目前我的程序在开中局的时候3秒内可以达到7-8层,残局的时候5秒可以到9-10层,美中不足的是评估函数实在太粗暴了。

      我是用c#写的,而且本人貌似只会c#。。期间无数次的参考象棋百科全书网,网址http://www.xqbase.com,有兴趣的可以去看下,很不错的一个网站。

      一个引擎说白了就是一个能根据输入局面等相关信息,给出相应反馈信息的控制台程序。

      引擎接受界面传来的消息【控制台的输入】,根据这个消息处理引擎内部需要做的事情,然后以控制台输出的方式作出反馈。我只知道界面会截获引擎的标准输入输出流,然后和引擎交互信息,具体怎么实现的,没有去研究。。

     一个引擎大概可以分以下几大步:

    1.监听输入

    2.解析指令

    3.运行相应的计算

    4.输出相应反馈

    在我自己的程序中,大概分为下面几部分:

    1.监听输入

    2.解析指令

    3.执行计算

    4.输出反馈

    5.记录日志

    首先需要解析界面发来的UCCI指令,这就需要一个解析指令的类,在我的程序中是UCCIHelper类;然后我们知道了界面让我们干什么,但是我们还没干,就需要根据指令和指令参数作出相应的动作。在我的程序中只支持以下几种指令:

    1.ucci:

    这个指令是在引擎刚被界面启动之后传给引擎的,属于引导期指令,他告诉引擎现在采用的协议是UCCI协议,引擎在这个时候要初始化引擎的一些数据设置一些参数等,最后反馈引擎名称,版本,版权等信息,最后反馈 ucciok,以这个反馈来告诉界面准备工作就绪,此后引擎将处于空闲状态【引擎刚启动的时候处于引导状态】。

    2.position fen <fen> moves <...>

    这个指令告诉引擎一个局面和在这个局面之后又走了哪几步,引擎就需要根据这个信息设置引擎内部表示棋盘的数组值,以及当前走子方,记录走过的局面,等等,

    此后引擎不做任何反馈,等待别的指令,引擎依然处于空闲状态。

    3.go [ponder][depth][time] []movestogo]

    这个指令告诉引擎开始计算,引擎将根据这个指令的参数设定数值,开始计算,此后引擎进入思考状态,在计算完成后,或者超时后,引擎输出bestmove xx ponder xx作为反馈,然后返回空闲状态。

    例如:go depth 7,引擎将以当前设置的棋盘数值和相关数据开始深度为7的计算,当计算完成后,反馈 bestmove b2e2 ponder b9c7,以此反馈告诉界面当前局面最佳走法是中炮,并且猜测对手可能跳马。

    4.ponderhit

    这个指令告诉引擎,猜测的对手走法命中,此指令通常是引擎处于后台思考状态中,收到这个指令后,引擎自己的时钟开启。

    5.stop

    这个指令要求引擎立即停止计算,反馈最佳走法。

    我的程序就只支持这五种指令,但已经和界面交互的很好了【对我来说,嘿嘿】

    这些都是和界面的交互等等就不说了,主要记录下核心部分的设计和遇到的问题:

    1.数据表示

    数据表示,无非就是棋子和棋盘,在我的程序中棋子用的是byte 1到7来表示七种不同的类型棋子,棋盘用的是一个大小为256的一维数组。

    2.产生走法

    这一部分也是整个引擎的相当大的一部分,要设计出好的走法生成函数还是很值得研究的,因为这一部分是程序运行时占用时间最多的。首先,我们需要找到棋盘上一个自己的棋子,然后根据他的走法规则,产生他能走的所有走法,最后把棋盘上所有自己的棋子能走的走法汇总到一个集合中,然后返回这个集合供其他部分使用。

    3.搜索

    搜索就是你一步我一步轮流走,把可能的情况都逐个推演。

    4.评估局面

    搜索的目的就是逐一推演可能的情况,但是都推演了一遍,怎么比较哪一种对自己比较好,就需要对局面做评估,给一个局面的好坏打一个分数。

    核心就这么四部分,就目前来说,所有的博弈软件都逃脱不了这种架构,但是就每一步而言每一种博弈软件都不尽相同。

    在我的程序中,产生走法用到了一些数组,来避免不必要的循环,但是车和炮的走法不知道怎么可以避免循环,搜索用的是α-β剪枝,这个算法对待搜索的节点顺序相当敏感,正规的软件中都有N多种排序,而在我的程序中只有两种排序,第一种是先搜索吃子的走法,按吃最有价值的子排序,剩下的不吃子走法按历史表排序,各种各样的排序策略被称为启发,评估那一部分直接照搬的开源的象棋引擎 ElephantEye 的子力价值表作为评估函数的核心,在评估这一部分比较专业一点还要使用静态搜索来克服水平线效应,但是我的程序搜索速度比较慢,吃不消静态搜索带来的资源开销,就没有用,有一个能加快搜索的方法就是空步向前裁剪,这个大概能提高2-3层,但是在残局和有些特殊局面的情况下,空步向前裁剪会出大问题,比如那些不走棋要比走任何一步都好的局面,例如困憋,在我的程序中,也没有扛得住能提高2-3层这个诱惑使用了这个技术,但是他的负面效应我仅仅做了在残局的时候不允许使用空步向前裁剪策略这一预防方法,比如开局的时候,红方中炮,我的程序执黑,他在不使用空步向前裁剪的时候,3-9层的搜索结果都是跳正马,但是使用了空步向前裁剪之后3-7层是跳正马,从第八层开始,他就认为跳边马比跳正马要好,这是不合理的,具体原因没有找到,还有一个问题就是对于同一个局面奇数层和偶数层搜索返回的分值经常是一个正一个负,数值也不同,按道理来说,对于同一个局面,奇数层和偶数层不应该差到变符号的地步,顶多就是数值不同而已。

    现在把源码地址贴下,里面的注释甚至要多于这篇文章,有兴趣的可以下载下来看看,有共同兴趣的可以加我qq好友,嘿嘿。

    源代码地址:

    https://files.cnblogs.com/lipf/EvilGeniusChessEngine.rar

    转载请注明出处

  • 相关阅读:
    JDK版本1.6和6.0到底指什么
    分布式存储Memcache替代Session方案
    Spring事务隔离级别和传播特性
    高性能并发系统架构应该如何设计?关键是什么?12306
    Idea无法DEBUG的问题
    springboot(三 使用mybatis +springboot 完成简单的增删改查)
    springboot(二 如何访问静态资源和使用模板引擎,以及 全局异常捕获)
    spring boot (入门简介 demo)
    java1.8新特性(optional 使用)
    java1.8 新特性(关于 match,find reduce )操作
  • 原文地址:https://www.cnblogs.com/lipf/p/2419692.html
Copyright © 2011-2022 走看看