增加人机对战功能,以及该功能与原程序融合。
一、创建人机AIChess类。
1、AI原理解释
使用0、1、2表示该位置不同的棋子状态,使用哈希表赋给不同的分值,用于后续计算AI下棋的位置。
2、功能实现
(1)首先创建一个AIChess类
存储AI功能的代码,同时初始化一个行列值。
package gobangv4;
import java.awt.*;
import java.util.Arrays;
import java.util.HashMap;
public class AIChess implements Data{
int r = 0;
int c = 0;
}
(2)创建一个哈希表存储不同情况的分值
//存储得分
int[][] codeArr = new int[16][16];
//定义不同情况下的分值
HashMap<String,Integer> map =new HashMap<>();
{
map.put("010", 10);
map.put("0110", 100);
map.put("01110", 1000);
map.put("011110", 5000);
map.put("020", 10);
map.put("0220", 100);
map.put("02220", 1000);
map.put("022220", 5000);
map.put("01", 5);
map.put("011", 50);
map.put("0111", 500);
map.put("01111", 5000);
map.put("02", 5);
map.put("022", 50);
map.put("0222", 500);
map.put("02222", 5000);
}
(3)获取每个无棋子的位置的分值
public void getCodeArr(int[][] chessArr){
//遍历现在的棋盘,得到每个位置的棋子状态
for (int i = 0; i < chessArr.length; i++) {
for (int j = 0; j < chessArr.length; j++) {
//首先判断是否为没有棋子的位置
int cnum = chessArr[i][j];
if (cnum == 0){
//分别向八个方向遍历
toLeft(chessArr,i,j);
toRight(chessArr,i,j);
toUp(chessArr,i,j);
toDown(chessArr,i,j);
toTopLeft(chessArr,i,j);
toTopRight(chessArr,i,j);
toBottomLeft(chessArr,i,j);
toBottomRight(chessArr,i,j);
}
}
}
}
//toLeft向左遍历
public void toLeft(int[][] chessArr,int r,int c){
if (c == 0) {
//处于棋盘最左侧,无需向左遍历
return;
}
int cn1 = chessArr[r][c-1];
if (cn1 == 0) {
//左侧第一位没有棋子
return;
}
String codeStr = "0"+cn1;
for (int i = c-2; i >= 0 ; i--) {
if (chessArr[r][i] == cn1) {
//左侧为相同颜色棋子
codeStr += cn1;
}else {
if (chessArr[r][i] == 0){
//左侧一位没有棋子
codeStr += "0";
}
break;
}
}
//根据哈希表获得该位置的分数
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
//toright向右遍历
public void toRight(int[][] chessArr,int r,int c){
if (c == chessArr.length-1){
//处于棋盘最右侧,无需向右遍历
return;
}
int cn1 = chessArr[r][c+1];
if (cn1 == 0) {
//右侧第一位没有棋子
return;
}
String codeStr = "0" + cn1;
for (int i = c+2; i < chessArr.length ; i++) {
if (chessArr[r][i] == cn1) {
//右侧为相同颜色棋子
codeStr += cn1;
}else {
if (chessArr[r][i] == 0){
//右侧一位没有棋子
codeStr += "0";
}
break;
}
}
//根据哈希表获得该位置的分数
System.out.println(codeStr);
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
//toup向上遍历
public void toUp(int[][] chessArr,int r,int c){
if (r == 0){
//处于棋盘最上侧,无需向上遍历
return;
}
int cn1 = chessArr[r-1][c];
if (cn1 == 0){
//上侧第一位没有棋子
return;
}
String codeStr = "0" + cn1;
for (int i = r-2; i >= 0 ; i--) {
if (chessArr[i][c] == cn1){
//上侧为相同颜色棋子
codeStr += cn1;
}else {
if (chessArr[i][c] == 0){
//上侧一位没有棋子
codeStr += "0";
}
break;
}
}
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
//todown向下遍历
public void toDown(int[][] chessArr,int r,int c){
if (r == chessArr.length-1){
//处于棋盘最下侧,无需向下遍历
return;
}
int cn1 = chessArr[r+1][c];
if (cn1 == 0){
//下侧第一位没有棋子
return;
}
String codeStr = "0" + cn1;
for (int i = r+2; i < chessArr.length ; i++) {
if (chessArr[i][c] == cn1){
//下侧为相同颜色棋子
codeStr += cn1;
}else {
if (chessArr[i][c] == 0){
//下侧一位没有棋子
codeStr += "0";
}
break;
}
}
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
//totopLeft左上侧遍历
public void toTopLeft(int[][] chessArr,int r,int c){
if (r == 0 || c == 0){
//棋子在最左侧、最上侧和左上角顶点
return;
}
int cn1 = chessArr[r-1][c-1];
if (cn1 == 0){
//左上侧一位无棋子
return;
}
String codeStr = "0" + cn1;
int i = r-2;
int j = c-2;
while(i >= 0 && j >= 0){
if (chessArr[i][j] == cn1){
//左上侧为相同颜色棋子
codeStr += cn1;
i--;
j--;
}else {
if (chessArr[i][j] == 0){
//左上侧一位没有棋子
codeStr += "0";
}
break;
}
}
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
//totopRight右上侧遍历
public void toTopRight(int[][] chessArr,int r,int c){
if (r == 0 || c == chessArr.length-1){
//棋子在最右侧、最上侧和右上角顶点
return;
}
int cn1 = chessArr[r-1][c+1];
if (cn1 == 0){
//右上侧一位无棋子
return;
}
String codeStr = "0" + cn1;
int i = r-2;
int j = c+2;
while(i >= 0 && j < 16){
if (chessArr[i][j] == cn1){
//右上侧为相同颜色棋子
codeStr += cn1;
i--;
j++;
}else {
if (chessArr[i][j] == 0){
//右上侧一位没有棋子
codeStr += "0";
}
break;
}
}
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
//toBottomLeft左下侧遍历
public void toBottomLeft(int[][] chessArr,int r,int c){
if (r == chessArr.length-1 || c == 0){
//棋子在最左侧、最下侧和左下角顶点
return;
}
int cn1 = chessArr[r+1][c-1];
if (cn1 == 0){
//左下侧一位无棋子
return;
}
String codeStr = "0" + cn1;
int i = r+2;
int j = c-2;
while(i < 16 && j >= 0){
if (chessArr[i][j] == cn1){
//左下侧为相同颜色棋子
codeStr += cn1;
i++;
j--;
}else {
if (chessArr[i][j] == 0){
//左下侧一位没有棋子
codeStr += "0";
}
break;
}
}
System.out.println(codeStr);
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
//toBottomRight右下侧遍历
public void toBottomRight(int[][] chessArr,int r,int c){
if (r == chessArr.length-1 || c == chessArr.length-1){
//棋子在最右侧、最下侧、右下角顶点
return;
}
int cn1 = chessArr[r+1][c+1];
if (cn1 == 0){
//右下侧一位无棋子
return;
}
String codeStr = "0" + cn1;
int i = r+2;
int j = c+2;
while(i < 16 && j < 16){
if (chessArr[i][j] == cn1){
//右下侧为相同颜色棋子
codeStr += cn1;
i++;
j++;
}else {
if (chessArr[i][j] == 0){
//右下侧一位没有棋子
codeStr += "0";
}
break;
}
}
int code = map.get(codeStr);
System.out.println(codeStr + ":" + code);
codeArr[r][c] += code;
}
(4)创建主函数进行测试
public static void main(String[] args) {
GameListener gameListener;
//模拟测试棋盘
int[][] chessArr = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
};
AIChess aiChess = new AIChess();
aiChess.getCodeArr(chessArr);
}
3、AI功能与源代码适配
(1)源代码优化
1>创建一个ChessUtils类,将人机对战、人人对战、悔棋以及新增的一些优化功能加入其中
public class ChessUtils implements Data {
int chessStatus = 0;
Graphics g;
int[][] chessArr = new int[ROW + 1][COL + 1];
ToWin toWin = new ToWin();
int chessSize = 0;
Chess[] chessList = new Chess[256];
String gameModel = "人人对战";
int agame = 0;
}
2>添加前置条件的检查
public boolean check(int x, int y) {
// 计算行列值
int r = (y - Y + SIZE / 2) / SIZE;
int c = (x - X + SIZE / 2) / SIZE;
if (chessStatus == 0) {
JOptionPane.showMessageDialog(null,
"请先开始新的对局~");
return false;
}
if (r > ROW || c > COL || x < X - SIZE / 2 || y < Y - SIZE / 2) {
JOptionPane.showMessageDialog(null,
"请在棋盘内落子~");
return false;
}
if (chessArr[r][c] != 0) {
JOptionPane.showMessageDialog(null, "此处已有棋子~");
return false;
}
return true;
}
3>人人对战
// 人人对战
public void pvpPlay(int x, int y) {
// 计算行列值
int r = (y - Y + SIZE / 2) / SIZE;
int c = (x - X + SIZE / 2) / SIZE;
if (!check(x, y)) {
return;
}
chessArr[r][c] = chessStatus;
Chess chess = new Chess(chessStatus,r,c);
chessList[chessSize] = chess;
chessSize++;
// 判断落子颜色
if (chessStatus == 1) {
g.setColor(Color.BLACK);
chessStatus = 2;
} else if (chessStatus == 2) {
g.setColor(Color.WHITE);
chessStatus = 1;
}
// 根据行列值还原标准坐标
int chessX = c * SIZE + X - SIZE / 2;
int chessY = r * SIZE + Y - SIZE / 2;
// 绘制棋子
g.fillOval(chessX, chessY, SIZE, SIZE);
if (toWin.isWin(chessArr,r, c)) {
JOptionPane.showMessageDialog(null, chessStatus== 1 ? "白棋胜利" : "黑棋胜利");
//将棋子状态归为0,使得后续不能再下棋。
chessStatus = 0;
}
}
4>人机对战
// 人机对战
public void pvAPlay(int x, int y) {
//人落子
if (!check(x, y)) {
return;
}
// 计算行列值
int r = (y - Y + SIZE / 2) / SIZE;
int c = (x - X + SIZE / 2) / SIZE;
chessArr[r][c] = 1;
Chess chess = new Chess(1, r, c);
chessList[chessSize] = chess;
// 根据行列值还原标准坐标
int chessX = c * SIZE + X - SIZE / 2;
int chessY = r * SIZE + Y - SIZE / 2;
// 绘制棋子
g.setColor(Color.BLACK);
g.fillOval(chessX, chessY, SIZE, SIZE);
chessSize++;
//判断输赢
if (toWin.isWin(chessArr, r, c)) {
JOptionPane.showMessageDialog(null, "玩家胜利");
//将棋子状态归为0,使得后续不能再下棋。
chessStatus = 0;
}
//AI落子
AIChess aiChess = new AIChess();
aiChess.playAI(chessArr, g);
int ar = aiChess.r;
int ac = aiChess.c;
chessArr[ar][ac] = 2;
Chess achess = new Chess(2, ar, ac);
chessList[chessSize] = achess;
chessSize++;
if (toWin.isWin(chessArr, ar, ac)) {
JOptionPane.showMessageDialog(null, "AI胜利");
//将棋子状态归为0,使得后续不能再下棋。
chessStatus = 0;
}
}
5>悔棋功能
public void goBack() {
if (chessSize <= 0) {
return;
}
int count = 1;
if (gameModel.equals("人机对战")) {
count = 2;
}
for (int i = 0; i < count; i++) {
//获取最后一个棋子
Chess chess = chessList[chessSize - 1];
//将悔棋去除的棋子位置设置为无棋子
chessArr[chess.r][chess.c] = 0;
//表明这个位置现在没有棋子对象
chessList[chessSize - 1] = null;
//确保下次落子棋子的颜色正确
chessStatus = chess.chessflag;
//去除悔棋的棋子的数量
chessSize--;
//重绘棋盘
drawChessPad();
//按照悔棋后的棋子位置画出棋子
drawChess();
}
}
6>赋予开局和结束对局条件
public void initGame() {
chessStatus = 1;
agame = 1;
chessArr = new int[ROW + 1][COL + 1];
}
public void end() {
chessStatus = 0;
agame = 0;
}
7>人人对战和人机对战的切换
GameLietener中mousePressed部分优化为以下代码:
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
//得到点击坐标
int x = e.getX();
int y = e.getY();
chessUtils.play(x,y);
}
play部分代码如下:
public void play(int x, int y) {
if (gameModel.equals("人人对战")) {
pvpPlay(x, y);
} else if (gameModel.equals("人机对战")) {
pvAPlay(x, y);
}
}
8>控制对局期间无法切换人人对战和人机对战功能
GameLietener中actionPerformed部分改动为:
else if (ac.equals("人机对战")) {
//获取按下的按钮,将其文本内容改为“结束对局”,背景色改为粉色
JButton btn = (JButton) e.getSource();
btn.setText("人人对战");
btn.setBackground(Color.pink);
// 将棋盘中每个位置的状态归为0
// chessUtils.chessArr = new int[16][16];
chessUtils.setGameModel(ac);
}else if (ac.equals("人人对战")) {
//获取按下的按钮,将其文本内容改为“开始游戏”,背景色改为天蓝色
JButton btn = (JButton) e.getSource();
btn.setText("人机对战");
btn.setBackground(Color.cyan);
chessUtils.setGameModel(ac);
}
ChessUtils部分为:
public void setGameModel(String gameModel) {
if (agame == 0) {
this.gameModel = gameModel;
}
}