這一篇我們將解決人機對戰的問題。解決方案是“權值法”。
活連 眠連 (至於什麼叫活連,眠連自行百度吧)
1連 40 1連20
2連 400 2連 200
3連 3000 3連 500
4連 10000 4連 3000
關於權值的設置可以自己在合理的範圍內設置,所謂合理就是:不能讓AI已經到達四連或者的情況下還去堵人的二連甚至一連。
人棋子的權值偏高,AI棋子的權值偏低。AI偏防禦型
package GoBang;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
public class ChessAI extends MouseAdapter implements GoBangconfig {
public GoBang gb;
int maxrc[] = new int[2]; //定義數組儲存最大權值的行和列,具體見三
public ChessAI(GoBang gb) {
this.gb = gb;
}
static HashMap<String, Integer> map = new HashMap<String, Integer>();
static {
// 活連
map.put("010", 40); //黑棋爲1
map.put("0110", 400);
map.put("01110", 3000);
map.put("011110", 10000);
map.put("020", 20); //白棋爲2
map.put("0220", 200);
map.put("02220", 500);
map.put("022220", 3000);
// 眠連
map.put("012", 40);
map.put("0112", 400);
map.put("01112", 3000);
map.put("011112", 10000);
map.put("021", 40);
map.put("0221", 400);
map.put("02221", 3000);
map.put("022221", 10000);
}
private int[][] weightArray = new int[row][column]; //權值數組
// 根據棋子數組中棋子相連的情況來計算權值存入到數組中
public void WeightCount() {
//System.out.println("sssssssssss" + gb.isArrive.length);
for (int r = 0; r < gb.isArrive.length; r++) {
for (int c = 0; c < gb.isArrive[r].length; c++) {
// 判斷此位置有無棋子,如果已經有棋子了就沒必要下了
if (gb.isArrive[r][c] == 0) {
// 水平方向
String code = "0";// 用來記錄棋子相連情況
int chess = 0;// 記錄第一次出現的棋子
int number = 0;// 記錄空位出現的次數
// 水平方向
// 水平向左
for (int c1 = c - 1; c1 >= 0; c1--) {
if (gb.isArrive[r][c1] == 0) {
if (c == c1 + 1) { // 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦
break;
}
// 不是連續兩個空位的情況
else if (number == 0) {// 表示第一次出現空位
code = code + gb.isArrive[r][c1];// 記錄棋子相連的情況
number++;// 空位的次數加1
} else if (number == 1) {// 表示第二次出現空位
if (gb.isArrive[r][c1] == gb.isArrive[r][c1 + 1]) {
break; // 檢測是否連續兩個位置是否都爲空位
}
code = code + gb.isArrive[r][c1];// 記錄棋子相連的情況
number++;
} else if (number == 2) {// 表示第三次出現空位
if (gb.isArrive[r][c1] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位
break;
}
} else {
if (chess == 0) { // 表示第一次出現棋子
chess = gb.isArrive[r][c1];// 存儲第一次出現棋子
code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況
} else if (chess == gb.isArrive[r][c1]) {// 判斷是否和第一次出現的棋子同色
code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況
} else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄
code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況
break;
}
}
}
Integer value = map.get(code);
if (value != null)// 判斷value是否不爲null
weightArray[r][c] += value;// 在對應空位累加權值 }
public int[] findmax() { // 得到最大權值的行列,用數組來儲存
int max = 0, i = 0, j = 0;
for (i = 0; i < weightArray.length; i++) {
for (j = 0; j < weightArray[i].length; j++) {
//System.out.print(weightArray[i][j] + "\t");
if (weightArray[i][j] > max) {
max = weightArray[i][j];
maxrc[0] = i;
maxrc[1] = j;
}
}
//System.out.println();
}
//System.out.println(maxrc[0] + "<>" + maxrc[1]);
return maxrc; //因爲不能同時返回兩個值,所以用數組來存儲後返回數組即可
}
至此,AI算法的核心部分已經完成了。需要做的就是把它和之前的chessListener聯繫起來,讓監聽能同時分情況調用PERSONPLAY和AIPLAY(不知道你是否還有印象在前一篇文章裏這裏留了個坑,留在這節補上)。 public void AIPLAY(int x, int y) {
// 一開始人下
// 計算棋子要落的交叉點
int countx = (y - 20 + size / 2) / size;
int county = (x - 20 + size / 2) / size;
g = (Graphics2D) gf.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); // 抗鋸齒
int arrivex, arrivey;
// 棋盤上的具體位置
arrivex = 20 + county * size;
arrivey = 20 + countx * size;
if (countx >= 0 && countx <= 15 && county >= 0 && county <= 15
&& gf.isArrive[countx][county] != 0) // 有棋子
{
JOptionPane.showMessageDialog(gf, "此處已有棋子,請換一個地方");
} else {
// 人下了黑棋
g.setColor(Color.black);
g.fillOval(arrivex - size / 2, arrivey - size / 2, size, size);
gf.isArrive[countx][county] = 1;
list.add(new chess(countx, county)); // 有序地存儲每個棋子的行列,爲悔棋做準備
// 判斷輸贏
if (Gobangwin.judge(gf.isArrive, countx, county)) {
JOptionPane.showMessageDialog(gf, "黑棋勝利");
gf.removeMouseListener(this); box.setEnabled(true); //解封box
return;
}
ChessAI c = new ChessAI(gf);
// System.out.println(">>>"+gf.isArrive[0].length+"<<<");
// 輪到AI下
c.WeightCount();
int maxrc[] = c.findmax();
arrivex = 20 + maxrc[1] * size;
arrivey = 20 + maxrc[0] * size;
g.setColor(Color.white);
g.fillOval(arrivex - size / 2, arrivey - size / 2, size, size);
gf.isArrive[maxrc[0]][maxrc[1]] = 2;
list.add(new chess(maxrc[0], maxrc[1]));// 有序地存儲每個棋子的行列,原因同上
// 判斷輸贏
if (Gobangwin.judge(gf.isArrive, maxrc[0], maxrc[1])) {
JOptionPane.showMessageDialog(gf, "白棋勝利");
gf.removeMouseListener(this); box.setEnabled(true); //解封box
}
}
}
public void PERSONPLAY(int x, int y) {
// 人下的方法
// 計算棋子要落的交叉點
int countx = (y - 20 + size / 2) / size;
int county = (x - 20 + size / 2) / size;
g = (Graphics2D) gf.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); // 抗鋸齒
int arrivex, arrivey; // 棋盤上
arrivex = 20 + county * size;
arrivey = 20 + countx * size;
if (gf.isArrive[countx][county] != 0) // 有棋子
{
JOptionPane.showMessageDialog(gf, "此處已有棋子,請換一個地方");
} else {// 當前位置可以下棋
if (turn == 1) {
// 設置顏色
g.setColor(Color.black);
// 下棋
g.fillOval(arrivex - size / 2, arrivey - size / 2, size, size);
gf.isArrive[countx][county] = 1;
turn++;
} else {
// 設置顏色
g.setColor(Color.white);
// 下棋
g.fillOval(arrivex - size / 2, arrivey - size / 2, size, size);
gf.isArrive[countx][county] = 2;
turn--;
}
//
list.add(new chess(countx, county));// 有序地存儲每個棋子的行列
// 判斷輸贏
if (Gobangwin.judge(gf.isArrive, countx, county)) {
if (turn == 2)
JOptionPane.showMessageDialog(gf, "黑棋勝利");
else
JOptionPane.showMessageDialog(gf, "白棋勝利");
gf.removeMouseListener(this); box.setEnabled(true); //解封box
}
}
}
public void WeightCount() { //System.out.println("sssssssssss" + gb.isArrive.length); for (int r = 0; r < gb.isArrive.length; r++) { for (int c = 0; c < gb.isArrive[r].length; c++) { // 判斷此位置有無棋子 if (gb.isArrive[r][c] == 0) { // 水平方向 String code = "0";// 用來記錄棋子相連情況 int chess = 0;// 記錄第一次出現的棋子 int number = 0;// 記錄空位出現的次數 // 水平方向 // 水平向左 for (int c1 = c - 1; c1 >= 0; c1--) { if (gb.isArrive[r][c1] == 0) { if (c == c1 + 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r][c1];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (gb.isArrive[r][c1] == gb.isArrive[r][c1 + 1]) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r][c1];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r][c1] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r][c1];// 存儲第一次出現棋子 code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r][c1]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況 break; } } } Integer value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 // 水平向右 for (int c1 = c + 1; c1 < gb.isArrive[r].length; c1++) { if (gb.isArrive[r][c1] == 0) { if (c == c1 - 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r][c1];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (c1 + 1 < gb.isArrive[r].length && gb.isArrive[r][c1] == gb.isArrive[r][c1 + 1]) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r][c1];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r][c1] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r][c1];// 存儲第一次出現棋子 code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r][c1]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r][c1]; // 記錄棋子的相連情況 break; } } } value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 // 豎直方向 // 豎直向上檢查 for (int r1 = r - 1; r1 >= 0; r1--) { if (gb.isArrive[r1][c] == 0) { if (r == r1 + 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r1][c];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (gb.isArrive[r1][c] == gb.isArrive[r1 + 1][c]) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r1][c];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r1][c] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r1][c];// 存儲第一次出現棋子 code = code + gb.isArrive[r1][c]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r1][c]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r1][c]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r1][c]; // 記錄棋子的相連情況 break; } } } value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 // 豎直向下檢查 for (int r1 = r + 1; r1 < gb.isArrive.length; r1++) { if (gb.isArrive[r1][c] == 0) { if (r == r1 - 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r1][c];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (r1 + 1 < gb.isArrive.length && gb.isArrive[r1][c] == gb.isArrive[r1 + 1][c]) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r1][c];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r1][c] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r1][c];// 存儲第一次出現棋子 code = code + gb.isArrive[r1][c]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r1][c]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r1][c]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r1][c]; // 記錄棋子的相連情況 break; } } } value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 // 右斜方向 // 向左上 for (int r1 = r - 1, c1 = c - 1; r1 > 0 && c1 > 0; r1--, c1--) { if (gb.isArrive[r1][c] == 0) { if (r == r1 + 1 && c == c1 + 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (r1 + 1 < gb.isArrive.length && c1 + 1 < gb.isArrive[r].length && gb.isArrive[r1][c1] == gb.isArrive[r1 + 1][c1 + 1]) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r1][c1] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r1][c1];// 存儲第一次出現棋子 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r1][c1]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 break; } } } value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 // 向右下 for (int r1 = r + 1, c1 = c + 1; r1 < gb.isArrive.length && c1 < gb.isArrive[r].length; r1++, c1++) { if (gb.isArrive[r1][c1] == 0) { if (r == r1 - 1 && c == c1 - 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (r1 + 1 < gb.isArrive.length && c1 + 1 < gb.isArrive[r1].length && (gb.isArrive[r1][c1] == gb.isArrive[r1 + 1][c1 + 1])) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r1][c1] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r1][c1];// 存儲第一次出現棋子 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r1][c1]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 break; } } } value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 // 左斜方向 // 向右上 for (int r1 = r - 1, c1 = c + 1; r1 >= 0 && c1 < gb.isArrive[r].length; r1--, c1++) { if (gb.isArrive[r1][c1] == 0) { if (r == r1 + 1 && c == c1 - 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (c1 + 1 < gb.isArrive[r].length && r1 - 1 > 0 && gb.isArrive[r1][c1] == gb.isArrive[r1 - 1][c1 + 1]) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r1][c1] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r1][c1];// 存儲第一次出現棋子 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r1][c1]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 break; } } } value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 // 向左下 for (int r1 = r + 1, c1 = c - 1; r1 < gb.isArrive.length && c1 > 0; r1++, c1--) { if (gb.isArrive[r1][c1] == 0) { if (r == r1 - 1 && c == c1 + 1) {// 在c1+1處就空了,說明是連續兩個空位,就不用再討論啦 break; } // 不是連續兩個空位的情況 else if (number == 0) {// 表示第一次出現空位 code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++;// 空位的次數加1 } else if (number == 1) {// 表示第二次出現空位 if (r1 + 1 < gb.isArrive.length && c1 - 1 > 0 && gb.isArrive[r1][c1] == gb.isArrive[r1 + 1][c1 - 1]) { break; // 檢測兩個位置是否都爲空位 } code = code + gb.isArrive[r1][c1];// 記錄棋子相連的情況 number++; } else if (number == 2) {// 表示第三次出現空位 if (gb.isArrive[r1][c1] == gb.isArrive[r][c]) // 檢測兩個位置是否都爲空位 break; } } else { if (chess == 0) {// 表示第一次出現棋子 chess = gb.isArrive[r1][c1];// 存儲第一次出現棋子 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else if (chess == gb.isArrive[r1][c1]) {// 判斷是否和第一次出現的棋子同色 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 } else {// 表示此位置的棋子的顏色與第一次出現的顏色不同,所以記錄 code = code + gb.isArrive[r1][c1]; // 記錄棋子的相連情況 break; } } } value = map.get(code); if (value != null)// 判斷value是否不爲null weightArray[r][c] += value;// 在對應空位累加權值 } } } }