• 沒有找到結果。

電腦暗棋之設計及實作

N/A
N/A
Protected

Academic year: 2021

Share "電腦暗棋之設計及實作"

Copied!
58
0
0

加載中.... (立即查看全文)

全文

(1)國立臺灣師範大學 資訊工程研究所碩士論文. 指導教授: 林順喜 博士. 電腦暗棋之設計及實作 The Design and Implementation of Computer Dark Chess. 研究生:. 謝曜安 撰. 中華民國九十七年六月.

(2) 摘 要. 電腦棋類一直是人工智慧發展的重要領域之一,而電腦暗棋至今尚未有人對 其做較深入的研究。暗棋是屬於不完全資訊含機率性的棋類遊戲,不像西洋棋、 象棋是屬於完全資訊的棋類遊戲,所以如果用一般遊戲樹進行搜尋,在走棋與翻 棋夾雜的情況下,會因分枝度過大而無法做深入的搜尋,因此難以做出較佳的決 策。 本論文首度對電腦暗棋做深入的研究,並提出一些演算法來解決電腦暗棋所 面臨的相關問題。經實戰測試,我們所寫出來的暗棋程式可以擊敗市面上我們蒐 集到的所有暗棋程式,棋力已接近人類玩家的一般水平。. 關鍵字:電腦暗棋、不完全資訊、人工智慧. i.

(3) ABSTRACT Computer chess is always an important research area in artificial intelligence. At present, there is no paper reported in detail about in the playing strategies of Dark chess. Dark chess is an incomplete information game with probabilities, which is not the same as complete information games, such as chess or Chinese chess. If we use conventional game-tree searching techniques to deal with Dark chess, then the number of branches will be very large because there are lots of moves for both “dark pieces” and “bright pieces”. Hence, it is not easy to improve the strength of the Dark chess by using the conventional game-tree searching techniques. In this paper, we comprehensively study the characteristics of Dark chess and present some novel approaches to solve the Dark chess problem. We developed a Dark Chess program which can beat all other Dark chess programs in the market and have the same scores as the average human players.. Keywords: artificial intelligence, Dark chess, incomplete information game. ii.

(4) 目 錄 摘 要................................................................................................................................ i ABSTRACT...................................................................................................................ii 目 錄..............................................................................................................................iii 附圖表目錄.................................................................................................................... iv 第一章 緒論................................................................................................................... 1 第一節 暗棋規則及玩法介紹............................................................................. 1 第二節 暗棋與其它棋類的異同及其困難的地方............................................. 4 第二章 資料結構........................................................................................................... 7 第一節 棋盤-棋子映射結構............................................................................... 7 第二節 著法預處理........................................................................................... 10 第三章 搜尋演算法..................................................................................................... 13 第一節 簡介....................................................................................................... 13 第二節 審局函數............................................................................................... 14 第三節 Min-Max 搜尋演算法 .......................................................................... 16 第四節 Nega-Max 搜尋演算法 ........................................................................ 19 第五節 Alpha-Beta 搜尋演算法 ....................................................................... 20 第六節 寧靜搜尋............................................................................................... 24 第七節 Transposition Table ............................................................................... 28 第八節 循環剪裁............................................................................................... 36 第九節 允許空步............................................................................................... 37 第十節 最佳路徑的獲取................................................................................... 38 第十一節 走棋與翻棋的抉擇........................................................................... 42 第四章 結論與未來研究方向 .................................................................................... 47 第一節 結論....................................................................................................... 47 第二節 未來研究方向....................................................................................... 50 參考著作....................................................................................................................... 52. iii.

(5) 附圖表目錄 圖 1-1 暗棋的棋盤 ........................................................................................................ 1 圖 1-2 長捉示意圖 ........................................................................................................ 3 圖 1-3 長捉示意圖 ........................................................................................................ 3 圖 1-4 暗棋王的程式畫面 ............................................................................................ 4 圖 1-5 暗棋大車拼的程式畫面 .................................................................................... 5 圖 2-1 一維陣列表示法 ................................................................................................ 7 圖 2-2 棋子編號圖 ........................................................................................................ 8 圖 2-3 著法產生圖 ........................................................................................................ 9 圖 2-4 MoveTab 示意圖 ............................................................................................... 10 圖 2-5 OrderTab 示意圖 ............................................................................................... 11 圖 3-1 對局樹 .............................................................................................................. 13 圖 3-2 審局示意圖 ...................................................................................................... 16 圖 3-3 Min-Max 搜尋示意圖....................................................................................... 17 圖 3-4 Min-Max 搜尋演算法虛擬碼........................................................................... 18 圖 3-5 Nega-Max 搜尋演算法虛擬碼 ......................................................................... 19 圖 3-6 Alpha-Beta 示意圖 ............................................................................................ 21 圖 3-7 Alpha-Beta 示意圖 ............................................................................................ 22 圖 3-8 Alpha-Beta 搜尋演算法虛擬碼 ........................................................................ 23 圖 3-9 水平效應示意圖 .............................................................................................. 24 圖 3-10 水平效應示意圖 ............................................................................................ 25 圖 3-11 Quiescent Search 虛擬碼 ................................................................................ 26 圖 3-12 寧靜搜尋示意圖 ............................................................................................ 27 圖 3-13 寧靜搜尋示意圖 ............................................................................................ 27 圖 3-14 相同盤面示意圖 ............................................................................................ 28 圖 3-15 相同盤面示意圖 ............................................................................................ 29 圖 3-16 盤面相同輪走方不同示意圖 ........................................................................ 30 圖 3-17 盤面相同輪走方不同示意圖 ........................................................................ 31 圖 3-18 節點存入 Hash Table 的函式虛擬碼 ............................................................ 33 圖 3-19 節點探測 Hash Table 的函式虛擬碼 ............................................................ 34 圖 3-20 Alpha-Beta 結合 Hash Table 的虛擬碼 .......................................................... 35 圖 3-21 重覆盤面示意圖 ............................................................................................ 36 圖 3-22 空步示意圖 .................................................................................................... 37 圖 3-23 空步示意圖 .................................................................................................... 38 圖 3-24 路徑獲取示意圖 ............................................................................................ 39 圖 3-25 路徑獲取示意圖 ............................................................................................ 39 iv.

(6) 圖 3-26 路徑獲取示意圖 ............................................................................................ 40 圖 3-27 路徑獲取示意圖 ............................................................................................ 41 圖 3-28 走棋與翻棋抉擇示意圖 ................................................................................ 43 圖 3-29 走棋與翻棋抉擇示意圖 ................................................................................ 46 表 4-1 實戰測試結果 .................................................................................................. 47 圖 4-1 與暗棋王實戰圖 .............................................................................................. 48 圖 4-2 與暗棋王實戰圖 .............................................................................................. 49 圖 4-3 與暗棋王實戰圖 .............................................................................................. 49 圖 4-4 與暗棋王實戰圖 .............................................................................................. 50. v.

(7) 第一章 緒論 第一節 暗棋規則及玩法介紹 暗棋,只使用中國象棋棋盤的一半,發明者為"棋壇總司令"謝俠遜。暗棋利 用了中國象棋的棋子一面有文字,另一面空白這個特色翻轉棋子,產生了帶運氣 成份的象棋變體[1],讓對奕時增加其趣味性。 暗棋的棋子不是放在交叉線上,而是放在格子上,共 4*8=32 格,剛好放下 所有棋子。開始時,將所有棋的背面向上,令人看不到棋子是什麼,然後洗亂棋 子,再放到棋盤上。如圖 1-1:. 圖 1-1 暗棋的棋盤. 未翻開的棋子稱為暗棋,翻開了的就叫明棋。雙方輪流行走,每次可選擇翻開一 枚暗棋、移動自己的一枚明棋或吃掉對手的一枚明棋。吃棋時的大小順序如下: 1.

