五子棋人機對戰


差不多是我一年前做的項目了,今天翻回來再回顧回顧,總結下。

1.  項目流程

UI界面部分

利用Java的圖形界面工具swing和awt來繪制棋盤的框架,繪制了15X15的網格。然后在畫布上增加監聽器來監聽鼠標點擊的部分,然后在鄰近的網格交點處繪制棋子,這樣就實現了下棋的效果。

邏輯處理部分

19X19的棋盤,就用19X19的二維數組來存儲棋盤的信息。每當玩家新增加一個棋子,后台部分就會遍歷一遍數組,對每一個槽進行8個方向深度為5的DFS搜索,判斷是否有五子相連的情況,這樣就實現了輸贏的判斷。

人機對戰原理

簡而言之就是統計棋型累計評分,例如4個我方或對方的棋子相連,那么權重就是一個非常大的數,用來表示這個位置非常重要;如果是一個3個棋子相連也賦予一個對應權值。我把這種棋局用字符串表示,權重用整型數表示,以鍵值對的方式存儲在一個HashMap中,那么這個哈希表就是電腦的判斷棋局的依據。接着在電腦出手的時候,就遍歷一遍二維數組,同樣是對每一個槽進行8個方向的DFS搜索,之后查詢哈希表累計該槽的權值。遍歷完之后,權值最大的位置就是電腦下棋的位置。

人機對戰進階

極大值極小值搜索

初始版本的AI只能根據當前的局勢判斷下一步,而極大極小值算法可以自己先多走幾步,預判未來的局勢再決定當前的局勢。過程是這樣的:


先設定好一個搜索樹層數 Depth ,即要模擬 Depth 步;因為每一方都想贏,所以每一步的模擬都是模擬當前角色的最優下法,那么從AI方計算的視角來看,AI下棋的時候應該要使得全局的權值最大,對方下棋的時候就應要使得全局的權值最小;其實雙方的最優下法邏輯是一樣的,只不過從我方來看對方要_加個負號_,也可以說是我要使我的分數盡量大,對方要使我的分數盡量小。最后,綜合模擬的若干步累計權值,取其中權值最大的下法下棋。


但是這個算法的開銷很大,因為假設棋盤有N個空位,那么這棵搜索樹的第一層就有N個節點,第二層就有N-1個節點……這樣下去時間復雜度和空間復雜度都是指數級的增長,所以為了優化我們需要剪枝來舍去不必要的搜索。

剪枝

這個算法就是給搜索樹上的每一個節點維護一個下界為上界為的窗口。這個窗口從父親到兒子直接傳遞,從兒子回溯到父親就要進行相應變化,因為搜索樹結果信息是自底向上傳遞的,所以更新也是自底向上更新。如果當前為對方節點,那么對方會盡量使可取值小,故下拉上界;若當前使我方,則會盡量使可取值大,故上推下界

如果發現那么就可以剪枝了,為什么呢?首先一個前提是,當前的每一步都是由AI來模擬對於每一個下棋這的最優走法。假設當前為MAX方,那么由某個子節點的信息MAX方更新了發現,說明當前MAX節點可獲得一個值大於由對方約束的最大值,那么對方是肯定不會走這里的,所以本節點之后的其他子節點就沒有繼續搜索的必要了;同理,假設當前為MIN方,那么由某個子節點的信息MIN方更新了,若發現,說明當前的MIN節點可以獲得一個值小於對方約束的最小值,那么對方是肯定不會走這里的,所以本節點的其他子節點可以剪掉。

項目中遇到的問題

剪枝算法的檢查

2. 知識點涉及

HashMap

哈希表本質上就是一個數組,只不過是通過哈希函數映射某個關鍵碼來獲取數組的下標。正是利用了數組空間連續查詢高效,所以哈希函數如果設計得好,能夠在的復雜度下實現查找。
但是容易出現沖突,就是不同的關鍵碼通過哈希函數映射出同一個值,這個時候就需要解決沖突的策略。大體上分為兩種:

  • 一種是開散列,就是把沖突放到哈希表外部解決,例如拉鏈法,Java中的 HashMap 就是利用拉鏈法來解決沖突的;
    • 第二種是閉散列,就是在哈希表內部解決沖突,例如開放地址法,桶式散列等等。

DFS

本質上就是狀態的轉移。它從某一個初始狀態開始,一直轉移到狀態無法轉移為止。如果把搜索的過程看作一棵樹,那么信息的傳遞就會是自下而上或者是自上而下,然后對應的最終的完整信息狀態就會出現在根或者是葉子。對於我這個五子棋,信息的傳遞就是自上而下的,也即最終某個方向上棋局的狀態是搜索到葉子處獲得。

3. 亮點

當然就是能夠人機對戰啦,還有自認為令人舒適的UI界面。

4. 可改進之處

  • 剪枝后的效率還是不太高,3層的延時在2s左右

5. 我的收獲


image.png
image.png
image.png


免責聲明!

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



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