zoukankan      html  css  js  c++  java
  • JavaScript中国象棋程序(8)

    “JavaScript中国象棋程序” 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序。这是教程的第8节。

    程序的最终效果点击这里查看

    这一系列共有9个部分:

    0、JavaScript中国象棋程序(0)- 前言

    在这最后一节,我们的主要工作是使用开局库、对根节点的搜索分离出来、以及引入PVS(Principal Variation Search)主要变例搜索。

    8.1、开局库

    这一节我们引入book.js文件。该文件中定义了一个二维数组BOOK_DAT。这个数组就是开局库,保存的数据格式如下:

    [lock, mv, vl]

    其中,lock = zobristLock >>> 1(无符号右移1位,高位补0)

    mv是步骤

    vl是权重(随机选择走法的几率,仅当两个相同的lock有不同的mv时,vl的值才有意义,这是为了实现走棋的随机性)。

    开局库是按照lock排序的,因此可以用二分查找。找到一项以后,把它前后lock相同的所有项都取出,从中随机选择一个mv。

    压缩开局库的容量,所有对称的局面只用一项,所以当一个局面在BOOK_DAT中找不到时,还应该试一下它的对称局面是否在BOOK_DAT中。

    8.2、对根节点的特殊处理

    现在由于开局库的加入,开局程序的走法具有了一定的随机性。但如果是开局库中没有的局面,程序的走法依然是固定不变的。我们在根节点处,对搜索得到的分值,做小范围的浮动,以此实现走法的随机性。

    vlBest += Math.floor(Math.random() * RANDOMNESS) - Math.floor(Math.random() * RANDOMNESS);

    其中,RANDOMNESS = 8。新的分值会在区间(vlBest - 8, vlBest + 8 )浮动。

    我们把对根节点的搜索分离出来,这有以下好处:

    1、更方便实现走法的随机性

    2、没有必要尝试Beta截断(根节点处Beta正无穷);

    3、省略了检查重复局面、获取置换表、空步裁剪等步骤。

    8.3、PVS主要变例搜索

    经过前面的工作,走法已经得到了很好的排序,好的走法会先被搜索。这是PVS的基础。

     a                                                                                 图b

    假设第一个走法是最好的走法,没有引发剪枝,A点的搜索区间为(0, 100),走法1得到估值30。由于30  > 0,所以A点的alpha变为30,以后的搜索区间变为(30, 100),所以B2点的搜索区间为(-100, -30)。

    可以进一步大胆地考虑,假设第1个走法就是最好的走法,那么后面走法得到的估值不会落在区间(30, 100)。所以从A点的第2个走法开始,要做的就是验证这种假设,搜索区间为(30, 31)。由于搜索区间很小,搜索速度会很快。返回值vl有3种情况。

    1)、vl <= 30。说明走法不比第1个走法好,假设成立。

    2)、vl >= 100。返回值比A点的原有搜索边界beta还大,应该剪枝,假设成立。

    3)、30 < vl < 100。走法比第1个走法好,假设不成立。

     3种情况时,走法不成立,应该对该走法重新以(30, 100)区间进行搜索。如果得到40,则该走法就是最好的走法,后续搜索又对该走法进行假设验证,区间为(40, 41)。

    8.4、长将判负策略

    程序会调用repStatus函数,判断局面是否出现重复,以及长将,这在第6节已经介绍。如果出现长将,会得到-BAN_VALUE(其中,BAN_VALUE = MATE_VALUE - 100),再根据杀棋步数做调整。但是由于长将判负并不是对某个单纯局面的评分,而是跟路线有关的,把这个分值直接存入置换表就不太合适了。

    我们的解决办法就是:获取置换表时把“利用长将判负策略搜索到的局面”过滤掉。如果某个局面分值在WIN_VALUE(MATE_VALUE - 200)和BAN_VALUE之间,那么这个局面就是“利用长将判负策略搜索到的局面”。如果是通过repStatus函数得到了和棋的分值,会同样被过滤掉。

    我们仍旧把“利用长将判负策略搜索到的局面”记录到置换表,因为这些局面提供的最佳走法是有启发价值的。反过来说,如果“利用长将判负策略搜索到的局面”没有最佳走法,那么这种局面就没有必要记录到置换表了。

    8.5、核心代码说明

    本节的代码可以在 Github 下载,也可以直接clone

    git clone -b step-8 https://github.com/Royhoo/write-a-chinesechess-program

    Position中新增或修改的主要属性和方法:

    1)、bookMove()

    获取开局库中的走法。

    Search中新增或修改的主要属性和方法:

    1)、searchRoot(depth)

    对根节点的搜索。

  • 相关阅读:
    关于js计算非等宽字体宽度的方法
    [NodeJs系列]聊一聊BOM
    Vue.js路由管理器 Vue Router
    vue 实践技巧合集
    微任务、宏任务与Event-Loop
    事件循环(EventLoop)的学习总结
    Cookie、Session和LocalStorage
    MySQL 树形结构 根据指定节点 获取其所在全路径节点序列
    MySQL 树形结构 根据指定节点 获取其所有父节点序列
    MySQL 创建函数报错 This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators
  • 原文地址:https://www.cnblogs.com/royhoo/p/6425912.html
Copyright © 2011-2022 走看看