计算机象棋循序渐进
 
象棋百科全书网 (webmaster@xqbase.com) 20084
 
() 掌握象棋划定礼貌
 
  与本文配套的树模顺序是“象棋小巫师0.2版,顺序清单是:
  (1) XQWL02.CPP——C++源顺序;
  (2) XQWLIGHT.RC——资源形貌文件;
  (3) RESOURCE.H——资源符号界说文件;
  (4) RES目录——图标、图画、声响等资源。
 
  在阅读本章前,推荐读者先阅读象棋百科全书网盘算机博弈专栏的以下几篇译文:
  (1) 国际象棋顺序设想(一):引言(François Dominic Laramée)
  (2) 国际象棋顺序设想(二):数据组织(François Dominic Laramée)
  (3) 国际象棋顺序设想(三):着法的发生(François Dominic Laramée)
  (4) 数据组织——简介(Bruce Moreland)
  (5) 数据组织——0x88着法发生要领(Bruce Moreland)
 
2.1 走法天生器
 
  走法天生器是象棋顺序中的一个主要组成局部,它能够处置责罚险些一切象棋划定礼貌的问题。
  假定咱们的棋盘运用9x10的数组,依照旧规的要领,找到一个马的一切走法,这将是一件异常痛苦的事:
 
// 剖断马的下面一格有无子
int yDst = ySrc + 2;
if (yDst <= Y_BOTTOM && ucpcSquares[xSrc][ySrc + 1] == 0) {
 int xDst = xSrc + 1;
 if (xDst <= X_RIGHT && !SELF_PIECE(ucpcSquares[xDst][yDst])) {
  ADD_MOVE(xSrc, ySrc, xDest, yDest);
 }
 xDst = xSrc - 1;
 if (xDst >= X_LEFT && !SELF_PIECE(ucpcSquares[xDst][yDst])) {
  ADD_MOVE(xSrc, ySrc, xDest, yDest);
 }
}
// 剖断马的下面一格有无子
……
 
  不只代码数目重大,运转速率缓慢,而且一不注意就随意纰漏写错。
  幸亏咱们的棋盘是一个巨细为16x16的二维数组,只不外写在顺序里的是 ucpcSquares[256] 而已。
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af
b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf
d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df
e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef
f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
  上表就是9x10的象棋棋盘在16x16的数组中的职位,咱们将在这个棋盘上总结马是怎样走棋的。
  首先,咱们预置一个常量数组 ccInBoard[256],透露表现哪些格子在棋盘外(紫色格子,填0),哪些格子在棋盘内(浅色格子,填1),以是就没有需要运用 x >= X_LEFT && x <= X_RIGHT && y >= Y_TOP && y <= Y_BOTTOM 之类的语句了,取而代之的是 ccInBoard[sq] != 0
  其次,一维数组的好就是上下前后相干异常简明——下面一格是 sq - 16,下面一格是 sq + 16,左面一格是 sq - 1,左面一格是 sq + 1。马能够跳的点只需8个,终点一定终点的偏移值是流动的:
 
const char ccKnightDelta[4][2] = {{-33, -31}, {-18, 14}, {-14, 18}, {31, 33}};
 
  而对应的马腿的偏移值是:
 
const char ccKingDelta[4] = {-16, -1, 1, 16};
 
  这个数组之以是命名为 ccKingDelta,是由于它也是帅()的偏移值。
  这样,找到一个马的一切走法就随意纰漏许多了。首先剖断某个偏向上的马腿是否是有子,然后剖断该偏向上的两个走法是否是能走:
 
for (i = 0; i < 4; i ++) {
 sqPin = sqSrc + ccKingDelta[i];
 if (IN_BOARD(sqPin) && ucpcSquares[sqPin] == 0) {
  for (j = 0; j < 2; j ++) {
   sqDst = sqSrc + ccKnightDelta[i][j];
   if (IN_BOARD(sqDst) && !SELF_PIECE(ucpcSquares[sqDst])) {
    ADD_MOVE(sqSrc, sqDst);
   }
  }
 }
}
 
  用相似的设施就能够够发生其他棋子的一切走法。
 
2.2 剖断走法是否是相符划定礼貌
 
  只管咱们以前运用了一些炫技,让走法天生器尽能够地小巧,但它依然是象棋顺序中最消耗时刻的运算模块。有时候走法天生器真是牛鼎烹鸡了,例如用户点击鼠标走一步棋的时刻,剖断这步棋是否是相符走法划定礼貌,就有几种分歧的斟酌:
  A. 用走法天生器发生悉数走法,看看这些走法中有无用户适才走出的那步棋,若是没有就说明用户在乱走;
  B. 前一种要领中,大局部事情都是空费的,由于用户只是走了一个棋子,走法天生器没需要天生其他棋子的走法;
  C. 用户只走了一步棋,而走法天生器会天生一个棋子的一切走法,是否是太虚耗了呢?
 
  剖断一个走法是否是恰当,有更简朴的要领。依然以马为例,假定用户的鼠标举措一定在棋盘内的,那末剖断历程以下:
  (1) 马是否是走了马步,即位移是否是相符 ccKinghtDelta 中的值;
  (2) 依据马步,找到对应的马腿职位,剖断马腿的格子上是否是有棋子。
 
  在象棋小巫师中,咱们用了一个 KNIGHT_PIN(sqSrc, sqDst) 的函数来取得马腿的职位(若是函数前往 sqSrc,则说明不是马步)。这样,剖断马的某个走法是否是相符划定礼貌,只要求很简朴的两句话:
 
sqPin = KNIGHT_PIN(sqSrc, sqDst);
return sqPin != sqSrc && ucpcSquares[sqPin] == 0;
 
2.3 剖断将军
 
  到现在为止,咱们剩下一件事没有做了,那就是剖断输赢。中国象棋的输赢规范就是帅()有无被将死或困毙,咱们的要领很简朴——天生一切走法,若是走恣意一步都邑被将军,那末该局势就是将死或困毙的局势,棋局到此完毕。
  那末怎样来剖断是否是被将军呢?咱们有两种要领:
  A. 让对方天生悉数走法,看看其中有无走法能够吃掉自身的帅()
  B. 依照剖断走法是否是相符划定礼貌的思绪,使用更简朴的要领。
 
  第一种要领没有甚么纰谬的,但计算机象棋顺序每秒种要求剖析上万个局势,对每一个局势都去天生悉数走法显著太花时刻了,以是咱们要实验第二种要领。事实上剖断帅()是否是被将军的历程其实不庞大:
  (1) 假定帅()是车,剖断它是否是能吃到对方的车和将()(中国象棋中有将帅不能对脸的划定礼貌)
  (2) 假定帅()是炮,剖断它是否是能吃到对方的炮;
  (3) 假定帅()是马,剖断它是否是能吃到对方的马,要求注重的是,帅()的马腿用的数组是 ccAdvisorDelta,而不是 ccKingDelta
  (4) 假定帅()是过河的兵(),剖断它是否是能吃到对方的卒()
  这样,一个庞大的走法天生历程(计划A)就被简化成几个简朴的走法剖断历程(计划B)
  • 上一篇 计算机象棋循序渐进():从图形界面做起
  • 下一篇 计算机象棋循序渐进():最后级的野生智能
  • 返 回 象棋百科全书——盘算机博弈
  • www.xqbase.com