(8) 紅方的帥可以吃掉黑方的:將、士、象、車、馬、包 紅方的仕可以吃掉黑方的:士、象、車、馬、包、卒 紅方的相可以吃掉黑方的:象、車、馬、包、卒 紅方的俥可以吃掉黑方的:車、馬、包、卒 紅方的傌可以吃掉黑方的:馬、包、卒 紅方的炮隔一子可以吃掉所有黑方的棋子 紅方的兵可以吃掉黑方的:將、卒 黑方的將可以吃掉紅方的:帥、仕、相、俥、傌、炮 黑方的士可以吃掉紅方的:仕、相、俥、傌、炮、兵 黑方的象可以吃掉紅方的:相、俥、傌、炮、兵 黑方的車可以吃掉紅方的:俥、傌、炮、兵 黑方的馬可以吃掉紅方的:傌、炮、兵 黑方的包隔一子可以吃掉所有紅方的棋子 黑方的卒可以吃掉紅方的:帥、兵 而遊戲目的則是,誰最後將對方的棋子全部吃光誰就勝利。如果最後雙方誰 也無法把對方的棋子全部吃掉,為避免無限走棋的狀態,當雙方走步合計達二十 五步或五十步均無吃棋或翻棋時,則視為和棋。還有在遊戲進行的過程中也禁止 長捉,這是為了避免當有一方處於劣勢時一直做掙扎,而造成雙方僵持不下的狀 態,如圖 1-2:. 2.

(9) 圖 1-2 長捉示意圖. 此時輪黑方,黑方翻了 D1 翻到包,如圖 1-3:. 圖 1-3 長捉示意圖. 此時如果紅方走 C2-C1 捉包,黑方走 D1-D2 逃包,紅方又走 C1-C2 捉包,黑方 走 D2-D1 逃包,紅方再 C2-C1 捉包…, 紅方為了不讓帥或仕被包吃掉,一直長 捉黑包,造成雙方僵持不下的局面。所以,長捉在遊戲進行的過程中是不被允許. 3.

(10) 的。故此例紅方不可一直長捉黑包,必須改走其它棋步(如翻棋),否則紅方視 為輸棋。. 第二節 暗棋與其它棋類的異同及其困難的地方 暗棋為不完全資訊含機率性的棋類遊戲,不像西洋棋、象棋是屬於完全資訊 的棋類遊戲,完全資訊棋類遊戲已經有很成熟的演算法,可以使電腦有很高的棋 力,但目前還尚未看到任何有關電腦暗棋的研究,且市面上我們所找到的暗棋程 式普遍棋力都不高,與一般玩家仍有一段差距,其中以極真科技在 2005 年出版 的一款名叫“暗棋王"的程式棋力還算可以,也是這次我們實驗的對戰對象之一 。如圖 1-4 是暗棋王的程式畫面:. 圖 1-4 暗棋王的程式畫面 4.

(11) 除了暗棋王以外,我們還蒐集到一款由新造科技所出版的“暗棋大車拼", 其棋力遠不如暗棋王。如圖 1-5 是暗棋大車拼的程式畫面:. 圖 1-5 暗棋大車拼的程式畫面. 暗棋程式的棋力之所以無法像西洋棋、象棋那樣有接近人類的水平,是因為 暗棋有明棋、暗棋,明棋可以進行走步,暗棋可以選擇翻開但事先不知道翻開的 棋子將會是什麼,所以在明棋、暗棋相互夾雜的情況下,如果用一般的遊戲樹進 行搜尋時,其搜尋的分枝度將會很大,遠遠高過西洋棋、象棋;如果用 rule base 的方式來解暗棋的話,也常常會出現誤判情勢的狀況,而導致最後的決策荒腔走. 5.

(12) 板。所以本研究的目的即是透過實作一電腦暗棋程式,對電腦暗棋的設計提出適 當的演算法,避免因為分枝度過大而無法做深入的搜尋,也避免因為 rule base 的 誤判而做出錯誤的決策。希望本研究能為電腦暗棋的設計揭開序幕,期待能增進 電腦暗棋研究風氣,早日能與高段玩家相庭抗衡。. 6.

(13) 第二章 資料結構 第一節. 棋盤-棋子映射結構. 我們的暗棋棋盤結構是使用一維陣列 Board[32] 來記錄棋盤上每一格的棋子 類型如圖 2-1:. 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 圖 2-1 一維陣列表示法. 之所以沒有採用較為直觀的二維陣列原因是:一維陣列表示法擁有比較快的存取 速度,因為高階語言中的二維陣列,經編譯程式處理之後,仍然是被轉換成一維 的記憶體,中間多了一道索引值轉換處理的動作,因此一維陣列表示法會比二維 陣列表示法要來得快[10]。 有了棋盤結構後,還需要一個棋子結構來記錄每個棋子位在棋盤上的哪個位 置,這邊我們使用的是一維陣列 Piece[34]來識別 32 個棋子還有空格、暗棋的部 份,如圖 2-2:. 7.

(14) 空格. 將. 0. 1. 黑馬 9. 黑士 2. 4. 5. 11. 12. 13. 紅相 19. 黑車 6. 20. 紅炮. 7. 14. 15. 紅俥 21. 22. 16 紅傌. 23. 29. 30. 8 紅帥. 24. 紅兵 28. 黑馬. 黑卒. 紅仕. 27. 3. 黑包 10. 18. 黑象. 17 紅炮. 25. 26. 暗棋 31. 32. 33. 圖 2-2 棋子編號圖. 棋盤、棋子結構均可對一個局面進行完整的描述。單用棋盤結構能方便地檢 閱棋盤上任一位置的資訊,但要產生合法走步時,卻要對整個棋盤進行一次檢查 ,共 32 次的比對才能找到每個棋子所在位置,效率較低;單用棋子結構能方便 知道某棋子在棋盤上的位置,但要檢閱棋盤狀況,例:是否能吃子時,最壞情況 下卻要對 16 個棋子掃描一次。為了充分利用兩者各自的優點,所以把這兩種方 法結合起來形成所謂的棋盤-棋子映射結構,當棋子移動時對棋盤、棋子結構進行 些微的調整,以達到快速檢索、檢閱的目的。例如圖 2-3:. 8.

(15) 圖 2-3 著法產生圖. 此例 Board[32]陣列內容如下:. 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10 11 12 13 14 15. 0. 28. 0. 24. 0. 17. 0. 29. 22. 12. 18. 0. 26. 0. 19. 13. 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 14. 20. 0. 27. 0. 21. 15. 30. 31. 0. 23. 0. 32. 0. 25. 0. 當紅方要產生所有合法走步時,只需要查詢 Piece[17] ~ Piece[32],其陣列內容如 下:. 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 5. 10. 14. 17. 21. 8. 26. 3. 30. 12. 19. 1. 7. 23. 24. 28. 針對還在棋盤上的紅方棋子產生合法走步即可,如果 Piece[K]為-1 代表棋子 K 沒 有在棋盤上或是尚未被翻開,這樣的做法比掃描整個棋盤共 32 個位置整整省了 16 次比對。又以圖 2-3 為例,紅方 C2-B2 仕吃卒,它要怎麼知道位在 B2(位置 9) 9.

(16) 的是卒呢?因為我們有用 Board [9]記錄該位置的棋子編號是 12 也就是卒,所以 一比對發現紅仕可以吃黑卒,所以 C2-B2 就是一個合法步。. 第二節 著法預處理 我們使用了 MoveTab[P][5] 二維陣列預先將位置 P 經由上、下、左、右移動 一格後所能走到的位置儲存下來,其中 MoveTab[P][0] 代表位置 P 能走到的方向 數量,而 MoveTab[P][1] ~ MoveTab[P][4] 則是記錄位置 P 走各方向後所到達的目 的位置,例如圖 2-4:. 圖 2-4 MoveTab 示意圖. 紅方 F1(位置 5)的相要怎麼知道有哪些方向可以走,它只要查 MoveTab[5][0]=3 發現有 3 個方向可以走,而依序存取 MoveTab[5][1] 的 4(左移一格後的目標位 置) 、MoveTab[5][2]的 6(右移一格後的目標位置) 、MoveTab[5][3]的 13(下移一 格後的目標位置) ,就可以得到 F1 所能走到的各方向的目標位置。這樣的做法會. 10.

