java五子棋實現---權值、博弈樹
阿新 • • 發佈:2019-02-16
花了很多天學習的一個關於五子棋的博弈樹,記錄一下。
先講一下五子棋的基本實現過程:
一、介面實現
Gobang.java
show(){}
main(){}
paint(){}
視窗使用Border佈局,寫倆JPanel,一個畫棋盤,一個做動作按鈕。這裡要注意的有兩點:一是重繪機制,直接把棋盤畫在paint方法裡;二是我們先不對棋盤做滑鼠監聽,等到我們點選了動作按鈕後再去監聽獲取畫筆。
二、介面實現了,要考慮在監聽類中做事件處理。我把後續的程式碼都放在了這個類裡。
在這之前,為了後續用起來方便,寫一個介面,把要用的常量定義好,另外兩個類只要實現這個介面就可以了。
1.先實現黑白棋交替下;(黑白棋用不同的標誌位)
2.棋子必須要下載交叉點上;(計算座標)
3.同一個位置只能下一顆棋子;(判斷該座標點的標誌是否為0)
4.棋子不能消失;(重繪機制)
5.考慮判斷輸贏。(四個方向判斷是否連成5子,我用的方法很垃圾很冗餘)
至此的話,人人對戰是可以的了。
三、人機對戰
這裡就要用到五子棋AI演算法了,有三個方法:權值法、博弈樹、機器學習。
權值法:
1.準備工作
1.考慮誰先下棋?
AI先下,棋子隨機下在中間一定的範圍內
2.定義陣列儲存權值
3.考慮棋子相連的情況和對應的權值
活連
1連 10
2連 100
3連 1000
4連 10000
眠連
1連 1
2連 5
3連 50
4連 10000
4.使用Java的中一個Map集合來儲存棋子相連的情況和對應的權值
HashMap<String,Integer> map = new HashMap<String,Integer>();
map.put("010",10);//儲存活1連
map.put("020",10);//儲存活1連
map.put("021",1);//儲存眠1連
2.演算法的實現
1.人下棋後,判斷輸贏,如果贏了遊戲結束了,如果沒有贏就進入AI下棋;
2.根據棋子陣列統計當前棋盤上的最優位置,使用權值表示出來。
3.找出wieghtArray陣列中最大的權值,獲取最大權值所在的行數和列數。
4.根據行數和列數,讓AI在這個位置下棋
5.清空wieghtArray陣列中的資料。
6.AI的棋子判斷輸贏,如果贏了遊戲結束了,如果沒有贏就進入人下棋(回到1步);
博弈樹:
關於博弈基礎--極大極小搜尋我就不多說了,估計也說不清楚。請看:https://blog.csdn.net/u013351484/article/details/50789521
先講一下五子棋的基本實現過程:
一、介面實現
Gobang.java
show(){}
main(){}
paint(){}
視窗使用Border佈局,寫倆JPanel,一個畫棋盤,一個做動作按鈕。這裡要注意的有兩點:一是重繪機制,直接把棋盤畫在paint方法裡;二是我們先不對棋盤做滑鼠監聽,等到我們點選了動作按鈕後再去監聽獲取畫筆。
二、介面實現了,要考慮在監聽類中做事件處理。我把後續的程式碼都放在了這個類裡。
在這之前,為了後續用起來方便,寫一個介面,把要用的常量定義好,另外兩個類只要實現這個介面就可以了。
1.先實現黑白棋交替下;(黑白棋用不同的標誌位)
2.棋子必須要下載交叉點上;(計算座標)
3.同一個位置只能下一顆棋子;(判斷該座標點的標誌是否為0)
4.棋子不能消失;(重繪機制)
5.考慮判斷輸贏。(四個方向判斷是否連成5子,我用的方法很垃圾很冗餘)
至此的話,人人對戰是可以的了。
三、人機對戰
這裡就要用到五子棋AI演算法了,有三個方法:權值法、博弈樹、機器學習。
權值法:
1.準備工作
1.考慮誰先下棋?
AI先下,棋子隨機下在中間一定的範圍內
2.定義陣列儲存權值
3.考慮棋子相連的情況和對應的權值
活連
1連 10
2連 100
3連 1000
4連 10000
眠連
1連 1
2連 5
3連 50
4連 10000
4.使用Java的中一個Map集合來儲存棋子相連的情況和對應的權值
HashMap<String,Integer> map = new HashMap<String,Integer>();
map.put("010",10);//儲存活1連
map.put("020",10);//儲存活1連
map.put("021",1);//儲存眠1連
2.演算法的實現
1.人下棋後,判斷輸贏,如果贏了遊戲結束了,如果沒有贏就進入AI下棋;
2.根據棋子陣列統計當前棋盤上的最優位置,使用權值表示出來。
3.找出wieghtArray陣列中最大的權值,獲取最大權值所在的行數和列數。
4.根據行數和列數,讓AI在這個位置下棋
5.清空wieghtArray陣列中的資料。
6.AI的棋子判斷輸贏,如果贏了遊戲結束了,如果沒有贏就進入人下棋(回到1步);
博弈樹:
關於博弈基礎--極大極小搜尋我就不多說了,估計也說不清楚。請看:https://blog.csdn.net/u013351484/article/details/50789521
當輪到電腦下棋的時候,電腦會試著下子然後進行遞迴,直到得到葉子節點,判斷棋盤格局,若電腦返回分值大的,若人返回分值小的。這就構成了一棵博弈樹。這顆博弈樹有點龐大,並不能依靠暴力搜尋來尋找最佳解法。因此需要用到一些剪枝手段。常用的比較初級的有 alpha-beta 剪枝。這裡用的也是這個,具體過程還是去看連結裡的解釋吧,看完再看五子棋博弈的程式碼會比較清晰。要注意的是:將第0層電腦層放出來寫,下一個棋子我記錄一下位置(下完遞迴後要回溯),如果後面返回的情況比它好,我就更新它。最後我肯定會得到電腦選擇的分值高的那個座標落子便是。貼點程式碼如下:
//輪到電腦下棋,試著下子然後進行遞迴,直到得到葉子節點,判斷棋盤格局,若電腦返回分值大的,若人返回分值小的。這就構成了一棵博弈樹。 //由於效能有限,我們考慮到第四層。第0層電腦層我們把它放出來寫,因為是要記錄一下最好情況,方便以後落最好位置。 public Point turnComputer() { Point point = new Point(); int max = -INF; for (int i = 1; i < raw - 1; i++) { for (int j = 1; j < column - 1; j++) { if (array[i][j] == 0) {//判斷落子點周圍有沒有棋子,沒有則跳過 if (array[i-1][j+1]==0&&array[i][j + 1] == 0 &&array[i+1][j+1]==0&& array[i-1][j-1]==0&&array[i][j - 1] == 0 &&array[i+1][j-1]==0&& array[i + 1][j] == 0 && array[i - 1][j] == 0) { continue; } else {//有棋子則電腦下子進行判斷 array[i][j] = -1; int k = alphabeta(array, 1, -INF, INF, 1);//往下遞迴 array[i][j] = 0; //下完子要回溯 if (k > max) {//電腦下子當然要選擇大 max = k; point.x = i; point.y = j; } } } } } return point; } public int alphabeta(int array[][], int player, int alpha, int beta, int depth) { if (depth == 4)//效能有限,只往下四層 return getScore(); //葉子層返回棋盤格局的分值 //不是葉子層則繼續試著下子 for (int i = 1; i < raw - 1; i++) { for (int j = 1; j < column - 1; j++) { if (array[i][j] != 0) continue; if (array[i-1][j+1]==0&&array[i][j + 1] == 0 &&array[i+1][j+1]==0&& array[i-1][j-1]==0&&array[i][j - 1] == 0 &&array[i+1][j-1]==0&& array[i + 1][j] == 0 && array[i - 1][j] == 0) { continue; } array[i][j] = player; int v = alphabeta(array, player * -1, alpha, beta, depth + 1);//遞迴 array[i][j] = 0;//回溯 if (player == -1) alpha = max(alpha, v); else beta = min(beta, v); if (beta <= alpha) {//剪枝 這裡要畫樹進行分析,我也琢磨了好久 if (player == -1) return alpha; return beta; } } } if (player == -1) return alpha; return beta; }
電腦下棋啦~
// 電腦下棋的方法 通過AI演算法得出的最有利於電腦的落子點下棋
public void computerChess() {
Point point = turnComputer();
int i = point.x;
int j = point.y;
gr.fillOval(X + size * j - size / 2, Y + size * i - size / 2, size, size);
array[i][j] = -1;
player = 1;
icCopy = i;
jcCopy = j;
}
那麼葉子節點的棋盤格局分值怎麼去估計呢? 從最終棋盤格局的每行、每列、每正上斜、每正下斜、每反上斜、每反下斜分析電腦方和我方棋子的分佈問題,分別計算各方的棋子成型個數乘以相應的權重,最後整個棋盤格局得分是電腦方得分減去我方。
四、重新開始事件和悔棋事件
重新開始需要將棋子陣列所有棋子全部置0然後重繪,悔棋是將剛剛下的白子黑子置0然後重繪。