项目介绍

该游戏支持人人对战和人机对战两种模式,并具备开始 / 结束对局、悔棋、棋局回放、难度选择等功能。游戏界面简洁美观,AI 逻辑采用评分机制实现,能做出较为合理的落子决策。

项目整体架构

本次实现的五子棋系统主要包含以下几个核心类:

  • AI 类:负责 AI 决策逻辑,包括评分表设计、多方向棋子搜索和最佳落子点计算
  • Chess 类:处理棋子数据和胜负判断(横 / 竖 / 斜向连子检查)
  • GoServer 类:游戏核心控制,包括落子、悔棋等核心操作
  • GobangUI 类:游戏界面实现,支持人人对战 / 人机对战切换及难度选择
  • GoListener 类:事件监听,处理用户交互和 AI 落子触发

其中,AI 类是整个系统的核心,决定了 AI 的 "智能程度",下面重点解析其实现逻辑。

1. 评分表设计思路

评分表本质是 "棋子组合模式" 与 "分数" 的映射关系。我们为不同难度(简单 / 中等 / 困难)设计了独立的评分表,通过easyScoreMapmediumScoreMaphardScoreMap三个 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方法撤销上一步操作,恢复棋盘状态和落子颜色
  • 界面刷新:通过drawChessPaddrawallChess方法更新棋盘显示
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 通过评分表和多方向搜索实现了基本的智能决策,核心亮点包括:

  1. 基于模式匹配的评分机制,可灵活调整 AI 策略
  2. 8 方向全角度搜索,全面评估落子价值
  3. 多难度适配,满足不同水平玩家需求

后续将从以下方向优化:

  • 引入 Alpha-Beta 剪枝算法,提升搜索效率
  • 增加历史落子记录分析,优化长局决策
  • 细化评分表,增加更多复杂棋子组合模式的判断
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