JS 中國象棋程式(0):介面設計

未濟發表於2017-02-23

“JavaScript中國象棋程式” 這一系列教程將帶你從頭使用JavaScript編寫一箇中國象棋程式。希望通過這個系列,我們對博弈程式的演算法有一定的瞭解。同時,我們也將構建出一個不錯的中國象棋程式。

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

在進入正題之前,本節是一些閒聊。如果你急切想進入正題,請跳過本節。

我學習中國象棋程式的歷程

最初是買了本書《C/C++中國象棋程式入門與提高》。這是本好書,寫得細緻、透徹,我很愉快地讀完了前6章,毫無壓力。這6章講解了局面表示、走法生成、局面評估、基本搜尋演算法等內容。遺憾的是,第7章我讀不下去了。第7章是在講解,如何使用VC6.0設計圖形使用者介面。天啊,我是一個web程式設計師,我只想了解一下象棋程式的設計思想以及演算法,我實在不想去使用陳舊的VC6.0來學習windows GUI程式設計啊。
幸運的是,我在這本書最後一頁的參考文獻裡,發現了象棋百科全書網。在這家網站的github倉庫,發現了一個JavaScript版本的中國象棋軟體,而且效能還不錯。使用HTML + JavaScript來設計介面,自然是簡單了很多啊,這樣就能集中精力去學習象棋程式的演算法了。

為什麼選擇JavaScript

本教程之所以選擇JavaScript,講解JavaScript版本的中國象棋程式,我有以下幾個理由吧:
1、這個JavaScript版本的中國象棋程式,效能還不錯。
2、介面設計簡單,可以把主要精力用在對演算法的學習上。
3、不用搭建環境。只要有文字編輯器(比如notepad++)和瀏覽器(最好是chrome吧),就足夠了。
4、程式用到的都是很基礎的JavaScript語法,應該沒有語法方面的障礙。

專案初衷

曾經讀到過一個教程手把手教你構建 C 語言編譯器,我覺得很有意思。於是我就仿照這它的格式,寫下了這個中國象棋程式教程。本教程的絕大部分思想都來自《C/C++中國象棋程式入門與提高》和象棋百科全書網象棋百科全書網上面還有很多不錯的文章,讓我受益匪淺。
如果你想了解原版的程式,請前往象棋百科全書網的github下載,這裡面有很多個版本,我們使用的是JavaScript版。

這個教程難學嗎?

1、至少前4節是不難的吧,都是一些基本的東西。
2、第5節介紹了Alpha-Beta搜素,這個演算法很重要,是後面幾節教程的基礎。
3、如果搞明白了Alpha-Beta搜尋演算法,隨後的3節應該也不算難吧,都是在Alpha-Beta演算法的基礎上進行優化。

最後,非常感謝象棋百科全書網的前輩以及《C/C++中國象棋程式入門與提高》的作者。
祝你學得愉快。

這一節我們設計圖形介面,顯示初始化棋局。當點選某棋子時,彈窗提示所點選的具體棋子。效果如下:

934105-20170221151846929-1961232006

 

1.1、棋盤表示

中國象棋有10行9列,很自然地想到可以用10×9矩陣表示棋盤。事實上,我們使用16×16矩陣來表示一個擴充了的虛擬棋盤。

934105-20170221152542195-1722498827

如上圖所示,灰色部分為真實棋盤,置於虛擬棋盤之中。這麼做可以快速判斷棋子是否走出邊界。例如象沿田字走,如果走到真實棋盤之外的虛擬棋盤中,說明走法不合法。

容易想到使用二維陣列表示16×16矩陣,這樣棋盤上的一個位置需要兩個變數表示。一個走法包括起點和終點,就需要四個變數。如果使用長度為256的一維陣列表示,一個位置只需一個變數,這就可以減少計算量。因此用一維陣列表示16×16矩陣。

一維矩陣和二維矩陣之間的轉換也很簡單:

其中,sq & 15是通過位運算取餘,與sq % 16結果相同(可參考篇文章)。

再使用一個輔助陣列,標識虛擬棋盤中,哪些位置屬於真實棋盤:

要判斷某位置是否在真實棋盤,可使用函式:

1.2、棋子表示

使用整數表示棋子:

紅方

8

9

10

11

12

13

14

黑方

16

17

18

19

20

21

22

棋子這樣表示,可以快速判斷某棋子屬於紅方還是黑方,如下表所示:

