聲明:本程式設計參考象棋巫師源碼(開發工具dephi 11,建議用delphi 10.3以上版本)。
本章目标:
實作中國象棋規則
上一章我們設計了圖形界面,可以開始輪流走棋了。但是,由于沒有按中國象棋的規則進行限制,所有的棋子都可以在棋盤上随意走動,這章我們開始制定行棋規則。
2.1 記錄局面
在制定規則之前,我們要先考慮把目前局面記錄下來,這樣棋子移動後才能知道移動後的局面。棋盤是10×9的格子組成,我們就用二維數組來記錄局面變化情況,同時用一個一維數組記錄每個棋子的位置:
在startUp函數裡,我們将chessbd 初始化,同時記錄初始棋局,代碼較前章稍作修改,已标記:
移動棋子之後,chessbd将發生變化,我們定義function TPieceMove.MovePiece(s,d:TPoint):Byte;這個函數記錄移動後的變化:
2.2、制定規則
現在可以制定規則,限制棋子移動的範圍。中國象棋走棋規則:車炮走直線,炮打隔山子,馬跳日、象飛田,士走斜線,兵有進無退。重要的設計思路:
根據src源點、dest目标點的縱橫坐标差的絕對值判斷棋子的移動軌迹是否合理。
兵(卒):有進無退,過河平移,每次一格。僞代碼 :
if 縱坐标絕對內插補點+橫坐标絕對內插補點<>1 then 傳回假;if 未過河 and 橫坐标絕對內插補點=1 then 傳回假。
馬:跳日字,且馬眼無子。僞代碼 :if 縱坐标絕對內插補點*橫坐标絕對內插補點<>2 then 傳回假; if 别腿 then 傳回假。如何判斷别腿?下圖講解:

從圖解中不難看出,馬橫跳時,馬眼的橫坐标是(源點橫坐标+目标點橫坐标)/2,縱坐标與源點相同;豎跳時,馬眼的縱坐标是(源點縱坐标+目标點縱坐标)/2,橫坐标與源點相同。僞代碼:
if 橫跳 and 馬眼無棋 then 傳回真;if 豎跳 and 馬眼無棋 then 傳回真。
象(相):走田字,象眼的位置簡單得多,坐标【(源點橫坐标+目标點橫坐标)/2,(源點縱坐标+目标點縱坐标)/2)】,與馬的判斷相同。僞代碼:
if 已過河 or 縱坐标絕對內插補點<>2 or 橫坐标絕對內插補點<>2 then 傳回假;if 象眼無棋 then 傳回真。
士(仕): 走斜線,且不能出宮。僞代碼:if 在宮裡 and 縱坐标絕對內插補點*橫坐标絕對內插補點=1 then 傳回真。
帥(将):不出宮,每次一格。僞代碼:if 在宮裡 and 縱坐标絕對內插補點+橫坐标絕對內插補點=1 then 傳回真。以上幾種棋判斷走法很簡單,就不發代碼了,文章最後有完整源碼。
車炮:走直線。雖說直線容易判斷,但是走棋判斷就稍複雜些。我們從源點搜尋到目标點,看中間有無棋子擋住,如此判斷。代碼如下:
2.3 是否将軍
中國象棋裡能将軍的棋子也就4種:兵(卒)、馬、炮、車,是以我們判斷是否将軍時,隻要判斷對方的這4種棋子是否将軍即可。這裡我們要為兵(卒)、馬、帥(将)定義步長,以便判斷。所謂步長,就是指兵、馬、帥走一步能到的位置,以原點坐标(0,0)為起點,确定以上棋子能走到位置,兵、帥的走法一緻,步長也一樣;馬八個方向都可以走,還得定義馬眼的位置;這裡把士、相的步長也一并定義了,後面有用 。車炮步長不定,是以不能定義。代碼如下:
定義步長之後,我們就可以根據步長來判斷帥(将)周邊是否有以上4種有攻擊力的棋子(注意:将帥面對面也是被認為是一種被将軍!):
以上代碼也不複雜,不再另外講解。中國象棋裡我們要考慮,如果走棋之後,走棋方處于将軍的狀态,就不能走這步棋,是以得撤回這步走棋:
2.4 是否赢棋
判斷是否赢棋,就是某一方被将軍後,無法解将,或是某一方子被剃光頭。以此來确定赢棋,設計思路:被将軍的一方生成所有的走法,逐一嘗試這些走法看是否能解将。紅黑雙方各有16個棋,除去已經被吃掉的棋,逐一生成走法即可,直接上代碼(看注釋):
剩下的工作就是逐一走這些走,判斷是否仍處于将軍狀态,再撤銷這些走法(為什麼用IsMate?純粹是與象棋巫師一緻,實在不明白為什麼這樣的函數名,我最初用的GameOver):
2.5 響應規則
在csBoard事件裡添加canMove、MovePiece、IsChecked,IsMate等規則函數即可,見源碼。
下一章将開始AI算法。
本章節源碼百度雲盤:
連結:中國象棋程式設計(二)制定規則
提取碼:1234