JavaScript中國象棋程序(2) - 校驗棋子走法


“JavaScript中國象棋程序” 這一系列教程將帶你從頭使用JavaScript編寫一個中國象棋程序。這是教程的第2節。

程序的最終效果點擊這里查看

這一系列共有9個部分:

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

 

這一節介紹棋子的走法。改進之后的程序,可以連續走動紅方棋子,但要符合象棋規則。黑方(電腦方)暫時還不能回應。

2.1、走法表示

一個走法包括起點和終點,分別用sqSrc和pcDst表示一維棋局數組中的起點和終點。很容易想到,使用數組[sqSrc, pcDst]表示一個走法。但程序將來會處理大量的走法,使用數組表示走法太浪費內存。由於sqSrc和pcDst都是不超過255的整數(因為一維棋盤數組的大小是256),可以將sqSrc和pcDst壓縮到一個整數中,算法如下:

function MOVE(sqSrc, sqDst) {
  return sqSrc + (sqDst << 8);
}

如果想從壓縮后的整數中,重新獲取起點和終點,可分別使用以下函數:

// 獲取走法的起點
function SRC(mv) {
  return mv & 255;
}

// 獲取走法的終點
function DST(mv) {
  return mv >> 8;
}

2.2、判斷目標位置是否有本方棋子

如果目標位置有本方棋子,那么該走法肯定是不合法的。

設置一個變量pcSelfSide。走棋方為紅方時pcSelfSide=8,走棋方為黑方時pcSelfSide=16。在上一節提到過:

紅方棋子 & 8 = 1

黑方棋子 & 16 = 1

(終點棋子 & pcSelfSide) != 0 就說明終止位置有本方棋子,走法不合法。

另外,我們設置七個全局變量:

var PIECE_KING = 0; // 將
var PIECE_ADVISOR = 1; // 士
var PIECE_BISHOP = 2; // 象
var PIECE_KNIGHT = 3; // 馬
var PIECE_ROOK = 4; // 車
var PIECE_CANNON = 5; // 炮
var PIECE_PAWN = 6; // 卒

來對棋子編號。例如,只要滿足:

棋子數值 - pcSelfSide = PIECE_KING

那么這個棋子就是將(帥)。使用同樣的方法,可以判斷出其他六種棋子。

2.3、校驗將(帥)的走法

將的走法有四個方向,如下圖所示:

 

 

在一維數組中,1、2、3、4幾個方向的起點和終點分別滿足以下等式:

pcDst = sqSrc - 16

pcDst = sqSrc - 1

pcDst = sqSrc + 1

pcDst = sqSrc + 16

將(帥)的走法需要滿足下面這兩個條件:

1)、終點sqSrc位於九宮

可使用一個輔助數組,標識出所有合法的位置。

var IN_FORT_ = [
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]; 

判斷是否在九宮,使用函數

function IN_FORT(sq) {
  return IN_FORT_[sq] != 0;
}

2)、pcDst - sqSrc等於-16、-1、1、16其中的一個。

設置一個數組16×32的一維數組

var LEGAL_SPAN = [
                       0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0,
];

在該數組中,(-16 + 256)、(-1 + 256)、(1 + 256)、(16 + 256)四個位置是1,其他位置都不是1。校驗函數如下

function KING_SPAN(sqSrc, sqDst) {
  return LEGAL_SPAN[sqDst - sqSrc + 256] = = 1;
}

2.4、校驗士的走法

士的走法也是四個方向,如下所示

 

士的4個方向分別滿足以下等式:

pcDst = sqSrc - 17

pcDst = sqSrc - 15

pcDst = sqSrc + 15

pcDst = sqSrc + 17

士的走法需要滿足下面這兩個條件:

1)、終點sqSrc位於九宮

這與將(帥)的判斷方法相同

2)、pcDst - sqSrc等於-17、-15、15、17其中的一個

在上面提到的輔助數組LEGAL_SPAN中,(-17 + 256)、(-15 + 256)、(15 + 256)、(17 + 256)四個位置是2,其他位置都不是2。檢驗函數如下

function ADVISOR_SPAN(sqSrc, sqDst) {
  return LEGAL_SPAN[sqDst - sqSrc + 256] == 2;
}

2.5、校驗象的走法

象的四個走法如下圖所示,黑三角是相應的象眼位置

象的4個方向分別滿足以下等式:

pcDst = sqSrc - 34

pcDst = sqSrc - 30

pcDst = sqSrc + 30

pcDst = sqSrc + 34

象的走法需要滿足下面三個條件:

1)、象不能過河

在程序中,棋局被表示為大小為256的一維數組,一半棋盤位於0到127,另一半位於128到255。

128的二進制是1000 0000,右起第八位是1。128到255這些數的二進制,右起第8位都是1;0到127這些數的二進制,右起第8位都是0。因此,如果象沒過河,也就是pcDst和sqSrc位於相同的一半棋盤,那么

(sqSrc ^ sqDst)二進制的右起第八位是0,((sqSrc ^ sqDst) & 256)等於0。判斷函數如下:

// 如果從起點sqSrc到終點sqDst沒有過河,則返回true;否則返回false
function SAME_HALF(sqSrc, sqDst) {
  return ((sqSrc ^ sqDst) & 0x80) == 0;
}