紅方棋子

黑方棋子

十進位制

二進位制

十進位制

二進位制

8

0000 1000

16

0001 0000

9

0000 1001

17

0001 0001

10

0000 1010

18

0001 0010

11

0000 1011

19

0001 0011

12

0000 1100

20

0001 0100

13

0000 1101

21

0001 0101

14

0000 1110

22

0001 0110

可以看出:

紅方棋子 & 8 = 1

黑方棋子 & 16 = 1

1.3、字串表示局面

使用陣列表示局面,程式處理起來很方便,但是再網上傳遞棋局很不方便。我們可以用一行字串表示一個局面,這就是FEN格式串,一種使用ASCII碼字元描述國際象棋局面的標準,當然也可應用於中國象棋。中國象棋的初始局面可表示為:

rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w – – 0 1

(1)、紅色區域,表示棋盤佈局,小寫表示黑方,大寫表示紅方。一個字母表示一個棋子,對應關係如下。

紅方

字母

黑方

字母

對應單詞

K

k

king

A

a

advisor

B

b

bishop

N

n

knight

R

r

rook

C

c

cannon

P

p

pawn

至於為什麼馬不用H(horse),象不用E(elephant),這是為了與國際象棋相對應。如果沒有棋子,則用數字表示出相鄰連續的空位數。中國象棋共有十行,每行都用一個字串表示,行間使用正斜槓分割。例如:

rnbakabnr表示:934105-20170221153730351-345818983

9表示:第二行都是空格。

1c5c1表示:934105-20170221153824913-1710346427

(2)、綠色區域,表示輪到哪一方走子,“w”表示紅方,“b”表示黑方。(沒有用r表示紅方,我想也是為了與國際象棋對應吧,畢竟國際象棋是黑白兩色。)

(3)、深紫色區域,在中國象棋中沒有意義,始終用“-”表示。

(4)、紫紅色區域,在中國象棋中沒有意義,始終用“-”表示。

(5)、藍色區域,表示雙方沒有吃子的走棋步數(半回合數),通常該值達到120就要判和(六十回合自然限著),一旦形成局面的上一步是吃子,這裡就標記“0”。

(6)、棕色區域,表示當前的回合數。

我們的程式就是使用FEN串初始化棋局的,這就涉及到了將FEN串轉化為一維棋局陣列。暫時不考慮哪方走子,只解析紅色部分,虛擬碼如下:

1.4、棋盤前端設計思路

由於棋盤有90個交叉點,我們把棋盤劃分為的90個小正方形區域,交叉點是小正方形的中心。每個區域都會定義一個img標籤。

934105-20170221154619898-632109959

上圖使用紅色方框,標識出了4個小正方形區域。

這些img標籤有兩個作用:

(1)、顯示棋子圖片

如果某個區域存在棋子,就會顯示相應的棋子圖片;否則,顯示一張透明圖片(也就是oo.gif)。

(2)、響應點選事件

每個img標籤都會繫結onmousedown事件。點選不同的img標籤時,會傳遞不同的引數給響應函式,這樣就知道點選的具體是哪個區域了。

1.5、核心程式碼說明

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

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

程式中定義了兩個物件:Board和Position。Board表示一個棋盤,主要功能是初始化棋局,顯示棋盤、棋子,響應棋盤上的點選事件。Position儲存了一維棋局陣列,並定義了很多對該陣列進行操作的方法。

Board物件例項化的程式碼位於index.html中。

通過prototype屬性,我們為這兩個物件新增了很多的屬性和方法。

Board的主要屬性和方法:

(1)、pos

這是Position物件的一個例項。

(2)、flushBoard()

重新整理棋盤,也就是重新顯示棋盤上的棋子。

(3)、drawSquare(sq)

顯示sq位置的棋子圖片。如果該位置沒棋子,則顯示一張透明的圖片。

(4)、clickSquare(sq_)

點選棋盤的響應函式。點選棋盤(棋子或者空位置),就會呼叫該函式。sq_是點選的位置。

Position的主要屬性和方法:

(1)、squares

這就是一維棋局陣列。

(2)、fromFen(fen)

通過FEN串初始化棋局,也就是將引數fen表示的棋局,轉化為一維棋局陣列squares表示的棋局。

(3)、addPiece(sq, pc)

將棋子pc新增進棋局中的sp位置。

相關文章