JS 中國象棋(1):校驗棋子走法

未濟發表於2017-02-23

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

程式的最終效果點選這裡檢視

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

2.1、走法表示

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

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

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

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

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

紅方棋子 & 8 = 1

黑方棋子 & 16 = 1

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

另外,我們設定七個全域性變數:

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

棋子數值 – pcSelfSide = PIECE_KING

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

2.3、校驗將(帥)的走法

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

934105-20170221162105991-892999564

 

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

pcDst = sqSrc – 16

pcDst = sqSrc – 1

pcDst = sqSrc + 1

pcDst = sqSrc + 16

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

(1)、終點sqSrc位於九宮

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

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

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

設定一個陣列16×32的一維陣列

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

2.4、校驗士的走法

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

934105-20170221162649101-1588153368

士的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。檢驗函式如下

2.5、校驗象的走法

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

934105-20170221162940101-1978167782

象的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。判斷函式如下:

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

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

(3)、象眼無棋子

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

2.6、校驗馬的走法

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

934105-20170221163126851-1402206891

馬的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)、對應馬腳的位置沒有棋子

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

該陣列在(-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、校驗車、炮的走法

車的走法如下圖所示:

934105-20170221163313491-838020647

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

(1)、走出棋盤

(2)、碰到本方棋子

(3)、吃掉對方棋子

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

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

具體校驗演算法可參看程式碼。

2.8、校驗卒(兵)的走法

紅兵的走法如下圖所示:

934105-20170221163522007-157717739

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

(1)、判斷是否過河

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

判斷函式如下:

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

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

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

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

因此,只要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。

相關文章