五子棋游戏开发2.0(AI实现)
本文介绍了一个五子棋游戏系统,支持人人对战和人机对战两种模式,具备开始/结束对局、悔棋、棋局回放等功能。系统采用评分机制实现AI决策,通过多方向搜索算法评估棋盘状态,并设计了简单、中等、困难三种难度级别。
项目介绍
该游戏支持人人对战和人机对战两种模式,并具备开始 / 结束对局、悔棋、棋局回放、难度选择等功能。游戏界面简洁美观,AI 逻辑采用评分机制实现,能做出较为合理的落子决策。
项目整体架构
本次实现的五子棋系统主要包含以下几个核心类:
- AI 类:负责 AI 决策逻辑,包括评分表设计、多方向棋子搜索和最佳落子点计算
- Chess 类:处理棋子数据和胜负判断(横 / 竖 / 斜向连子检查)
- GoServer 类:游戏核心控制,包括落子、悔棋等核心操作
- GobangUI 类:游戏界面实现,支持人人对战 / 人机对战切换及难度选择
- GoListener 类:事件监听,处理用户交互和 AI 落子触发
其中,AI 类是整个系统的核心,决定了 AI 的 "智能程度",下面重点解析其实现逻辑。
1. 评分表设计思路
评分表本质是 "棋子组合模式" 与 "分数" 的映射关系。我们为不同难度(简单 / 中等 / 困难)设计了独立的评分表,通过easyScoreMap、mediumScoreMap、hardScoreMap三个 HashMap 存储。
// 三种难度的评分映射
private HashMap<String, Integer> easyScoreMap = new HashMap<String, Integer>();
private HashMap<String, Integer> mediumScoreMap = new HashMap<String, Integer>();
private HashMap<String, Integer> hardScoreMap = new HashMap<String, Integer>();
// 当前使用的评分表
private HashMap<String, Integer> currentScoreMap;
// 难度等级:1-简单,2-中等,3-困难(默认中等)
private int difficulty = 2;
// 初始化三个难度的评分表
{
// 简单难度:防守为主,评分整体较低,对威胁反应迟钝
easyScoreMap.put("011110", 30000); // 活四
easyScoreMap.put("01111", 20000); // 冲四
easyScoreMap.put("11101", 15000); // 间隔冲四
easyScoreMap.put("01110", 8000); // 活三
easyScoreMap.put("0110110", 6000); // 双活三
easyScoreMap.put("11011", 5000); // 间隔三
easyScoreMap.put("01101", 7000); // 潜在冲四
easyScoreMap.put("011101", 6000); // 间断威胁
easyScoreMap.put("010", 200); // 活一
easyScoreMap.put("0110", 1000); // 活二
easyScoreMap.put("01", 100); // 眠一
easyScoreMap.put("011", 800); // 眠二
easyScoreMap.put("1101", 3000); // 边缘威胁
easyScoreMap.put("022220", 25000); // 白棋活四
easyScoreMap.put("02222", 18000); // 白棋冲四
easyScoreMap.put("22022", 13000); // 白棋间隔冲四
easyScoreMap.put("02220", 7000); // 白棋活三
easyScoreMap.put("0220220", 5000); // 白棋双活三
easyScoreMap.put("02202", 6000); // 白棋潜在冲四
easyScoreMap.put("022202", 5000); // 白棋高威胁
easyScoreMap.put("020", 100); // 白棋活一
easyScoreMap.put("0220", 500); // 白棋活二
easyScoreMap.put("02", 50); // 白棋眠一
easyScoreMap.put("022", 400); // 白棋眠二
easyScoreMap.put("2202", 1000); // 白棋边缘进攻
// 中等难度:攻防平衡,评分适中
mediumScoreMap.put("011110", 50000); // 活四
mediumScoreMap.put("01111", 40000); // 冲四
mediumScoreMap.put("11101", 35000); // 间隔冲四
mediumScoreMap.put("111011", 30000); // 复合冲四
mediumScoreMap.put("01110", 20000); // 活三
mediumScoreMap.put("0110110", 18000); // 双活三
mediumScoreMap.put("11011", 15000); // 间隔三
mediumScoreMap.put("01101", 20000); // 潜在冲四
mediumScoreMap.put("011101", 18000); // 间断威胁
mediumScoreMap.put("010", 500); // 活一
mediumScoreMap.put("0110", 5000); // 活二
mediumScoreMap.put("01", 2000); // 眠一
mediumScoreMap.put("011", 8000); // 眠二
mediumScoreMap.put("1101", 13000); // 边缘威胁
mediumScoreMap.put("022220", 45000); // 白棋活四
mediumScoreMap.put("02222", 38000); // 白棋冲四
mediumScoreMap.put("22022", 33000); // 白棋间隔冲四
mediumScoreMap.put("02220", 18000); // 白棋活三
mediumScoreMap.put("0220220", 16000); // 白棋双活三
mediumScoreMap.put("02202", 18000); // 白棋潜在冲四
mediumScoreMap.put("022202", 16000); // 白棋高威胁
mediumScoreMap.put("020", 400); // 白棋活一
mediumScoreMap.put("0220", 600); // 白棋活二
mediumScoreMap.put("02", 300); // 白棋眠一
mediumScoreMap.put("022", 900); // 白棋眠二
mediumScoreMap.put("2202", 2200); // 白棋边缘进攻
// 困难难度评分表 - 原始参数,敏锐识别各种威胁
hardScoreMap.put("011110", 50000); // 活四(致命威胁,最高优先级)
hardScoreMap.put("01111", 40000); // 冲四(极高威胁)
hardScoreMap.put("11101", 35000); // 间隔冲四
hardScoreMap.put("110111", 30000); // 复合冲四威胁
hardScoreMap.put("01110", 20000); // 活三(下一步可能成四)
hardScoreMap.put("0110110", 18000); // 双活三威胁
hardScoreMap.put("11011", 15000); // 间隔三
hardScoreMap.put("01101", 20000); // 1101模式
hardScoreMap.put("011101", 18000); // 11101模式
hardScoreMap.put("010", 500); // 活一
hardScoreMap.put("0110", 5000); // 活二
hardScoreMap.put("01", 2000); // 眠一
hardScoreMap.put("011", 8000); // 眠二
hardScoreMap.put("1101", 13000); // 无左侧空位的1101
hardScoreMap.put("022220", 45000); // 白棋活四
hardScoreMap.put("02222", 38000); // 白棋冲四
hardScoreMap.put("22022", 33000); // 白棋间隔冲四
hardScoreMap.put("02220", 18000); // 白棋活三
hardScoreMap.put("0220220", 16000); // 白棋双活三
hardScoreMap.put("02202", 18000); // 2202模式
hardScoreMap.put("022202", 16000); // 22202模式
hardScoreMap.put("020", 40); // 白棋活一
hardScoreMap.put("0220", 600); // 白棋活二
hardScoreMap.put("02", 30); // 白棋眠一
hardScoreMap.put("022", 900); // 白棋眠二
hardScoreMap.put("2202", 2200); // 白棋边缘进攻
hardScoreMap.put("22202", 25000); // 白棋边缘威胁
// 初始使用中等难度评分表
currentScoreMap = mediumScoreMap;
}
评分表设计遵循以下原则:
- 能直接获胜的模式(如活四、冲四)评分最高
- 进攻性模式(如活三、双活三)评分次之
- 低威胁模式(如活一、眠二)评分较低
- 对白棋和黑棋设置不同评分,体现攻防策略差异
2. 难度适配机制
通过setDifficulty方法切换不同评分表,实现 AI 难度调整:
public void setDifficulty(int difficulty) {
if (difficulty == 1) {
currentScoreMap = easyScoreMap;
} else if (difficulty == 2) {
currentScoreMap = mediumScoreMap;
} else if (difficulty == 3) {
currentScoreMap = hardScoreMap;
} else {
currentScoreMap = mediumScoreMap; // 默认中等
}
this.difficulty = difficulty;
}
多方向搜索算法
为了全面评估一个空位的价值,AI 需要检查该位置在 8 个方向(横、竖、45° 斜、135° 斜,每个方向分正反)上的棋子组合模式。
1. 搜索逻辑通用框架
2. 8 方向协同搜索
除向右搜索外,系统还实现了向左(toleft)、向上(toup)、向下(todown)、左上(toleftup)、右下(torightdown)、右上(torightup)、左下(toleftdown)七个方向的搜索方法。
每个方向的搜索都会生成对应的组合编码并计算分数,最终累加至scoreArr数组中。
其核心流程如下:
//向左查找
private 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 = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[r][i] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
if (codeStr.contains("1")) {
score = (int)(score * 1.2); // 黑棋威胁加成(强化防守)
} else if (codeStr.contains("2")) {
score = (int)(score * 0.9); // 白棋进攻弱化(避免过度进攻)
}
System.out.println("向左查找到:" + codeStr + " 分数:" + score);
String rightStr = toright(chessArr, r, c);
Integer rscore = currentScoreMap.get(rightStr);
if (rscore != null) {
if (rightStr.contains("1")) {
score += (int)(rscore * 1.2); // 黑棋横向威胁加成
} else {
score += (int)(rscore * 0.9); // 白棋横向进攻弱化
}
}
scoreArr[r][c] += score;
}
//向右查找
private String toright(int [][] chessArr,int r,int c) {
if (c == col) {
return "";
}
int cn1 = chessArr[r][c + 1];
if (cn1 == 0) {
return "";
}
String codeStr = "0" + cn1;
for (int i = c + 2; i <= col; i++) {
// 左边第二颗开始还有同色的棋子 累加字符串
if (chessArr[r][i] == cn1) {
codeStr = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[r][i] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
if (codeStr.contains("1")) {
score = (int)(score * 1.2);
} else if (codeStr.contains("2")) {
score = (int)(score * 0.9);
}
System.out.println("向右查找到:" + codeStr + "分数:" + score);
scoreArr[r][c] += score;
return codeStr;
}
//向上查找
private 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 = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[i][c] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
if (codeStr.contains("1")) {
score = (int)(score * 1.2);
} else if (codeStr.contains("2")) {
score = (int)(score * 0.9);
}
System.out.println("向上查找到:" + codeStr + " 分数:" + score);
String downStr = todown(chessArr, r, c);
Integer dscore = currentScoreMap.get(downStr);
if (dscore != null) {
if (downStr.contains("1")) {
score += (int) (dscore * 1.2);
} else {
score += (int) (dscore * 0.9);
}
}
scoreArr[r][c] += score;
}
//向下查找
private String todown(int [][] chessArr,int r,int c){
if (r==row ) {
return "";
}
int cn1 = chessArr[r+1][c];
if (cn1 == 0) {
return "";
}
String codeStr = "0" + cn1;
for (int i = r+2; i <= row; i++) {
// 下边第二颗开始还有同色的棋子 累加字符串
if (chessArr[i][c] == cn1) {
codeStr = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[i][c] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
if (codeStr.contains("1")) {
score = (int)(score * 1.2);
} else if (codeStr.contains("2")) {
score = (int)(score * 0.9);
}
System.out.println("向下查找到:" + codeStr + "分数:" + score);
scoreArr[r][c] += score;
return codeStr;
}
//向左上查找
private void toleftup(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;
for (int i = r - 2, j = c - 2; i >= 0 && j >= 0; i--, j--) {
// 左上边第二颗开始还有同色的棋子 累加字符串
if (chessArr[i][j] == cn1) {
codeStr = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[i][j] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
// 对角线威胁稍弱于直线
if (codeStr.contains("1")) {
score = (int)(score * 1.1);
} else if (codeStr.contains("2")) {
score = (int)(score * 0.85);
}
System.out.println("向左上查找到:" + codeStr + "分数:" + score);
String rightdownStr = torightdown(chessArr, r, c);
Integer rdscore = currentScoreMap.get(rightdownStr);
if (rdscore != null) {
if (rightdownStr.contains("1")) {
score += (int) (rdscore * 1.1);
} else {
score += (int) (rdscore * 0.85);
}
}
scoreArr[r][c] += score;
}
//向右下查找
private String torightdown(int [][] chessArr,int r,int c){
if (r==row || c==col ) {
return "";
}
int cn1 = chessArr[r+1][c+1];
if (cn1 == 0) {
return "";
}
String codeStr = "0" + cn1;
for (int i = r+2, j=c+2; i <=row&& j<=col; i++,j++) {
// 右下边第二颗开始还有同色的棋子 累加字符串
if (chessArr[i][j] == cn1) {
codeStr = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[i][j] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
// 对角线白子分值系数提高
if (codeStr.contains("1")) {
score = (int)(score * 1.1);
} else if (codeStr.contains("2")) {
score = (int)(score * 0.85);
}
System.out.println("向右下查找到:" + codeStr + "分数:" + score);
scoreArr[r][c] += score;
return codeStr;
}
//向左下查找
private void toleftdown(int[][] chessArr, int r, int c) {
if (r == row || c == 0) {
return;
}
int cn1 = chessArr[r + 1][c - 1];
if (cn1 == 0) {
return;
}
String codeStr = "0" + cn1;
for (int i = r + 2, j = c - 2; i <= row && j >= 0; i++, j--) {
// 左下边第二颗开始还有同色的棋子 累加字符串
if (chessArr[i][j] == cn1) {
codeStr = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[i][j] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
// 新增:白棋对角线加成,黑棋弱化
if (codeStr.contains("1")) {
score = (int)(score * 1.1);
} else if (codeStr.contains("2")) {
score = (int)(score * 0.85);
}
System.out.println("向左下查找到:" + codeStr + "分数:" + score);
String rightupStr = torightup(chessArr, r, c);
Integer ruscore = currentScoreMap.get(rightupStr);
if (ruscore != null) {
if (rightupStr.contains("1")) {
score += (int) (ruscore * 1.1);
} else {
score += (int) (ruscore * 0.85);
}
}
scoreArr[r][c] += score;
}
//向右上查找
private String torightup(int [][] chessArr,int r,int c){
if (r==0 || c==col ) {
return "";
}
int cn1 = chessArr[r-1][c+1];
if (cn1 == 0) {
return "";
}
String codeStr = "0" + cn1;
for (int i = r-2, j=c+2; i >=0 && j<= col; i--,j++) {
// 右上边第二颗开始还有同色的棋子 累加字符串
if (chessArr[i][j] == cn1) {
codeStr = codeStr + cn1;
} else {
//另一端遇到了不同色或者空位
if (chessArr[i][j] == 0) {
codeStr = codeStr + "0";
}
break;
}
}
int score = currentScoreMap.getOrDefault(codeStr, 0);
if (codeStr.contains("1")) {
score = (int)(score * 1.1);
} else if (codeStr.contains("2")) {
score = (int)(score * 0.85);
}
System.out.println("向右上查找到:" + codeStr + "分数:" + score);
scoreArr[r][c] += score;
return codeStr;
}
}
模拟棋盘测试
AI 类中最具特色的设计之一,是内置了模拟棋盘测试框架,通过预设棋局验证评分逻辑的准确性。在main方法中,我们可以看到这样的实现:
public static void main(String[] args) {
//模拟棋盘
int[][] arr = {
{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, 1, 0, 1, 1, 0, 0, 0},
{1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 0, 0, 0},
{0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 0, 1, 1, 0, 0, 0},
{0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0},
{0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 2, 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, 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}
};
System.out.println("当前棋盘:");
System.out.println(arr.length);
System.out.println(arr[0].length);
AI ai=new AI();
ai.getscoreArr(arr);
}
int row = 25;
int col = 28;
int [][]scoreArr;
public void getscoreArr(int[][] chessArr){
scoreArr=new int[row+1][col+1];
for (int i = 0; i <= row; i++) {
for (int j = 0; j <= col; j++) {
int cn=chessArr[i][j];
if (cn==0){
toleft(chessArr,i,j);
toright(chessArr,i,j);
toup(chessArr,i,j);
todown(chessArr,i,j);
toleftup(chessArr,i,j);
torightdown(chessArr,i,j);
torightup(chessArr,i,j);
toleftdown(chessArr,i,j);
}
}
}
//打印分数
for (int i = 0; i <=row ; i++) {
for (int j = 0; j <=col; j++) {
if (scoreArr[i][j]>0){
System.out.print(scoreArr[i][j]+" \t");
}else{
System.out.print(chessArr[i][j]+" \t");
}
}
System.out.println();
}
}
//查找分数最大的位置
public Point getAIPoint(int[][] chessArr) {
getscoreArr(chessArr);
int r = 0, c = 0;
int maxScore = 0;
for (int i = 0; i <= row; i++) {
for (int j = 0; j <= col; j++) {
int score = scoreArr[i][j];
if (score > maxScore) {
maxScore = score;
r = i;
c = j;
}
}
}
return new Point(r, c);
}
(编码规则:以 "0" 表示空位,"1" 表示黑棋,"2" 表示白棋,通过拼接形成组合模式(如 "011110" 表示两侧有空位的四个黑棋,即活四)。)
胜负判断与游戏控制
1. 连子判断实现
Chess 类负责胜负判断,通过检查横、竖、左斜、右斜四个方向的连子数量,当任一方向连子数≥5 时判定获胜:
public class Chess implements GoData{
int r;
int c;
int chessflag;//棋子颜色(1:黑,2:白)
int index;//落子序号
public Chess(int r,int c,int chessflag,int index){
this.r=r;
this.c=c;
this.chessflag=chessflag;
this.index=index;
}
//绘制棋子(渐变)
public void drawChess(Graphics g){
int cx = c * size + X - size / 2;
int cy = r * size + Y - size / 2;
if (chessflag == 1) {
for (int k = 0; k < size; k++) {
Color color = new Color(k * 3, k * 3, k * 3);
g.setColor(color);
g.fillOval(cx + k / 2, cy + k / 2, size - k, size - k);
}
} else {
for (int k = 0; k < size; k++) {
Color color = new Color(150 + k * 2, 150 + k * 2, 150 + k * 2);
g.setColor(color);
g.fillOval(cx + k / 2, cy + k / 2, size - k, size - k);
}
}
g.setColor(Color.ORANGE);
g.setFont(new Font("楷体",Font.BOLD,15));
g.drawString(index+"",cx+size/3,cy+2*size/3);//绘制序号
}
//胜负判断
public boolean judgewin(int[][] chessArr){
//横向判断
if (row(chessArr)>=5){
return true;
}else if (col(chessArr)>=5) {
return true;
}else if (left(chessArr)>=5) {
return true;
} else if (right(chessArr)>=5) {
return true;
}
return false;
}
//横向检查
public int row(int[][] chessArr){
int count=1;
//向左判断
for (int i = c-1; i >=0 ; i--) {
if(chessArr[r][i]==chessflag){
count++;
}else{
break;
}
}
//向右判断
for (int i = c+1; i <=col ; i++) {
if(chessArr[r][i]==chessflag){
count++;
}else{
break;
}
}
System.out.println("横向查找到:"+count);
return count;
}
//纵向检查
public int col(int[][] chessArr){
int count=1;
//向上判断
for (int i = r-1; i >=0 ; i--) {
if(chessArr[i][c]==chessflag){
count++;
}else{
break;
}
}
//向下判断
for (int i = r+1; i <=row ; i++) {
if(chessArr[i][c]==chessflag){
count++;
}else{
break;
}
}
System.out.println("纵向查找到:"+count);
return count;
}
//左斜检查
public int left(int[][] chessArr){
int count=1;
//左上判断
for (int i = r-1,j=c-1; i >=0 && j>=0; i--,j--) {
if(chessArr[i][j]==chessflag){
count++;
}else{
break;
}
}
//左下判断
for (int i = r+1,j=c+1; i <=row && j<=col ; i++,j++) {
if(chessArr[i][j]==chessflag){
count++;
}else{
break;
}
}
System.out.println("左斜查找到:"+count);
return count;
}
//右斜检查
public int right(int[][] chessArr){
int count=1;
//右上判断
for (int i = r-1,j=c+1; i >=0 && j<=col; i--,j++) {
if(chessArr[i][j]==chessflag){
count++;
}else{
break;
}
}
//右下判断
for (int i = r+1,j=c-1; i <=row && j>=0 ; i++,j--) {
if(chessArr[i][j]==chessflag){
count++;
}else{
break;
}
}
System.out.println("右斜查找到:"+count);
return count;
}
}
2. 核心游戏功能
GoServer 类实现了落子、悔棋等核心功能:
- 落子:
saveChess方法记录棋子位置和颜色,更新棋盘状态 - 悔棋:
rbChess方法撤销上一步操作,恢复棋盘状态和落子颜色 - 界面刷新:通过
drawChessPad和drawallChess方法更新棋盘显示
public class GoServer implements GoData {
int chessflag = 0;// 0:未开始 1:黑棋 2:白棋
int[][] chessArr = new int[row + 1][col + 1];// 棋盘状态数组
Chess[] chesslist = new Chess[(row + 1) * (col + 1)];// 棋子序列
int chessindex;//棋子数量
public boolean CheckChess(int x, int y) {
//校正坐标
int c = (x - X + size / 2) / size;
int r = (y - Y + size / 2) / size;
//游戏未开始检查
if (chessflag == 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;
}
//绘制棋盘
public void drawChessPad(Graphics g) {
//绘制背景
g.setColor(new Color(186, 140, 24));
//绘制网格
g.fillRect(X - size / 2, Y - size / 2, (col + 1) * size, (row + 1) * size);
g.setColor(Color.BLACK);
for (int i = 0; i <= 28; i++) {
g.drawLine(X, Y + i * size, X + col * size, Y + i * size);//横排
g.drawLine(X + i * size, Y, X + i * size, Y + row * size); //竖排
}
}
public void drawallChess(Graphics g) {
//for (int i = 0; i <row; i++) {
//for (int j = 0; j <col; j++) {
//int chessnum = chessArr[i][j];
// if (chessnum != 0) {
// drawChess(i, j, chessnum, g);
// }
//}
// }
for (int i = 0; i < chessindex; i++) {
Chess chess = chesslist[i];
chess.drawChess(g);
}
}
public void drawChess(int r, int c, int chess, Graphics g) {
int cx = c * size + X - size / 2;
int cy = r * size + Y - size / 2;
if (chess == 1) {
for (int k = 0; k < size; k++) {
Color color = new Color(k * 3, k * 3, k * 3);
g.setColor(color);
g.fillOval(cx + k / 2, cy + k / 2, size - k, size - k);
}
} else {
for (int k = 0; k < size; k++) {
Color color = new Color(150 + k * 2, 150 + k * 2, 150 + k * 2);
g.setColor(color);
g.fillOval(cx + k / 2, cy + k / 2, size - k, size - k);
}
}
}
//保存棋子
public Chess saveChess(int r, int c) {
chessArr[r][c] = chessflag;
Chess chess = new Chess(r, c, chessflag, chessindex);
chesslist[chessindex] = chess;
chessindex++;
return chess;
}
//悔棋功能
public void rbChess(Graphics g) {
if (chessindex == 0) {
JOptionPane.showMessageDialog(null,
"无法悔棋");
return;
}
Chess chess = chesslist[chessindex - 1];
chessArr[chess.r][chess.c] = 0;//撤销上一个棋子
chessindex--;
chessflag = chess.chessflag;//恢复悔棋后下棋颜色
//刷新界面
drawChessPad(g);
drawallChess(g);
}
//回放功能
public void rpChess(Graphics g) {
drawChessPad(g);
for (int i = 0; i < chessindex; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Chess chess = chesslist[i];
chess.drawChess(g);
}
}
public void playchess(int r, int c, Graphics g) {
Chess chess = saveChess(r, c);
chess.drawChess(g);
boolean judgewin = chess.judgewin(chessArr);
if (judgewin) {
String wintip;
if (chessflag == 1) {
wintip = "黑棋胜利";
} else {
wintip = "白棋胜利";
}
chessflag = 0;
JOptionPane.showMessageDialog(null,
wintip);
}
//交替落子
if (chessflag == 1) {
chessflag = 2;
} else if (chessflag == 2) {
chessflag = 1;
}
}
public void startgame(Graphics g) {
chessflag = 1;
chessindex = 0;//初始化棋子
chessArr = new int[row+1][col+1];//清空棋盘状态
drawChessPad(g);//绘制初始棋盘
}
public void endgame() {
chessflag = 0;//重置游戏状态
}
// 获取棋盘状态
public int[][] getChessArr() {
return chessArr;
}
}
界面交互设计
GobangUI 类实现了友好的用户界面,支持:
- 对战模式切换:人人对战 / 人机对战
- AI 难度选择:简单 / 中等 / 困难(仅人机模式可用)
- 核心操作按钮:开始对局、悔棋、回放对局

界面交互逻辑由 GoListener 类处理,当选择人机对战时,会触发aiMove方法让 AI 计算最佳落子点并执行落子
// AI落子方法
private void aiMove() {
// 获取当前棋盘状态
int[][] chessArr = gs.getChessArr();
// 获取AI最佳落子点
Point aiPoint = ai.getAIPoint(chessArr);
//设置当前难度
ai.setDifficulty(aiDifficulty);
if (aiPoint != null) {
int r = aiPoint.x;
int c = aiPoint.y;
// 确保该位置可以落子
if (chessArr[r][c] == 0 && gs.chessflag==2) {
gs.playchess(r, c, g);
}
}
}
总结与优化方向
本文实现的五子棋 AI 通过评分表和多方向搜索实现了基本的智能决策,核心亮点包括:
- 基于模式匹配的评分机制,可灵活调整 AI 策略
- 8 方向全角度搜索,全面评估落子价值
- 多难度适配,满足不同水平玩家需求
后续将从以下方向优化:
- 引入 Alpha-Beta 剪枝算法,提升搜索效率
- 增加历史落子记录分析,优化长局决策
- 细化评分表,增加更多复杂棋子组合模式的判断
更多推荐


所有评论(0)