• JavaScript中国象棋程序(4)


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

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

    这一系列共有9个部分:

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

    上一节的程序,电脑是在随机走棋,这样太没劲了。这一节我们的程序中加入极大极小搜索算法,这样程序会稍微有点智商。不过棋力不高,也就是普通小学生的水平吧。

    4.1、局面评估

    局面评估,就是判断局面对红方(或黑方)的优势,并把优势量化。棋子价值可用以下不等式表达:

    > 车 > 马、炮 > 仕、相 > 兵

    棋子价值可以简单量化为:

    10

    20

    20

    40

    45

    90

    1000

    但是棋子价值是跟位置有关系的,比如兵在过河前价值很小,过河后价值大涨。在我们的程序中,兵的位置价值数组如下:

    [	// 兵
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  9,  9,  9, 11, 13, 11,  9,  9,  9,  0,  0,  0,  0,
        0,  0,  0, 19, 24, 34, 42, 44, 42, 34, 24, 19,  0,  0,  0,  0,
        0,  0,  0, 19, 24, 32, 37, 37, 37, 32, 24, 19,  0,  0,  0,  0,
        0,  0,  0, 19, 23, 27, 29, 30, 29, 27, 23, 19,  0,  0,  0,  0,
        0,  0,  0, 14, 18, 20, 27, 29, 27, 20, 18, 14,  0,  0,  0,  0,
        0,  0,  0,  7,  0, 13,  0, 16,  0, 13,  0,  7,  0,  0,  0,  0,
        0,  0,  0,  7,  0,  7,  0, 15,  0,  7,  0,  7,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    ]
    

    在初始位置,中兵价值15,其他四个位置价值都是7。位于九宫的中心位置时,价值达到最高的44。这个数组肯定不是凭空想象出来的,应该是象棋百科全书网的前辈,经过无数次的试验得到的。

    帅的位置价值数组如下:

    [	// 帅
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  2,  2,  2,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0, 11, 15, 11,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    ]

    帅是无价的。没有了帅,游戏是要结束的。数组里的1、2、11、15仅仅表示了帅的位置分值,并不是说帅只值这个数。由于兵、帅的位置没有重合,这两个数组可以合并。

    如此一来,每种棋子就都会有一个与绝对位置相关的价值数组,因此我们的程序里有一个常量数组PIECE_VALUE[7][256]。该数组只提供了红方的位置分值。想获得黑方的位置分值,使用前面介绍过的函数SQUARE_FLIP(sq)翻转一下位置即可。此外,我们在Position对象中定义vlWhite和vlBlack两个属性,分别表示红方和黑方棋子价值。每次调用addPiece(sq, pc, bDel)增删棋子时,都会更新vlWhite和vlBlack。获取红方优势的局面评估函数:

    Position.prototype.evaluate = function() {
      var vl = this.vlWhite - this.vlBlack;
      return vl;
    }

    4.2、简单的两层搜索

     

    圆形节点为红方走棋的局面,方形节点为黑方走棋局面,红色数字为局面估值(也就是红方的优势)。深度优先遍历这个搜索树。

    1)、初始局面为A,该红方走棋。红方有B1、B2、B3三种走法。

    2)、假设红方选择第一种走法,走到了局面B1。

    3)、在局面B1,该黑方走棋。黑方有C1、C2、C3三种走法。

    4)、C1、C2、C3是叶子节点,调用局面评估函数,算得局面估值(也就是红方优势)分别是8、10、15。

    5)、回到局面B1,此时该黑方走棋。黑方后完后,红方优势越小,对黑方越有利。黑方自然会走到对自己最有利的C1局面。此时我们认为,B1局面的估值是8。

    6)、同样的方法,B2、B3的估值分别是5、10。

    7)、回到初始局面A,红方走棋。现在已经知道,选择第一种走法,最终估值为8;选择第二种走法,最终估值为5;选择第三种走法,最终估值为10。估值就是红方的优势,红方自然会选择对自己优势最大的走法,也就是走到局面B3。

    8)、可知行棋路线为A -> B3 -> C7。

    A点选择估值最大的局面,称为极大点;B1、B2、B3选择估值最小的局面,称为极小点。

    4.3、极大点搜索算法

    在程序中,定义一个全局变量,表示搜索深度:

    var MINMAXDEPTH = 3;

    也就是说,程序会搜索3层。当然你也可以改为4,这样电脑的棋力会更强。当搜索深度超过4时,电脑会走得很慢很慢。根节点的层次是MINMAXDEPTH,每往下搜索一层,层次减1。当层次为0时,不再向下搜索,而是调用评估函数计算估值。

    伪代码如下:

    // 极大点搜索
    Search.prototype.maxSearch = function(depth) {
      如果depth等于0,调用评估函数并返回分值
      
      var vlBest = 负无穷;			// 初始最优值
      var mvs = 当前局面全部走法;		// 生成当前局面的所有走法
      var value = 0;
      for (var i = 0; i < mvs.length; i ++) {
    	执行招法mvs[i]
    	调用极小点搜索算法,深度设为depth-1,并将返回值赋给value
    	撤销招法mvs[i]
    	
    	if (value > vlBest) {		// 寻找最大估值
    	  vlBest = value;
    	  if (depth == MINMAXDEPTH) {	// 如果回到了根节点,需要记录根节点的最佳走法
    	    记录根节点的最佳走法
    	  }
    	}	
      }  
      return vlBest;			// 返回当前节点的最优值
    }

    4.4、极小点搜索算法

    极小点搜索这与极大点搜索很相似,但有3处不同:

    1)、初始最优值为正无穷。

    2)、递归调用的是极大点搜索算法。(而极大点搜索算法会递归调用极小点搜索算法)

    3)、寻找的是最小估值。

    4.5、极大极小搜索算法

    if (该红方走棋) {

      调用极大点搜索算法,深度为MINMAXDEPTH

    } else {

      调用极小点搜索算法,深度为MINMAXDEPTH

    }

    4.6、核心代码说明

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

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

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

    1)、result

    对局结果,有4种状态。

    0表示结果未知(正在战斗中,这有在这一状态下,程序才响应用户的点击事件)

    1表示你赢了(也就是电脑输了)

    2表示和棋

    3表示你输了(也就是电脑赢了)

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

    1)、vlWhite

    红方所有棋子的价值

    2)、vlBlack

    黑方所有棋子的价值

    3)、addPiece(sq, pc, bDel)

    此方法增加了一项功能,就是在增减棋子时,更新vlWhite和vlBlack。

    4)、checked()

    判断老将是否被对方攻击。具有攻击性的棋子是车、马、炮、兵。我们要判断对方的这四类棋子是否攻击到了己方老将,以及是否将帅对脸。算法如下:

    1、假设帅(将)是车,判断它是否能吃到对方的车和将(帅)。如果能吃到对方的车,说明己方帅(将)被对方车攻击;如果能吃到将(帅),说明存在将帅对脸。

    2、假设帅(将)是炮,判断它是否能吃到对方的炮。

    3、假设帅(将)是马,判断它是否能吃到对方的马,需要注意的是,帅(将)的马腿用的数组是ADVISOR_DELTA,而不是KING_DELTA。

    4、假设帅(将)是过河的兵(卒),判断它是否能吃到对方的卒(兵)。

    5)、makeMove()

    改方法做了一些改进。如果移动棋子后,发现老将被对方攻击,也就是说这步棋是去送死的,那么就要撤销对棋子的移动,并返回false。

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

    1)、mvResult

    这是搜索算法找到的最佳走法,随后电脑就会执行这步棋。

    2)、maxSearch()

    极大点搜索算法

    3)、minSearch()

    极小点搜索算法

    4)、maxMinSearch()

    极大极小搜索算法

  • 相关阅读:
    应对高并发场景的redis加锁技巧
    Spring中@Transactional事务回滚(含实例具体解说,附源代码)
    计算机网络10--计算机网络体系结构简单介绍
    IIS身份验证的配置
    AMR音频文件格式分析
    IOS版本号被拒的经历
    两分钟读懂《成大事者不纠结》——读书笔记
    同一个TextView设置不同的颜色和大小
    似非而是的程序猿悖论---为什么救火比防火更加吃香?
    OS
  • 原文地址:https://www.cnblogs.com/royhoo/p/6425658.html
Copyright © 2020-2023  润新知