(17) 比每次都要上、下、左、右四次位移量加上還要判斷是否超出邊界要來的有效率。 除了預先處理著法可走到的方向外,我們還使用了 OrderTab[P1][P2] 來比較 兩顆棋子的大小,其中 P1 代表來源棋子的編號,而 P2 代表目的棋子的編號。 如果 OrderTab[P1[P2]的值是-1 的話,表示 P1 不能吃 P2;如果 OrderTab[P1][P2] 的值是 0 的話表示 P1 可以吃 P2。例如圖 2-5:. 圖 2-5 OrderTab 示意圖. E2(位置 12)的黑車檢查 MoveTab[12][0]=4 會發現有 4 個方向可以走,分別是 E1(位置為 MoveTab[12][1]=4)、D2(位置為 MoveTab[12][2]=11)、F2(位置為 MoveTab[12][3]=13) 、E3(位置為 MoveTab[12][4]=20) 。其中黑車的棋子編號 6, 而 E1 的棋子編號是 20,檢查 OrderTab[6][20] = -1,表示黑車不能吃紅相,所以 E2-E1 不是一個合法步;而 D2 的棋子編號是 33,檢查 OrderTab[6][33] = -1,表 示黑車不能吃暗棋,所以 E2-D2 不是一個合法步;而 F2 的棋子編號是 24,檢查. 11.

(18) OrderTab[6][24] = 0,表示黑車能吃傌,所以 E2-F2 是一個合法步;而 E3 的棋子 編號是 0,檢查 OrderTab[6][0] = 0,表示黑車可以走到空格,所以 E2-E3 是一個 合法步。這樣的做法會比使用一堆 if 指令來判斷是否可吃子、目的格是否為空格、 目的格是否為暗棋要來得有效率。. 12.

(19) 第三章 搜尋演算法. 第一節 簡介. 在撰寫暗棋程式之前,首先想想我們是如何下棋?我們在走子之前,通常要 暗自在心中自我對話一番:如果我走了這一步棋,敵方會怎麼回應它?這步棋由 我方的觀點來看是不錯,可是對方卻有不錯的對策,所以我方必須考慮另一步… 。我方走這步,對方走那步,我方再走一步,對方再走另一步,結果到底對哪一 方有利?同樣地,暗棋程式也必須具備推算對方可能應對走法的能力,並且在所 有可能的走法組合中加以審慎評估,然後辨認出最有利的一步棋[4]。 我們通常利用樹狀結構(又稱:對局樹或是遊戲樹)將對奕的過程給表現出 來,如圖 3-1 是搜尋 2 層的對局樹:. 目前棋局狀態 我方可能走法 我方可能形成棋局 敵方可能走法 敵方可能形成棋局. 圖 3-1 對局樹. 從圖 3-1 不難發現,對局樹的節點數隨著層數的增加呈指數倍的增加,如果一步. 13.

(20) 棋有 10 種走法,而雙方各要走 10 步才能分出勝負的話,那麼一共有 1020 種狀態 需要搜尋[6],這是一個非常大的量,根本無法在合理的時間範圍內搜尋出來。所 以通常只能做有限層數的搜尋,或者想辦法不要搜尋所有可能的走法而仍能保有 充足的資訊。 當對局樹搜尋到底層的葉節點時,程式必須呼叫審局函數,來替敵我雙方的 走法組合打分數,然後由底層的葉節點將分數往上依序傳回給父節點,一直到最 後整棵對局樹都搜尋完了,根節點將會保留最有利我方的分數值,進而找出最有 利我方的一步棋。. 第二節 審局函數. 前一節有提到,當對局樹搜尋到葉節點時必須呼叫審局函數,因此審局函數 代表的就是對盤面的一個靜態評估,而搜尋是對盤面的動態評估。一般而言,審 局函數的制定因人而異,但不失兩個原則[5]: (一)精確:審局函數必須正確無誤地反應出局面的優劣。唯有精確的審局函數 才能使搜尋程式在決定著手時,不致於發生失誤。 (二)快速:求出審局函數的時間必須要短。如果求出審局函數所花費的時間太 長的話,勢必會拖慢整個搜尋運算的時間,使所建立的對局搜尋樹深度較 淺,因而減低棋力。. 14.

(21) 審局函數設計的分類通常有兩種:靜態審局函數和動態審局函數。靜態審局 函數泛指審局函數在棋局對弈的過程中,始終以固定的評估元素數值做為審局函 數的運算,來評估局面,撰寫起來比較容易,且速度較快。另一方面,動態審局 函數泛指在棋局對弈的過程中,評估元素的數值會隨著局勢的變化而做改變,撰 寫起來較為複雜,且速度較慢。 而我們在這次的暗棋程式實作裡,使用的是靜態的子力價值評分,如下: 將、帥:810 分 士、仕:270 分 象、相:90 分 車、俥:18 分 包、炮:18 分 馬、傌:6 分 卒、兵:1 分 從上可看出子力較強的分數比較大,子力較弱的分數比較小,包的價值約等同一 隻車的價值,至於實際的分數值是由我們主觀定義出來的。 之所以使用靜態子力價值評分的原因之一是撰寫起來比較容易,而且計算的速 度快,因為它可以在搜尋的過程中逐步維護(Incremental Maintain) ,且在大部份 的情況下它可以準確地反應出雙方經過一連串走步後,到底誰好誰壞的這個量值 。例如圖 3-2:. 15.

(22) 圖 3-2 審局示意圖. 上圖輪紅方先下,經由 10 層的對局樹搜尋得到最佳路徑為: F2-F1 吃卒,C4-B4 吃炮,D2-D3,B4-C4,D3-D4,C4-B4,D4-C4,D1-D2,C4-B4 吃車,D2-D1。 紅方在第一步吃了卒得到 1 分,在第九步吃了車得到 18 分,但在第二步被黑方 車吃炮損失了 18 分,所以最後紅方的分數 = 1 + 18 – 18 = 1。代表紅方走了第一 步 F2-F1 一直到第十步黑方走了 D2-D1 這一連串的走步後,所反應出來的是紅方 可以得到 1 分的價值。. 第三節. Min-Max 搜尋演算法. Min-Max 搜尋法是假設敵我雙方都選擇最佳走法時,一種以深度優先搜尋的 演算法。它從根節點出發,呼叫著法產生器產生所有可能的走法,以深度優先的 方式向下進行節點的拜訪,當拜訪到給定深度後,在葉節點處使用審局函數判斷. 16.

(23) 盤面好壞。 Min-Max 搜尋演算法在輪到我方走步時,選擇能得到最大分數的走法。反之, 在對手走步時,則是選取得到最小分數的走法。因此 Min-Max 搜尋演算法在對局 樹不同層中,交互使用取 Max 值和 Min 值的走法,這也就是為什麼叫做 Min-Max 搜尋的原因。例如圖 3-3:. max. 2. 輪紅方走棋 2. min. min. 2. -3. -3. 輪黑方走棋 5. 6. 圖 3-3 Min-Max 搜尋示意圖. 假設紅黑雙方輪流各下一手棋之後共有 4 種可能盤面,而每個盤面根據審局函數 會回傳不同的分數,最後根據每個節點輪紅方或黑方走步,分別選擇能夠得到最 大或最小分數的走法,如此以深度優先的方式進行著,最後便能得到在根節點處 對紅方而言最好的走法[10]。 圖 3-4 是 Min-Max 搜尋演算法的虛擬碼:. 17.

(24) int MinMax(int depth){ int best,val; if(depth==0){ return Evaluate();. //葉節點傳回審局函數的分數 //總是以 root 方也就是我方的角度來評分. } if(SideToMove()==My){ best=-INFINITY;. //輪到我方走棋 //我方要取最大值,所以將 best 設成無限小. } else{ best=INFINITY;. //敵方要取最小值,所以將 best 設成無限大. } GenerateLegalMoves(); while(MoveLeft()){ MakeNextMove(); val=MinMax(depth-1); UnMakeMove();. //產生所有合理著法 //嘗試每一個走步 //走棋 //遞迴呼叫,層數減 1 //還原走步. if(SideToMove()==My){ if(val>best){ //我方要保留最大值 best=val; } } else{ if(val<best){. //敵方要保留最小值. best=val; } } } return best;. //將分數返回給父節點. } 圖 3-4 Min-Max 搜尋演算法虛擬碼. 18.

(25) 第四節. Nega-Max 搜尋演算法. 在上一節中我們有提到 Min-Max 搜尋演算法,但 Min-Max 總是需要檢查哪 一方要取最大值,而哪一方又要取最小值,而執行不同的動作。Knuth 和 Moore 在 1975 年提出了 Nega-Max 搜尋演算法[3],消除了要去判斷現在是哪一方而取最 大值還是最小值的差別,且使得程式碼變得簡潔優雅。如圖 3-5 是 Nega-Max 搜 尋演算法的虛擬碼:. int NegaMax(int depth){ int best=-INFINITY,val; if(depth==0){ return Evaluate(); } GenerateLegalMoves(); while(MovesLeft()){ MakeNextMove(); val= -NegaMax(depth-1);. //葉節點傳回審局函數的分數 //是以葉節點的角度來評分. //這裡要取負號. UnMakeMove(); if(val>best){ best=val; } } return best; } 圖 3-5 Nega-Max 搜尋演算法虛擬碼. 19.

(26) 從圖 3-5 的虛擬碼可以看出 Nega-Max 比 Min-Max 撰寫起來較為簡潔,而它 的核心在於:父節點的值是各子節點的最大值取負號。之所以能這樣做的原因是 我們今天要解決的是屬於“零和"的棋類博奕,所謂“零和"指的就是說,在遊 戲的任何一個時刻,一方獲得的利益就相當於另一方的損失,不會出現雙贏的局 面,雙方的利益所得加起來等於零,例如:西洋棋、象棋,還有我們這次研究的 暗棋,都是屬於“零和"的棋類博奕。所以,在算法的原理上 Nega-Max 和 MinMax 是完全等效的,Nega-Max 僅僅是一種更好的表達形式,更有利於程式的撰 寫。. 第五節. Alpha-Beta 搜尋演算法. 在 Min-Max 或是 Nega-Max 搜尋演算法中,對於對局樹裡的每個節點的每一 個所有可能著法都要走過一次,假設對局樹中每個節點平均有 30 個著法,如果 要搜尋 N 層,就要拜訪大約 30N 個節點,因此這樣做需要耗費相當多的時間,效 率非常地差[7]。 Brudno 在 1963 年提出了 Alpha-Beta 搜尋演算法,它在大部份的情況下會比 Min-Max 有更佳的搜尋效率。而 Alpha-Beta 的核心思想是:在搜尋的過程中,發 現無論如何都無法改變對方目前的最佳分數時,就可以提早放棄,不必浪費時間 搜尋其它的著法。例如圖 3-6:. 20.

(27) 圖 3-6 Alpha-Beta 示意圖. 圖 3-6 中,方形節點會從子節點中取最大值,圓形節點會從子節點中取最小值, 而葉節點則會傳回以根節點為評分角度的審局函數值。其中節點 A,經由深度優 先搜尋左子樹,得到左子節點的值為 30,由於節點 A 會從其子節點中取最大值, 所以 A 的值必不小於 30,換句話說,A 的右節點的值必須大於 30 ,才可能會被 A 選上。 當找尋過 B 的左子節點時,得到分數 20,因為 B 要取最小值,所以 20 便是 B 的最大可能值。然而,B 之值必須大於 30 才會被 A 選上,且 B 剩下的子節點 無論分數如何,都不可能使 B 的值大於 30,所以就不用往下搜尋剩下的節點了。 Alpha-Beta 搜尋演算法必須使用兩個參數,分別是 α 和 β。其中 α 是記錄最 大層節點目前的最大值,而 β 是記錄最小層節點的最小值,兩個參數以傳值 call by value 的方式傳遞給下層的子樹。如果在取最大值的時候,發現了一個大於等 於 β 的值,就不用再對其它分枝進行搜尋,這就是所謂的 β 截斷;同理,在取最 小值的時候,發現了一個小於等於 α 的值,也不用再對其它分枝進行搜尋,這就 21.

(28) 是所謂的 α 截斷。例如圖 3-7 是 Alpha-Beta 另一個比較複雜的範例:. 圖 3-7 Alpha-Beta 示意圖. 圖 3-7 是 Alpha-Beta 以深度優先從左至右拜訪的樹狀圖,其節點截斷順序如下: 1. B 節點取值 25 的時候,25 ≧ 20,造成 C 節點的截斷(β 截斷)。 2. E 節點取值 6 的時候,6 ≦ 20,造成 F 節點的截斷(α 截斷)。 3. H 節點取值 3 的時候,3 ≦ 6,造成 I 節點的截斷(α 截斷)。 4. J 節點取值 6 的時候,6 ≦ 20,造成 K 節點的截斷(α 截斷)。 很顯然地,Alpha-Beta 搜尋演算法的搜尋效率與走法的排列順序有極密切的 關係。如果總是先嘗試壞的走法的話,那最終將會與 Min-Max 搜尋演算法一樣, 完全沒有任何截斷的機會。倘若能將好的走法排在前面優先嘗試的話,就可以儘 早發生截斷,省掉對其它節點不必要的搜尋。例如圖 3-7 中,總共有 31 個節點,. 22.

(29) 最終只搜尋了 19 個節點,省了 12 個節點。在最佳走法搜尋順序時,Knuth 和 Moore 證明了 Alpha-Beta 搜尋演算法最少必須搜尋的節點數目[10]為:. n = 2b d / 2 − 1 n = b ( d +1) / 2 + b ( d −1) / 2 − 1. (d 為偶數) (d 為奇數). 其中 n 代表搜尋節點數目,b 代表 branch factor, d 代表搜尋深度。 圖 3-8 是以 Nega-Max 形式來表達 Alpha-Beta 搜尋演算法的虛擬碼:. int AlphaBeta(int depth,int alpha,int beta){ int val; if(depth==0){ return Evaluate(); } GenerateLegalMoves(); while(MovesLeft()){ MakeNextMove(); val= -AlphaBeta(depth-1,-beta,-alpha); //alpha 和 beta 順序對調 //且加上負值 UnMakeMove(); if(val>=beta){ return beta;. //beta 截斷. } if(val>alpha){ alpha=val; } } return alpha; } 圖 3-8 Alpha-Beta 搜尋演算法虛擬碼. 23.

(30) 以 Nega-Max 形式寫成的 Alpha-Beta 搜尋演算法,當 α 和 β 兩個參數在傳遞 給下層的時候,是將兩個順序對調,並加上負值,這樣做使得 α 截斷也能簡化成 以 β 截斷的形式表現出來,消除了 Min-Max 形式的 Alpha-Beta 搜尋演算法,在 α 截斷與 β 截斷判斷形式上的不同,並且讓程式碼更簡潔有力。在我們這次暗棋程 式的實作中,也是以 Nega-Max 形式的 Alpha-Beta 搜尋演算法為其最主要核心。. 第六節 寧靜搜尋. 一般利用 Alpha-Beta 搜尋演算法所展開的對局樹深度有限,所以在某一固定 深度之下的情形將會被忽略,這時就經常會有所謂的水平效應(Horizontal Effect) 的產生。例如圖 3-9 輪黑方走搜尋 10 層:. 圖 3-9 水平效應示意圖. 24.

(31) 當搜尋路徑為:C1-D1,F2-F4 吃卒,D1-E1 吃炮,H3-H4 吃卒,E1-D1,H4-H3,. D1-C1,E4-E3,C3-C4,F4-C4 吃馬,總共 10 步後,到達如圖 3-10 的盤面:. 圖 3-10 水平效應示意圖. 此時因為沒有剩餘深度可以讓黑方嘗試走 B4-C4 吃炮,導致黑方最後得到吃炮+. 18 分、損失兩隻卒-2 分、損失一隻馬-6 分,總共 10 分這樣誤判的結果。 為了解決上述的問題,我們使用寧靜搜尋(Quiescent Search)。當沒有剩餘 深度時不立刻返回審局函數的評估分數值,而是根據盤面只針對吃子步進行展開 ,直到最後的寧靜盤面,也就是不再有棋子被吃為止,才返回審局函數的值。這 樣的做法多少可以克服因水平效應而造成的搜尋不準確的結果。 圖 3-11 是 Quiescent Search 的虛擬碼:. 25.

(32) int Quies(int alpha,int beta){ int val; GenerateCapMoves(); while(MovesLeft()){ MakeNextMove(); val= -Quies(-beta,-alpha);. //只產生吃子步. //遞迴呼叫時不再需要 depth 參數. UnMakeMove(); if(val>=beta){ return beta; } if(val>alpha){ alpha=val; } } return alpha; } 圖 3-11 Quiescent Search 虛擬碼. 在我們撰寫 Quiescent Search 的過程中,有看過許多版本的虛擬碼,它們都 會在 GenerateCapMoves()之前,先呼叫審局函數 Evaluate(),看審局函數的值是否 可以造成 β 截斷,如果可以造成 β 截斷就直接返回 beta 值,不過我們發現這樣做 的結果可能會導致搜尋結果不正確。原因如圖 3-12,輪黑方走,進行 10 層的搜 尋:. 26.

(33) 圖 3-12 寧靜搜尋示意圖. 當搜尋路徑為:F1-G1,F2-G2,H3-H2,E1-F1,H2-G2 吃兵,F1-E1,B1-A1,. E1-F1,A1-B1,F1-G1 吃包,總共 10 步後,到達如圖 3-13 的盤面:. 圖 3-13 寧靜搜尋示意圖. 此時輪黑方走,如果這時呼叫審局函數 Evaluate(),會得到-17 分的值,而這時窗 口的 beta 值是-18,-17 大於-18 會造成所謂的 β 截斷,所以將分數回傳上去,最. 27.

(34) 後導致圖 3-12 輪黑方走的搜尋結果為-17 分。這樣的搜尋結果是錯的,因為正確 的結果應該是紅、黑雙方在 10 層的搜尋內,互相都吃不到對方的子,返回 0 分 才對。所以,寧靜搜尋當盤面還有吃子步可以嘗試的時候,不應該直接用審局函 數的值來當做是否可以截斷的依據,而直接返回 beta 值,以免造成搜尋結果的不 正確。. 第七節. Transposition Table. 在對局樹的搜尋過程中,不少盤面節點雖然是經過不同走法順序所到達的, 但其盤面狀態有可能是完全一致的,如圖 3-14:. 圖 3-14 相同盤面示意圖. 不管是走路徑:E2-F2,E4-F4,F2-F3,G3-H3,還是走路徑:E2-E3,E4-F4,E3-F3,. G3-H3,都可以到達如圖 3-15 的盤面: 28.

(35) 圖 3-15 相同盤面示意圖. 從以上的例子可以知道,如果之前有某個盤面節點已經搜尋過的話,當再次 遇到同樣狀態盤面節點時,就可以套用之前搜尋的結果,省下再重複搜尋的時間。 而 Transposition Table(又稱 Hash Table) ,則是一個可以將已搜尋過的節點資 訊記錄起來的雜湊表。節點資訊通常包括:盤面 key 值、節點搜尋深度、節點分 數、節點類型…等。一般使用 Zobrist Hash 的方式來進行盤面 key 值的生成,以 達到快速檢測當前節點是否已經搜尋過的目的。. Zobrish Hash 方法是在搜尋之前,預先產生大隨機數的二維陣列 Zobrist[棋子 類型][棋子位置],至於大隨機數要多大?在我們這次的暗棋程式實作裡是使用. 32 位元的無號整數,有了 Zobrist[棋子類型][棋子位置] 這樣的二維陣列以後, 當前盤面的 key 值,便是所有棋盤上的棋子相對應到 Zobrist[棋子類型][棋子位置] 二維陣列做 xor 運算(以^表示)後的和。這樣在有著法產生移動時,並不需要重 新計算盤面的 key 值,只需要將當前 key 值(假設叫:ZobristKey)做以下步驟[8]:. 29.

(36) 1. ZobristKey^=Zobrist[移動棋子類型][移動棋子原位置]。 2. ZobristKey^=Zobrist[移動棋子類型][移動棋子新位置]。 3. 如果著法是吃子著法的話,需要再做:ZobristKey^=Zobrist[被吃棋子類型][被 吃棋子位置]。 而為了避免盤面相同輪走方不同卻擁有一樣的 key 值的情況,我們還需要一個大 隨機數 ZobristPlayer 來代表輪走方,每走一步就做 ZobristKey^=ZobristPlayer 運 算,最後的 ZobristKey 才是真正代表盤面且加上輪走方訊息的 key 值。如果不加. ZobristKey^=ZobristPlayer 運算會怎樣呢?我們以如圖 3-16 輪紅方走當做例子, 此時的盤面 key 值 ZobristKey = Zobrist[卒][0] ^ Zobrist[兵][1] ^ Zobrist[炮][2] ^. Zobrist[ 卒 ][7] = 816894721 ^ 138765628 ^ 3996203608 ^ 1478426211 = 1812624575。. 圖 3-16 盤面相同輪走方不同示意圖. 30.

(37) 紅方走了 C1-A1 吃卒後,如圖 3-17,此時輪黑方走,此時的盤面 key 值 ZobristKey. = ZobristKey ^ Zobrist[炮][2] ^ Zobrist[炮][0] ^ Zobrist[卒][0] = 1812624575 ^ 3996203608 ^ 3032318783 ^ 816894721 = 2935939268。. 圖 3-17 盤面相同輪走方不同示意圖. 如果圖 3-16 紅、黑雙方依路徑:B1-A1 吃兵,H1-H2,C1-C2,H2-H1,C2-B2,. H1-H2,B2-A2,H2-H1,A1-B1,H1-H2,A2-A1,H2-H1,也可以到達如圖 3-17 的盤面,但此時是輪紅走,而盤面 key 值仍舊是 2935939268。所以如果只用棋盤 上所有棋子相對應的 Zobris t[棋子類型][棋子位置]做 xor 後的總和,這時就會發 生盤面相同但輪走方不同卻擁有相同 key 值的情況,而造成搜尋時節點資訊的誤 用。如果有在每走一步做 ZobristKey^=ZobristPlayer 運算,以圖 3-16 為例,輪紅 方走時 ZobristPlayer 必然跟 ZobristKey 做了偶數次 xor 運算,而輪黑方走時. ZobristPlayer 必然跟 ZobristKey 做了奇數次 xor 運算,所以即使原來的 ZobristKey. 31.

(38) 相同,也會因為跟 ZobristPlayer 做了奇、偶次數 xor 運算的關係,而讓最後的. ZobristKey 值不同。 Hash Table 是一種空間換取時間的思維,由於實際記憶體空間有限,所以我 們不可能替 Hash Table 設置一個無限大的空間供其使用,因此我們必須對所產生 的節點是否存入 Hash Table 做些限制,避免一些無用的節點資訊佔據了 Hash Table 的空間,而這些無用的節點通常是一些距離根節點很遠的節點,因為它們被重複 搜尋的機率很低。還有當兩個節點盤面值都對應到同一個 slot 位置時,也必須有 因應的策略。其中 slot 的算法為:slot = 盤面 key 值%Hash_Table_Size。其中%表 示求餘數的運算。 在我們這次暗棋程式的實作中,Hash_Table_Size 為 1M,剛好是 220,因為在 計算 slot 位置時必須用到除法指令,但電腦一做除法就會很耗時,所以如果能將. Hash_Table_Size 設為 2N 這個常量,想要得到除以 2N 後的餘數,只要做如下修改: slot = 盤面 key 值& ( Hash_Table_Size – 1 ),以圖 3-17 輪紅走為例,可算出 slot = 盤面 key 值 2935939268 &(1M – 1)=2935939268 & 1048575 = 975044,而電腦 做&運算(AND)的速度是非常快的。 還有我們對於寧靜搜尋的節點不存入 Hash Table,避免過多距離根節點很遠 的節點充斥整個 Hash Table ,而它們被再利用的機率卻很小。而對於盤面值對應 到同一個 slot 位置時,我們採用“深度優先覆蓋"的策略,所謂的“深度優先覆 蓋"即待存入 Hash Table 的節點的深度必須大於或等於已存入 Hash Table 節點的. 32.

(39) 深度才覆蓋。據研究表示:深度優先覆蓋的效果要比始終覆蓋或完全不覆蓋要好 得多[2],且程式撰寫起來並不複雜。如圖 3-18 是節點資訊存入 Hash Table 的函 式虛擬碼:. void RecordHash(int depth,int val,int hashf){ HashStruct *p = &Hash_Table[ZobristKey & (Hash_Table_Size-1)]; if(depth>=p->depth){ //深度優先覆蓋,存入節點相關資訊 p->key=ZobristKey; //存入盤面 key 值 p->depth=depth; //存入節點深度 p->val=val; //存入節點分數 p->hashf=hashf; //存入節點類型 } } 圖 3-18 節點存入 Hash Table 的函式虛擬碼. 從上面的虛擬碼可以看出,Hash Table 裡最後還存了節點類型。而為什麼已 經存入了節點分數,還要再存入節點類型?這是因為在 Alpha-Beta 搜尋演算法的 過程中,任何節點的分數都是下列三種情況之一: (一)節點分數 ≧ beta,即所謂的 beta 節點。 (二)節點分數 ≦ alpha,即所謂的 alpha 節點。 (三)alpha < 節點分數 < beta,即所謂的 PV(Principal Variation)節點。 一般來說,只有 PV 節點的分數,才可以當作是節點的準確值存入 Hash Table ,其餘的 beta 節點、alpha 節點的分數只能表示是節點分數的一個邊界而已。但. 33.

(40) 存入這樣的邊界分數,仍有助於我們下次搜尋到同樣盤面節點的時候,進行剪裁 的動作。所以既然三種節點的分數都可以存入 Hash Table ,我們也就必須在存入 分數的同時,把該分數所代表的意義,也就是節點類型給存下來。當目前節點探 測 Hash Table 成功時,如果已存入的節點類型是 PV 的話,則直接返回節點分數 ,如果已存入節點類型是 beta 或 alpha 的話,需要進行邊界值的比較,決定是否 能剪裁。如圖 3-19 是節點探測 Hash Table 的函式虛擬碼:. int ProbeHash(int depth,int alpha,int beta){ HashStruct *p = &Hash_Table[ZobristKey & (Hash_Table_Size-1)]; if(ZobristKey==p->key&&p->depth>=depth){ //探測成功 if(p->hashf==PV_FLAG){ return p->val; } if(p->hashf==BETA_FLAG&&p->val>=beta){ //與上界 beta 比較 return beta; } if(p->hashf==ALPHA_FLAG&&p->val<=alpha){//與下界 alpha 比較 return alpha; } } return valUNKNOWN;. //探測失敗或是剪裁失敗. } 圖 3-19 節點探測 Hash Table 的函式虛擬碼. 從上面的虛擬碼可以看出,所謂的探測成功,除了比較當前的盤面值與 Hash. Table 的盤面值是否一樣以外,還要比較 Hash Table 的節點深度是否大於或等於當 34.

(41) 前節點的深度。這是因為 Hash Table 中的節點深度如果比當前節點深度淺的話, 所存的節點資訊還不足以讓當前節點拿來使用,因為它的搜尋深度還不夠深。只 有在 Hash Table 的節點深度大於或等於當前節點深度的時候,我們才能拿裡面的 資訊來利用。圖 3-20 是 Alpha-Beta 搜尋結合 Hash Table 的虛擬碼:. int AlphaBeta(int depth,int alpha,int beta){ int val,foundPV=0; if(depth==0) return Quies(alpha,beta); //進入寧靜搜尋 if((val==ProbeHash(depth,alpha,beta))!=valUNKNOWN){ //探測 Hash Table return val; } GenerateLegalMoves(); while(MovesLeft()){ MakeNextMove(); val= -AlphaBeta(depth-1,-beta,-alpha); UnMakeMove(); if(val>=beta){ RecordHash(depth,beta,BETA_FLAG);. //存入 beta 節點. return beta; } if(val>alpha){ foundPV=1;. //有找到 PV 節點. alpha=val; } } if(foundPV) RecordHash(depth,alpha,PV_FLAG); else RecordHash(depth,alpha,ALPHA_FLAG);. //存入 PV 節點 //存入 alpha 節點. return alpha; } 圖 3-20 Alpha-Beta 結合 Hash Table 的虛擬碼. 35.

(42) 第八節 循環剪裁. 在許多時候,從某個盤面走了幾步後又回到跟原來盤面一樣的情形,例如圖. 3-21,輪紅方走:. 圖 3-21 重覆盤面示意圖. 上圖紅、黑雙方走了:D3-D4,D1-D2,D4-D3,D2-D1 後,又回到跟原來一 樣的盤面。在這們這次暗棋程式的實作裡,有用一個小型的 Hash Table,其大小 為 128 個 entry,只記錄當前路徑所走過的每個節點的盤面 key 值,節點返回上一 層時會移除它在 Hash Table 中的 key 值。當要替目前節點展開所有合理著法進行 子樹的搜尋前,我們會先探測在這小型的 Hash Table 裡,是不是已經有相同的盤 面了,如果有的話則呼叫審局函數,返回審局分數,而不用再對子樹進行搜尋, 進而提高搜尋效率,這就是所謂的循環剪裁。. 36.

(43) 第九節 允許空步. 在電腦西洋棋、象棋裡有所謂的空步剪裁(Null Move pruning) ,利用我方不 走棋而讓對方連續走棋,且在減少深度的淺層搜尋下,如果也能使節點分數大於 等於 beta 的話,則進行剪裁[9]。而在我們這次的暗棋程式實作中,我們在搜尋時 賦予走空步另一層意義,走空步只是為了在還有暗棋的盤面時,如果走的每一步 棋都會使情況更槽,這時不應該強迫走棋,因為在現實的狀況中,它可能會去做 翻棋的動作。例如圖 3-22,輪黑方走:. 圖 3-22 空步示意圖. 黑方走了 A2-A1 後,紅方走 B1-A1 吃卒後,如圖 3-23,輪黑方走:. 37.

(44) 圖 3-23 空步示意圖. 此時黑方剩 E1-D1 象吃兵可以走,但吃了兵後,紅方可以走 C1-D1 帥吃象,則黑 方損失慘重,因為損失了一隻卒和一隻象,但只獲得 1 隻兵共 –90 的分數。如圖. 3-23 的情況,黑方會嘗試翻棋,但我們在搜尋時為了速度不會真的去做翻棋動作 的搜尋,所以用允許空步來表示翻棋的動作。所以如圖 3-22 輪黑方走,假設進行. 4 層的搜尋,可得到:A2-A1,B1-A1 吃卒,空步(嘗試翻棋) ,A1-B1,總共 –1 分。這樣做可以在讓有暗棋的情況下,如果明棋的每個走步走了都會更槽時,可 以允許空步,隱含翻了某個暗棋的動作,讓審局分數更能反應真實情況的價值。. 第十節 最佳路徑的獲取. 當我們對一個盤面進行搜尋完成之後,除了要得到準確的分數外,對於最佳 路徑的獲取也是很關心的。例如圖 3-24,輪黑方走,進行 6 層的搜尋:. 38.

(45) 圖 3-24 路徑獲取示意圖. 當搜尋路徑為:B3-A3,H1-G1,A3-A4,G1-F1,A4-H4 吃仕,F1-E1,總共走了. 6 步後,黑方可以得到 270 分(吃掉一隻紅仕的分數) 。而當搜尋路徑為:B3-B4, H1-G1,B4-H4 吃仕,G1-F1,G3-G2,F1-E1,黑方也一樣可以得吃掉一隻紅仕 的分數。而在實戰中,因為黑方先嘗試展開 B3-A3 這條路徑,所以它走了 B3-A3 以為可以吃到紅仕,如圖:3-25,輪紅方走,而紅方走了 H1-G1 後如圖 3-26,又 輪到黑方走:. 圖 3-25 路徑獲取示意圖 39.

(46) 圖 3-26 路徑獲取示意圖. 黑方此時又因為搜尋路徑:A3-B3,G1-F1,B3-B4,F1-E1,B4-H4 吃仕,E1-D1 先被嘗試,所以又走了 A3-B3。黑方這樣反反覆覆在 B3-A3 ÙA3-B3 這兩步徘徊, 以為可以吃到紅仕,但在實戰中若這樣反覆走步卻怎樣也吃不到紅仕。 而為了解決上述的現象,其實我們可以用疊代加深搜尋(Iterative Deepening. Search)的方式來解決。以圖 3-26 黑方要進行 6 層的搜尋為例,當疊代搜尋到第 3 層時,黑方可以得到最佳路徑:A3-A4,G1-F1,A4-H4 吃仕,因此搜尋完 3 層 的最佳走步為:A3-A4。同樣地,搜尋完 4 層的最佳步也是 A3-A4。而搜尋到第. 5 層時,因為會拿搜尋完 4 層的最佳步,來當做 root 第一個嘗試的走步,因此 A3-A4 這一步會比 A3-B3 這步同樣可以獲得一隻仕的走步要先被搜尋,所以最後搜尋完. 5 層的最佳走步,依然會是 A3-A4。同理,搜 6 層時 A3-A4 仍然會優先被搜尋, 所以最後搜完 6 層之後的最佳走步就是 A3-A4。使用疊代加深搜尋可以保證,如 果能獲得利益的話,它會走出最快獲得利益的走法。因為在淺層疊代搜尋時,就. 40.

(47) 已經搜尋到的最佳走法,如果疊代搜尋到最後,走這樣的步依然可以獲得最大利 益的話,它將不會被後來同樣可以獲得一樣利益的其它走步給覆蓋。 而在劣勢時,又該怎樣挑出最頑強的抵抗手法?以圖 3-27 為例,輪黑方走, 進行 10 層的搜尋:. 圖 3-27 路徑獲取示意圖. 從圖 3-27 可以看得出來,黑方的象會在 10 步以內被仕跟帥夾殺,但要怎樣走出 最頑強的抵抗手法?同樣地,可以利用疊代加深搜尋的方式,確保走出來的步是 最頑強的走法,來將自己的戰線延長,增加對手犯錯的機會。如果不用疊代加深 搜尋的話,可能因為搜尋路徑:B2-B1,A1-B1 吃象,H1-G1,D3-E3,G1-F1,. E3-E2,F1-G1,E2-F2,G1-H1,F2-G2,先被搜尋(黑方的象在第 2 步 A1-B1 就 被吃了),而搜尋路徑: B2-C2 ,A1-B1 , H1-G1, B1-B2,C2-C1, D3-D2 ,. G1-F1,D2-D1,C1-B1,B2-B1 吃象,後被搜尋(黑方的象在第 10 步 B2-B1 才 被吃)。那麼走這兩條路徑都是損失一隻象的分數,所以 B2-B1 比 B2-C2 優先被. 41.

(48) 嘗試,使得它成為搜尋完 10 層後最佳走步。從這邊可以看的出來不使用疊代加 深搜尋的話,可能會受到著法優先嘗試順序的影響,而走出提前送子的情形。. 第十一節 走棋與翻棋的抉擇. 在暗棋的遊戲裡,最難的就是走棋與翻棋的抉擇。當面對一個盤面時,往往 不知道該走棋比較好還是翻棋比較好,又或是一定要翻棋時,也不知道該翻哪個 位置比較好,這一點是跟西洋棋、象棋有很大的不同。 為了在走棋與翻棋中做抉擇,我們在這次的暗棋程式實作中將搜尋拆成兩部 份: (一)明棋搜尋。 (二)翻棋搜尋。明棋搜尋時只對翻開的棋子進行走步的深 層搜尋(例如:搜尋 10 層) ,搜尋的過程中不做翻棋的動作,且根節點(第一步) 不允許走空步。而翻棋搜尋時,會對每一個尚未翻開棋子的位置,賦予其每一個 可能出現的兵種,以此狀態當做根節點,然後進行走步的淺層搜尋(例如:搜尋. 6 層),同樣地,搜尋的過程中不做翻其它棋的動作。 翻棋搜尋的結果最後會拿來跟明棋搜尋的結果做比較,一開始先將某位置的. gt、sum 初始為 0,如果翻棋(翻出某一兵種)搜尋的結果好於明棋搜尋的話,則 gt = gt + 該兵種未翻開的數量;如果翻棋搜尋的結果比明棋搜尋還要壞的話,則 gt = gt – 該兵種未翻開的數量。最後,不管是好是壞都執行 sum = sum + (翻棋搜 尋分數 – 明棋搜尋分數) * 該兵種未翻開的數量。這樣替每個尚未翻開棋子的位 置,都算出 gt 和 sum 值後,取出 gt 值最高的位置,如果有好幾個位置 gt 值一樣 42.

(49) 的話,則比較 sum 值,來當作是翻棋最有利的位置。 最後,要在走棋與翻棋做抉擇時,只要判斷翻棋最有利位置的 gt 值是不是大 於 0 即可,如果大於 0 的話,則選擇翻棋,否則就走棋。 我們以實戰中如圖 3-28,輪黑方走為例,看黑方是怎樣在走棋與翻棋之間做 抉擇的:. 圖 3-28 走棋與翻棋抉擇示意圖. 此時盤面還剩:士*2、象*1、車*2、馬*2、包*2、卒*3、仕*1、相*1、俥*2、傌. *2、炮*2、兵*2,總共 22 隻棋子還沒翻開。 現在黑方進行 10 層的明棋搜尋,得到的結果是: 主要路徑:A2-A1,B1-A1,空步,C1-B1,空步,B1-C1 分數:-1(我們令 x = -1),是黑方損失一隻卒的分數。. 43.

(50) 接著進行 6 層的翻棋搜尋,得到的結果是翻 F1 或 E2 最有利,因為兩個位置所算 出來的 gt 和 sum 值都一樣。底下我們則以翻 F1 位置時,它的 gt 和 sum 值是如何 算出來的為例: 現在準備翻開位置 F1,令 gt 和 sum 初始值都為 0: (1)翻出士後再搜尋:B2-A2,空步,A2-A1,空步,A1-A2,所得分數:-1, 士還有 2 個未翻開。因為所得分數–1 = x,所以 gt 值不變,sum = sum + ( -1 – x ) *. 2 = 0。 (2)翻出象後再搜尋:B2-A2,空步,A2-A1,空步,A1-A2,所得分數:-1, 象還有 1 個未翻開。因為所得分數-1 = x,所以 gt 值不變,sum = sum + ( -1 - x ) *. 1 = 0。 (3)翻出車後再搜尋:B2-A2,空步,A2-A1,空步,A1-A2,所得分數:-1, 車還有 2 個未翻開。因為所得分數-1 = x,所以 gt 值不變,sum = sum + ( -1 - x ) *. 2 = 0。 (4)翻出馬後再搜尋:B2-A2,空步,A2-A1,空步,A1-A2,所得分數:-1, 馬還有 2 個未翻開。因為所得分數-1 = x,所以 gt 值不變,sum = sum + ( -1 - x ) *. 2 = 0。 (5)翻出包後再搜尋:B2-A2,空步,A2-A1,空步,A1-A2,所得分數:-1, 包還有 2 個未翻開。因為所得分數-1 = x,所以 gt 值不變,sum = sum + ( -1 - x ) *. 2 = 0。. 44.

(51) (6)翻出卒後再搜尋:B2-A2,空步,A2-A1,空步,A1-A2,所得分數:-1, 卒還有 3 個未翻開。因為所得分數-1 = x,所以 gt 值不變,sum = sum + ( -1 - x ) *. 3 = 0。 (7)翻出仕後再搜尋:F1-E1,A2-A1,B1-A1,空步,C1-B1,所得分數:-91, 仕還有 1 個未翻開。因為所得分數-91 < x,所以 gt = gt – 1 = -1,sum = sum + ( -91. - x ) * 1 = -90。 (8)翻出相後再搜尋:F1-E1,A2-A1,B1-A1,空步,C1-B1,所得分數:-91, 相還有 1 個未翻開。因為所得分數-91 < x,所以 gt = gt – 1 = -2,sum = sum + ( -91. - x ) * 1 = -90 – 90 = -180。 (9)翻出俥後再搜尋:B2-A2,E1-F1,A2-A1,F1-E1,A1-A2,E1-F1,所得分 數:17,俥還有 2 個未翻開。因為所得分數 17 > x,所以 gt = gt + 2 = -2 + 2 = 0,. sum = sum + ( 17 - x ) * 2 = -180 + 18 * 2 = -144。 (10)翻出傌後再搜尋:B2-A2,E1-F1,A2-A1,F1-E1,A1-A2,E1-F1,所得 分數:5,傌還有 2 個未翻開。因為所得階數 5 > x,所以 gt = gt + 2 = 2,sum = sum. + ( 5 - x ) * 2 = -144 + 6 * 2 = -132。 (11)翻出炮後再搜尋:B2-A2,E1-F1,A2-A1,F1-E1,A1-A2,E1-F1,所得 分數:17,砲還有 2 個未翻開。因為所得分數 17 > x,所以 gt = gt + 2 = 4,sum =. sum + ( 17 - x ) * 2 = -132 + 18 * 2 = -96。. 45.

(52) (12)翻出兵後再搜尋:B2-A2,E1-F1,A2-A1,F1-E1,A1-A2,E1-F1,所得 分數:0,兵還有 2 個未翻開。因為所得分數 0 > x,所以 gt = gt + 2 = 6,sum = sum. + ( 0 - x ) * 2 = -96 + 1 * 2 = -94。 最後可以得到 gt = 6,sum = -94。 而最後要做走棋與翻棋的抉擇時,因為 gt = 6 > 0,表示 F1 位置可能翻出 22 顆棋子,gt 值將介於 -22 至 22 之間,實際算出 gt = 6,翻棋划算的機率較高,所 以黑方選擇翻棋較為有利。我們在實戰測試時黑方翻了 F1 翻到紅俥,如圖 3-29:. 圖 3-29 走棋與翻棋抉擇示意圖. 46.

(53) 第四章 結論與未來研究方向 第一節 結論 據我們所知,在此之前尚未有任何電腦暗棋的相關文獻,因此本論文首度對 電腦暗棋做較深入的研究。我們透過對電腦暗棋所需要的資料結構及搜尋演算法 的設計與實作,來闡述一種可能在大部份情況下都能有不錯棋力的方式,用來解 決電腦暗棋相關問題。 我們所撰寫的暗棋程式,在 CPU:1.83GHz、記憶體:512MB 的環境下,分 別與極真科技的暗棋王還有人類玩家(即本論文作者、網路玩家)進行 20 盤的 實戰測試,平均都能在 30 秒以內做出決策(走棋或翻棋),其最終結果如下:. 勝. 敗. 和. 我們的程式 vs 暗棋王. 14 盤. 4盤. 2盤. 我們的程式 vs 人類玩家. 8盤. 8盤. 4盤. 表 4-1 實戰測試結果. 由上實戰測試結果可以發現,與暗棋王對戰,如果扣掉和局不算的話,我們 所寫的暗棋程式勝率高達 77%(=. 14 ),而暗棋王是極真科技在 2005 年出版 14 + 4. 的暗棋軟體,在我們蒐集的程式當中,屬於棋力最高的一款。但由於該程式可能 47.

(54) 是以 rule base 來撰寫人工智慧的部份,所以在很多情況下會誤判情勢,而最終被 我們以搜尋演算法為核心的程式給擊敗。再看我們的程式與人類玩家實戰測試的 結果,仍有 50%(=. 8 )的勝率,可見我們的程式已有接近人類玩家的棋力。 8+8. 而之所以會有敗績,乃是因為暗棋本屬於不完全資訊含機率性的遊戲,當程式在 嘗試翻子的時候,只能以近似的算法,來算出可能翻哪個位置比較有利,但如果 翻到的棋子真的很差的話,或是對方剛好翻到很好的棋子的話,自然就會導致最 後的敗果。而從實戰測試結果可以發現,即使在含有機率的情況下,我們以搜尋 演算法為基礎核心的方式,還是比用 rule base 的方式要來的好。如圖 4-1,紅方 是暗棋王,黑方是我們的程式,現在輪黑方走:. 圖 4-1 與暗棋王實戰圖. 黑方走了 F2-F1 的妙著,如圖 4-2,輪紅方走:. 48.

(55) 圖 4-2 與暗棋王實戰圖. 紅方走了 G2-H2 仕吃卒,準備再吃車,如圖 4-2,輪黑方該我們的程式走:. 圖 4-3 與暗棋王實戰圖. 此時黑方走了 E1-D1 象吃兵後,紅、黑方雙進行如下走步:C1-D1 帥吃象,E2-E1 棄士,D1-E1 帥吃士,如圖 4-4,輪黑方走:. 49.

(56) 圖 4-4 與暗棋王實戰圖. 黑方最後走了 E4-E1 包吃帥。從圖 4-3,紅方走了 G2-H2 吃卒後,黑方一連串棄 象、棄士,而到最後包轟帥的手段,真叫人拍案叫絕。倘若紅方沒有 G2-H2 吃卒 ,或許可以防止黑方後來的一連串棄子取帥的手段。此例(如圖 4-2)輪紅方走, 若改由我們的程式來下紅方,則會走 G2-G1 捉包,因而避開被吃帥的危機。由此 可見,搜尋的威力此時勝於 rule base 對情勢的判斷。而面對人類玩家是以策略性 思考時,搜尋仍讓我們所走出來的棋步或翻子,具有相當一定的水準,使得與人 類玩家進行實戰測試時,仍有五成的勝率,可以與人類玩家相庭抗衡。. 第二節 未來研究方向 我們的暗棋程式是以搜尋演算法為其主要核心,所以若是能將搜尋的部份透 過平行化計算或是多執行緒的處理,則可以提高搜尋的速度。一般而言,棋類程 式的棋力與搜尋深度成正比,當然暗棋程式也不例外,因此若採用平行計算的方. 50.

(57) 式,將對局樹做適當的切割後,交由給不同的處理器去做搜尋,則能大幅提高搜 尋的速度,加深搜尋深度,進而提高棋力。除了將搜尋改成平行的計算方式外, 其實也可以考慮如何讓比較重要的路徑做延伸搜尋(Extension),而讓比較不重 要的路徑做減少深度的搜尋(Reduction),這樣的做法也可以使棋力提高,是值 得研究的方向。 另一方面就是審局函數的改良,我們目前的審局函數只有單純的子力價值分 數,所以當殘局盤面可能要花十幾手,甚至二十幾手棋才會有吃子的情形時,因 為搜尋的深度有限,無法看到需要花那麼多手棋才會顯示出來的盤面反應,而導 致走出來的棋步,無法以目標導向的朝著正確的路徑前進。所以若是能加入一些 棋形特徵,例如考慮棋子間的位置關係,或是兵種之間的組合,都能幫助我們在 殘局盤面時,在有限深度搜尋的情況下,仍可以有目標導向的朝著較正確的路徑 前進。所以改良審局函數,加入一些棋形特徵的判斷,也是很值得研究的方向。. 51.

(58) 參考著作. [1] “Wikipedia",網址:http://zh.wikipedia.org/wiki/。 [2] “象棋百科全書",網址:http://www.elephantbase.net/index.htm。 [3] 王小春,“人機博奕",重慶大學出版社,2002 年 6 月。 [4] 吳身潤,“人工智慧程式設計",維科圖書,2002 年 3 月。 [5] 何宏發、謝秋桂,“電腦象棋-原理、設計、實作及工具箱",第三波出版社, 1988 年 12 月。 [6] 方裕欽,“UCT 算法的適用性及改進策略研究-以黑白棋為例",國立臺灣 師範大學資訊工程研究所碩士論文,2008。. [7] 林子哲,“「深象」象棋軟體平行化之研究",國立臺灣師範大學資訊工程 研究所碩士論文,2007。. [8] 涂志堅,“電腦象棋的設計與實現",中山大學碩士論文,2004。 [9] 郭哲宇,“電腦象棋擴大空步剪裁演算法的設計及實作",國立臺灣師範大 學資訊工程研究所碩士論文,2007。. [10] 黃文樟,“電腦象棋深象中局程式的設計與實作",國立臺灣師範大學資訊 工程研究所碩士論文,2006。. 52.

(59)

參考文獻

相關文件

push cx ; save character count mov si,dx ; point to input buffer dec cx ; save room for null byte L1: mov ah,1 ; function: keyboard input. int 21h ; returns character in AL cmp

method void setInt(int j) function char backSpace() function char doubleQuote() function char newLine() }.. Class

method void setInt(int j) function char backSpace() function char doubleQuote() function char newLine() }. Class

[r]

Write three nuclear equations to represent the nuclear decay sequence that begins with the alpha decay of U-235 followed by a beta decay of the daughter nuclide and then another

◦ 金屬介電層 (inter-metal dielectric, IMD) 是介於兩 個金屬層中間,就像兩個導電的金屬或是兩條鄰 近的金屬線之間的絕緣薄膜,並以階梯覆蓋 (step

強制轉型:把 profit轉換成double的型態

int main(int argc, char** argv).