2)、pcDst - sqSrc等於-34、-30、34、30其中的一個

在上面提到的輔助數組LEGAL_SPAN中,(-34 + 256)、(-30 + 256)、(30 + 256)、(34 + 256)四個位置是3,其他位置都不是3。檢驗函數如下

function BISHOP_SPAN(sqSrc, sqDst) {
  return LEGAL_SPAN[sqDst - sqSrc + 256] == 3;
}

3)、象眼無棋子

象眼位於sqSrc和sqDst的中點,判斷(sqSrc + sqDst)/2 的位置是否有棋子即可。

2.6、校驗馬的走法

馬的8個走法如下圖所示,黑三角是相應的馬腳位置

 

馬的8個方向滿足以下等式:

pcDst = sqSrc - 33

pcDst = sqSrc - 31

pcDst = sqSrc - 18

pcDst = sqSrc + 14

pcDst = sqSrc - 14

pcDst = sqSrc + 18

pcDst = sqSrc + 31

pcDst = sqSrc + 33

對應的馬腳分別位於:

sqSrc - 16

sqSrc - 16

sqSrc - 1

sqSrc - 1

sqSrc + 1

sqSrc + 1

sqSrc + 16

sqSrc + 16

馬的走法需要滿足下面兩個條件:

1)、pcDst - sqSrc等於-33、-31、-18、14、-14、18、31、33其中的一個

2)、對應馬腳的位置沒有棋子

我們使用一個新的輔助數組,來判斷馬的走法是否合法。

var KNIGHT_PIN_ = [
                              0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,-16,  0,-16,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0, -1,  0,  0,  0,  1,  0,  0,  0,  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,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0, 16,  0, 16,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,
];

該數組在(-33 + 256)、(-31 + 256)、(-18 + 256)、(14 + 256)、(-14 + 256)、(18 + 256)、(31 + 256)、(33 + 256)這8個位置分別存放了-1、-1、-16、-16、1、1、16、16,其他位置都是0。

設置變量sqPin滿足:

sqPin = sqSrc + KNIGHT_PIN_[sqDst - sqSrc + 256]

如果馬的走法滿足條件(1),那么spPin就是馬腳的位置;否則sqPin = sqSrc。

因此馬的走法合法,只需要滿足sqPin != sqSrc並且sqPin位置無棋子。

2.7、校驗車、炮的走法

車的走法如下圖所示:

 

沿着四條直線走,每個方向可一直向前走,直到:

1)、走出棋盤

2)、碰到本方棋子

3)、吃掉對方棋子

例如方向1,每走一步都是在起點基礎上-1;方向2是在起點基礎上-16;方向3是在起點基礎上+1;方向4是在起點基礎上+16。

炮與車的行棋規則類似,也可以沿一個方向一直向前走,不過遇到棋子時,要越過去(也就是翻山)。翻山后,炮只能吃對方棋子,不能落到空位置。

具體校驗算法可參看代碼。

2.8、校驗卒(兵)的走法

紅兵的走法如下圖所示:

 

過河前,只能向前走。過河后,可以左右走。

1)、判斷是否過河

以紅方為例,紅方是向上走。如果紅方過河,則會走到棋盤0到127的位置,此時所處位置的二進制表示,右起第8位是0。(因為128的二進制是1000 0000,這與之前講過的SAME_HALF函數類似)

判斷函數如下:

// sp是棋子位置,sd是走棋方(紅方0,黑方1)。如果該位置已過河,則返回true;否則返回false。
function AWAY_HALF(sq, sd) {
  return (sq & 0x80) == (sd << 7);

}

如果兵已經過河,是可以左右走的,滿足下面條件的走法一定合法:

AWAY_HALF(sq, sd) && (sqDst == sqSrc - 1 || sqDst == sqSrc + 1)

2)、判斷兵(卒)是否是向前走了一步

紅兵向前走一步是sqSrc - 16,黑卒向前走一步是sqSrc + 16。可用如下函數得到兵(卒)向前一步的位置:

// sp是棋子位置,sd是走棋方(紅方0,黑方1)。返回兵(卒)向前走一步的位置。
function SQUARE_FORWARD(sq, sd) {
  return sq - 16 + (sd << 5);
}

因此,只要sqDst = = SQUARE_FORWARD(sq, sd),說明兵(卒)是向前走了一步,走法合法。

2.9、核心代碼說明

本節的代碼可以在 Github 下載,也可以直接clone

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

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

1)、sqSelected

當前被選中棋子的位置。

2)、clickSquare(sq_)

用戶點擊棋盤時,我們分兩種情況進行討論:

1、當前棋盤上沒有棋子被選中

也就是說sqSelected為0。如果點擊的是己方棋子,那么直接選中該子

sqSelected = 點擊的位置

2、當前棋盤上有棋子被選中

也就是說sqSelected不為0。如果這次點擊的仍然是己方棋子,這說明用戶重新選擇了要走的棋子,

sqSelected = 新的點擊的位置

如果這次點擊的是對方棋子,或者是一個空位置,這說明用戶是在走棋。起點是原來選中的位置,終點是當前選中的位置。

3)、addMove(mv)

判斷一步棋是否合法,如果合法,就執行這步棋。

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

1)、legalMove(mv)

判斷步驟是否合法。合法返回true,非法返回false。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM