小项目 五子棋游戏

2021-12-21
11 min read
Featured Image

项目特点

  1. 两种游戏模式: 人机对战和玩家对战.
  2. 可以多步悔棋.
  3. 项目未使用图片, 图形都是绘制而成.

项目结构以及源码

  • src
    • ButtonAction 类
    • Config 接口
    • Core 类
    • GameMouse 类
    • GameUI 类
    • MyFrame 类

ButtonAction 类

此类继承于 ActionListener 接口, 主要用于传递按钮点击事件的函数.

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ButtonAction implements ActionListener {
    public Graphics gr;
    public MyFrame mf;
    public JButton redoButton;
    public JButton restartButton;
    public JRadioButton aiButton;
    public JRadioButton playerButton;
    //利用构造器把画布中的按钮和画笔全部传进来
    public ButtonAction(Graphics gr, MyFrame mf, JButton redoButton, JButton restartButton, JRadioButton aiButton, JRadioButton playerButton) {
        this.gr = gr;
        this.mf = mf;
        this.redoButton = redoButton;
        this.restartButton = restartButton;
        this.aiButton = aiButton;
        this.playerButton = playerButton;
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.getSource());
        Object target = e.getSource();
        if(aiButton.isSelected()) {//选择 AI 对战模式
            Core.mode = 0;
        }
        else if(playerButton.isSelected()) {//选择玩家对战模式
            Core.mode = 1;
        }
        if(target == aiButton || target == playerButton) { //如果点击了模式选择的选择按钮
            for (int i = 0; i <15 ; i++) {
                for (int j = 0; j < 15; j++) {
                    Core.map[i][j] = 0;//一旦重新选择模式, 棋盘将清空
                }
            }
            Core.flag = 1;//棋子颜色变为黑色
            Core.res = 0;//对战结果为 无结果
            mf.paint(gr);
            System.out.println("MODE: " + Core.mode);
        }

        if(target == redoButton) { //如果点击会千牛
            if(!Core.chessStack.empty() && Core.mode == 1) { //玩家对战模式,只需悔棋到上一步
                int y = Core.chessStack.pop();//弹栈
                int x = Core.chessStack.pop();
                Core.map[x][y] = 0;
                Core.flag = - Core.flag;
                Core.res = 0;
                mf.paint(gr);
            }
            if(!Core.chessStack.empty() && Core.mode == 0) {//人机对战模式需要悔棋两步
                int y = Core.chessStack.pop();//弹栈第一次
                int x = Core.chessStack.pop();
                Core.map[x][y] = 0;
                Core.flag = - Core.flag;
                y = Core.chessStack.pop();//弹栈第二次
                x = Core.chessStack.pop();
                Core.map[x][y] = 0;
                Core.flag = - Core.flag;
                Core.res = 0;
                mf.paint(gr);
            }
        }
        else if(target == restartButton) { //重新开始按钮, 棋盘清零, 结果重置
            for (int i = 0; i <15 ; i++) {
                for (int j = 0; j < 15; j++) {
                    Core.map[i][j] = 0;
                }
            }
            Core.flag = 1;
            Core.res = 0;
            mf.paint(gr);
        }
    }
}

GameMouse 类

此类继承于MouseListener接口, 主要负责定义鼠标点击时候的函数. 包括获取坐标, 绘制棋子,下棋等操作.

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;

public class GameMouse implements MouseListener, Config {
    public Graphics gr;
    public MyFrame mf;
    public int chessX, chessY;
    int res = 0;
    private HashMap<String, Integer> Map = new HashMap<>();//用于存储ai算法的权值
    private HashMap<String, Integer> aiValues = Core.generateAIMap(Map);//生成权值

    public GameMouse(Graphics gr) {
        this.gr = gr;

    }

    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {
        // 获取坐标
        int x = e.getX();
        int y = e.getY();
        int xTemp, yTemp;
        System.out.println(x + " " + y);
        gr.setColor(Color.black);
        //计算交点值
        if ((x - X0) % SIZE > SIZE / 2) {
            xTemp = (x - X0) / SIZE + 1;
        } else {
            xTemp = (x - X0) / SIZE;
        }
        if ((y - Y0) % SIZE > SIZE / 2) {
            yTemp = (y - Y0) / SIZE + 1;
        } else {
            yTemp = (y - Y0) / SIZE;
        }
        // 只有棋子坐标在特定范围内的时候, 再赋值, 否则给个赋值, 下面的代码也不用再执行
        if (xTemp >= 0 && xTemp <= 14) {
            chessX = xTemp;
        } else {
            chessX = -1;
        }
        if (yTemp >= 0 && yTemp <= 14) {
            chessY = yTemp;
        } else {
            chessY = -1;
        }
        // 只有没有结果的时候才下棋, 如果有结果, 不下棋,也不重绘棋盘
        if (Core.res == 0 && chessX >= 0 && chessX <= 15 && chessY >= 0 && chessY <= 15) {
            if (Core.mode == 0) {
                //调用下棋函数
                Core.aiPlaceChess(mf, gr, Core.map, chessX, chessY, aiValues);
                //传当前方法内的横纵坐标到 mf 对象内, 用于悔棋

                System.out.println("当前结果: " + Core.res);

            }
            if (Core.mode == 1) {
                //调用下棋函数
                Core.placeChess(mf, gr, Core.map, chessX, chessY);
                //传当前方法内的横纵坐标到 mf 对象内, 用于悔棋
                Core.chessStack.push(chessX);
                Core.chessStack.push(chessY);
                res = Core.isWin(Core.map, chessX, chessY);
                System.out.println("当前结果: " + Core.res);

                if (res == 4) {
                    JOptionPane.showMessageDialog(null, "黑色胜利.");
                    Core.res = res;
                }
                if (res == -4) {
                    JOptionPane.showMessageDialog(null, "白色胜利.");
                    Core.res = res;
                }
            }

        }
    }


    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}

MyFrame 类

继承了Jframe类, 主要用于提供画笔, 重绘棋盘等功能.

import javax.swing.*;
import java.awt.*;

public class MyFrame extends JFrame implements Config {
    //重写 paint 方法, 用于改变视窗大小和重新开始等情况的重绘棋盘
    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(Color.black);
        for (int i = 0; i < LINE; i++) {
            g.setColor(Color.black);
            g.drawLine(X0, Y0 + i * SIZE, (LINE - 1) * SIZE + X0, Y0 + i * SIZE);
            g.drawLine(X0 + i * SIZE, Y0, X0 + i * SIZE, (LINE - 1) * SIZE + Y0);
        }
        g.drawRect(48,78,704,704);
        g.fillOval(200 - 5,230 - 5,10,10);
        g.fillOval(600 - 5,630 - 5,10,10);
        g.fillOval(600 - 5,230 - 5,10,10);
        g.fillOval(200 - 5,630 - 5,10,10);
        g.fillOval(400 - 5,430 - 5,10,10);
        for (int i = 0; i < LINE; i++) {
            for (int j = 0; j < LINE; j++) {
//                System.out.print(map[j][i] + " ");
                if (Core.map[i][j] == 1) {
                    for (int k = 0; k < 50; k++) {
                        Color c = new Color(4*k,4*k,4*k);
                        g.setColor(c);
                        g.fillOval(X0 + i * SIZE - 25 + k/3, Y0 + j * SIZE - 25 + k/3, 50 - k, 50 - k);
                    }

                } else if (Core.map[i][j] == -1) {
                    for (int k = 0; k < 50; k++) {
                        Color c = new Color(k+200,k+200,k+200);
                        g.setColor(c);
                        g.fillOval(X0 + i * SIZE - 25 + k/3, Y0 + j * SIZE - 25 + k/3, 50 - k, 50 - k);
                    }
                }
            }
//            System.out.print("\n");

        }

    }
}

Core 类

此类主要提供一些列静态算法, 为游戏的核心逻辑类.

import javax.swing.*;
import java.awt.*;
import java.util.HashMap;
import java.util.Stack;

public class Core implements Config {
    static int[][] map = new int[16][16]; // 棋盘二维数组
    static Stack<Integer> chessStack = new Stack<>();//用于悔棋的堆栈
    static int mode = 0; //人机或者是玩家对战模式
    static int flag = 1; // 下一步颜色
    static int res = 0; //最后结果
    static int max = -100; //通过权值表算出的最大值
    static int maxI, maxJ; //最大权值的棋盘位置,用于白棋落子
    //
    //哈希表的内容不能直接在这里添加, 要在函数体内
    private HashMap<String, Integer> Map = new HashMap<>();

    /**
     *  将全指标输入空的哈希表
     * @param aiMap 初始的空哈希表
     * @return
     */
    public static HashMap generateAIMap(HashMap<String, Integer> aiMap) {
        aiMap.put("0000", 10);
        aiMap.put("B", 7);
        aiMap.put("BB", 800);
        aiMap.put("BBB", 15000);
        aiMap.put("BBBB", 800000);
        aiMap.put("W", 15);
        aiMap.put("WW", 400);
        aiMap.put("WWW", 1800);
        aiMap.put("WWWW", 100000);
        return aiMap;
    }

    //region 权值 复杂
//    public static HashMap generateAIMap1(HashMap<String, Integer> aiMap) {
//        aiMap.put("0000", 10);
//        aiMap.put("B0WW", 7);
//        aiMap.put("0B0W", 8);
//        aiMap.put("B0W0", 9);
//        aiMap.put("B00W", 30);
//        aiMap.put("00B0", 40);
//        aiMap.put("0B00", 70);
//        aiMap.put("000B", 100);
//        aiMap.put("00BB", 200);
//        aiMap.put("BB0W", 500);
//        aiMap.put("0BB0", 800);
//        aiMap.put("BB00", 900);
//        aiMap.put("B0BB", 1000);
//        aiMap.put("BBBW", 1100);
//        aiMap.put("BB0B", 1500);
//        aiMap.put("BBB0", 100000);
//        aiMap.put("BBBB", 800000);
//        aiMap.put("W0BB", 15);
//        aiMap.put("0W0B", 16);
//        aiMap.put("0W0B", 16);
//        aiMap.put("WW", 400);
//        aiMap.put("WWW", 1800);
//        aiMap.put("WWWW", 100000);
//        return aiMap;
//    }
    //endregion

    /**
     * 与 AI 对战的下棋模式
     * @param mf 画布
     * @param gr 画笔
     * @param map 棋局
     * @param chessX AI 下棋前的黑棋横坐标
     * @param chessY AI 下棋前的黑棋纵坐标
     * @param aiValues 权值表
     */
    public static void aiPlaceChess(MyFrame mf, Graphics gr, int[][] map, int chessX, int chessY, HashMap<String, Integer> aiValues) {
        int res = 0;
        if (Core.flag == 1) {
            gr.setColor(Color.black);
            //判断落子位置是否有棋子
            if (map[chessX][chessY] == 0) {
                map[chessX][chessY] = 1;
                Core.chessStack.push(chessX); //用于悔棋
                Core.chessStack.push(chessY);
                for (int k = 0; k < 50; k++) {
                    Color c = new Color(4 * k, 4 * k, 4 * k);
                    gr.setColor(c);
                    gr.fillOval(X0 + chessX * SIZE - 25 + k / 3, Y0 + chessY * SIZE - 25 + k / 3, 50 - k, 50 - k);
                }
                System.out.println("下黑棋");
                System.out.println("棋子坐标为:" + chessX + "," + chessY + " 棋子颜色为: " + Core.flag);
                Core.flag = -1;
                aiCheckMax(aiValues, map);
                res = isWin(map, chessX, chessY);
                if (res >= 4) {
                    JOptionPane.showMessageDialog(null, "宝宝真厉害,奖励宝宝一个亲亲.");
                    Core.res = res;
                }
            }
            if (Core.res == 0) {
                gr.setColor(Color.white);
                chessX = maxI;
                chessY = maxJ;
                Core.chessStack.push(chessX);
                Core.chessStack.push(chessY);
                map[chessX][chessY] = -1;
                for (int k = 0; k < 50; k++) {
                    Color c = new Color(k + 200, k + 200, k + 200);
                    gr.setColor(c);
                    gr.fillOval(X0 + chessX * SIZE - 25 + k / 3, Y0 + chessY * SIZE - 25 + k / 3, 50 - k, 50 - k);
                }
                System.out.println("下白棋");
                System.out.println("棋子坐标为:" + maxI + "," + maxJ + " 棋子颜色为: " + Core.flag);
                Core.flag = 1;
                res = isWin(map, chessX, chessY);

                if (res <= -4) {
                    JOptionPane.showMessageDialog(null, "西白,几把人机竟然赢了,下一把给它锤爆嗷.");
                    Core.res = res;
                }
            }
        }

    }

    /**
     * 查找出权值最大的位置, 并将最大值坐标存储到 Core 类的静态变量中
     *
     * @param aiValues 棋子对应的权值表
     * @param map      当前棋盘
     */
    public static void aiCheckMax(HashMap<String, Integer> aiValues, int[][] map) {
        int[][] score = new int[16][16];
        max = -100;
        StringBuilder sbHorizon = new StringBuilder(5);
        // 先试一下一个点的算法
        for (int i = 0; i < LINE; i++) {
            for (int j = 0; j < LINE; j++) {
                //如果位置没有棋子, 再去做下面四个循环
                if (map[i][j] == 0) {
                    int[] scoreArray = new int[4]; //存储每个空位周围 4 个方向的权值

                    //水平方向的分数
                    //  双循环原因: 第一层来看控制五元组的位置, 第二层变量五元组中的每个元素
                    for (int z = -4; z < 1; z++) {
                        for (int k = z; k < 5 + z; k++) {
                            if (i + k < 15 && i + k >= 0) {
                                if (map[i + k][j] == 1) sbHorizon.append("B");
                                if (map[i + k][j] == -1) sbHorizon.append("W");
                            }
                        }
                        String horString = sbHorizon.toString();
                        scoreArray[0] = scoreArray[0] + checkScore(horString);
                        sbHorizon.delete(0, 5);
                    }
                    //垂直方向
                    for (int z = -4; z < 1; z++) {
                        for (int k = z; k < 5 + z; k++) {
                            if (j + k < 15 && j + k >= 0) {
                                if (map[i][j + k] == 1) sbHorizon.append("B");
                                if (map[i][j + k] == -1) sbHorizon.append("W");
                            }
                        }
                        String horString = sbHorizon.toString();
                        scoreArray[1] = scoreArray[1] + checkScore(horString);
                        sbHorizon.delete(0, 5);
                    }
                    // " \ " 方向
                    for (int z = -4; z < 1; z++) {
                        for (int k = z; k < 5 + z; k++) {
                            if (j + k < 15 && j + k >= 0 && i + k < 15 && i + k >= 0) {
                                if (map[i + k][j + k] == 1) sbHorizon.append("B");
                                if (map[i + k][j + k] == -1) sbHorizon.append("W");
                            }
                        }
                        String horString = sbHorizon.toString();
                        scoreArray[2] = scoreArray[2] + checkScore(horString);
                        sbHorizon.delete(0, 5);
                    }
                    // " / " 方向
                    for (int z = -4; z < 1; z++) {
                        for (int k = z; k < 5 + z; k++) {
                            if (j - k < 15 && j - k >= 0 && i + k < 15 && i + k >= 0) {
                                if (map[i + k][j - k] == 1) sbHorizon.append("B");
                                if (map[i + k][j - k] == -1) sbHorizon.append("W");
                            }
                        }
                        String horString = sbHorizon.toString();
                        scoreArray[3] = scoreArray[3] + checkScore(horString);
                        sbHorizon.delete(0, 5);
                    }
                    score[i][j] = scoreArray[0] + scoreArray[1] + scoreArray[2] + scoreArray[3];
                    if (score[i][j] > max) {
                        max = score[i][j];
                        maxI = i;
                        maxJ = j;
                    }
                }

            }
        }
        System.out.println("最大值坐标: x: " + maxI + " y: " + maxJ);

    }

    /**
     * 通过对照全指标, 得出五元组的分数
     *
     * @param s 五元组中的棋子分布情况
     * @return
     */
    public static int checkScore(String s) {
        int score = 0;
        if (s.contains("B") && s.contains("W")) score = -1; // 尝试解决这个问题 1, 如果最后扫描到白子, 应该结果分数小一点
        else if (s.contains("B") && !s.contains("W")) {
            if (s.contains("BBBB")) score = 800000;
            else if (s.contains("BBB")) score = 15000;
            else if (s.contains("BB")) score = 800;
            else if (s.contains("B")) score = 7;
        } else if (!s.contains("B") && s.contains("W")) {
            if (s.contains("WWWW")) score = 100000;
            else if (s.contains("WWW")) score = 1800;
            else if (s.contains("WW")) score = 400;
            else if (s.contains("W")) score = 15;
        } else score = 0;
        return score;

    }

    /**
     * 判断输赢
     *
     * @param map    棋盘的二维数组 (注意此矩阵直观上看实际是棋盘的90度旋转)
     * @param chessX 当前下的棋子横坐标
     * @param chessY 当前下的棋子纵坐标
     * @return 4 为黑赢 -4 为白赢
     */
    public static int isWin(int[][] map, int chessX, int chessY) {
        int num = 0;
        // 设置一个最大的结果值, 最后返回
        // 原因是下面的四个循环因为存在先后顺序, 可能最后小的 num 覆盖掉之前大的 num 导致返回的 num 不准确. 该赢的赢不了.
        int maxNum = 0;
        // 横赢算法
        if ((map[chessX + 1][chessY] == map[chessX][chessY] && chessX + 1 <= LINE) || (chessX > 1 && map[chessX - 1][chessY] == map[chessX][chessY])) {
            num = 0;
            for (int i = -4; i < 5; i++) {
                if (chessX + i >= 0 && chessX + i + 1 <= LINE) {
                    if (map[chessX + i][chessY] == map[chessX + i + 1][chessY] && map[chessX + i][chessY] != 0) {
                        num = num + map[chessX + i][chessY];
                        System.out.println("横赢num" + num);
                        if(Math.abs(num) >= maxNum) maxNum =num;
                    }
                }
            }
        }
        // 竖赢算法
        if ((map[chessX][chessY + 1] == map[chessX][chessY] && chessY + 1 <= LINE) || (chessY > 1 && map[chessX][chessY - 1] == map[chessX][chessY])) {
            num = 0;
            for (int i = -4; i < 5; i++) {
                if (chessY + i >= 0 && chessY + i + 1 <= LINE) {
                    if (map[chessX][chessY + i] == map[chessX][chessY + i + 1] && map[chessX][chessY + i] != 0) {
                        num = num + map[chessX][chessY + i];
                        System.out.println("竖赢num" + num);
                        if(Math.abs(num) >= maxNum) maxNum =num;
                    }
                }
            }
        }
        // 右斜下赢
        // 先判断斜下是否有两个相连的情况, 并且横坐标等于 0 的时候 不做 x-1 的判断, 横坐标等于 15 的时候 不做 +1 判断.
        if ((chessX > 1 && chessY > 1 && map[chessX][chessY] == map[chessX - 1][chessY - 1]) || (chessY < 15 && chessX < 15 && map[chessX][chessY] == map[chessX + 1][chessY + 1])) {
            num = 0;
            for (int i = -4; i < 5; i++) {
                if (chessX + i >= 0 && chessX + i <= LINE - 1 && chessY + i >= 0 && chessY + i <= LINE - 1) {
                    if (map[chessX + i][chessY + i] == map[chessX + i + 1][chessY + i + 1] && map[chessX + i][chessY + i] != 0) {
                        num = num + map[chessX + i][chessY + i];
                        System.out.println("斜下num" + num);
                        if(Math.abs(num) >= maxNum) maxNum =num;
                    }
                }
            }
        }
        //左斜下赢
        if ((chessX < 15 && chessY > 1 && map[chessX][chessY] == map[chessX + 1][chessY - 1]) || (chessY < 15 && chessX > 1 && map[chessX][chessY] == map[chessX - 1][chessY + 1])) {
            num = 0;
            for (int i = -4; i < 5; i++) {
                if (chessX - i >= 1 && chessX - i <= LINE - 1 && chessY + i >= 0 && chessY + i <= LINE - 1) {
                    if (map[chessX - i][chessY + i] == map[chessX - i - 1][chessY + i + 1] && map[chessX - i][chessY + i] != 0) {
                        num = num + map[chessX - i][chessY + i];
                        System.out.println("左斜下num" + num);
                        if(Math.abs(num) >= maxNum) maxNum =num;
                    }

                }

            }
        }

        return maxNum;
    }

    /**
     * 下棋子
     *
     * @param mf     棋子所在画布
     * @param gr     画布的画笔
     * @param map    存储棋盘的二位数字
     * @param chessX 当前棋子横坐标
     * @param chessY 当前其子纵坐标
     */
    public static void placeChess(MyFrame mf, Graphics gr, int[][] map, int chessX, int chessY) {
        if (Core.flag == 1) {
            gr.setColor(Color.black);
            //判断落子位置是否有棋子
            if (map[chessX][chessY] != 1 && map[chessX][chessY] != -1) {
                map[chessX][chessY] = 1;
                for (int k = 0; k < 50; k++) {
                    Color c = new Color(4 * k, 4 * k, 4 * k);
                    gr.setColor(c);
                    gr.fillOval(X0 + chessX * SIZE - 25 + k / 3, Y0 + chessY * SIZE - 25 + k / 3, 50 - k, 50 - k);
                }
                System.out.println("下黑棋");
                System.out.println("棋子坐标为:" + chessX + "," + chessY + " 棋子颜色为: " + Core.flag);
                Core.flag = -1;
            }
        } else if (Core.flag == -1) {
            gr.setColor(Color.white);
            if (map[chessX][chessY] != 1 && map[chessX][chessY] != -1) {
                map[chessX][chessY] = -1;
                for (int k = 0; k < 50; k++) {
                    Color c = new Color(k + 200, k + 200, k + 200);
                    gr.setColor(c);
                    gr.fillOval(X0 + chessX * SIZE - 25 + k / 3, Y0 + chessY * SIZE - 25 + k / 3, 50 - k, 50 - k);
                }
                System.out.println("下白棋");
                System.out.println("棋子坐标为:" + chessX + "," + chessY + " 棋子颜色为: " + Core.flag);
                Core.flag = 1;
            }
        }
    }


}

Config 接口

提供棋盘的一些基本数据, 例如行数列数,左上角的起始坐标.

public interface Config {
    public static final int X0 = 50;
    public static final int Y0 = 80;
    public static final int LINE = 15;
    public static final int SIZE = 50;

}

GameUI 类

初始化 UI 的类, 定义了 UI 中所需要的画布, 按钮等可视元素. 并将事件监听器添加到这些可视元素上.

import javax.swing.*;
import java.awt.*;

public class GameUI {
    public static void main(String[] args) {
        GameUI ui = new GameUI();
        ui.initUI();
    }

    public void initUI() {
        //需要调用有重写paint方法的JFrame子类
        MyFrame jf = new MyFrame();
        jf.setSize(1000, 830);
        jf.setTitle("五子棋游戏");
        jf.setLocationRelativeTo(null);
        JButton restartButton = new JButton("重新开始");
        JButton redoButton = new JButton("悔棋");
        JRadioButton aiButton = new JRadioButton("人机对战",true);
        JRadioButton playerButton = new JRadioButton("玩家对战");
        ButtonGroup modeButtonGroup = new ButtonGroup();
        modeButtonGroup.add(aiButton);
        modeButtonGroup.add(playerButton);
        Box b1 = Box.createHorizontalBox();
        Box b2 = Box.createVerticalBox();
        jf.add(b1);
        b1.add(Box.createRigidArea(new Dimension(800,50)));
        // b1 内嵌套一个 b2
        b1.add(b2);
        b2.add(restartButton);
        b2.add(redoButton);
        b2.add(aiButton);
        b2.add(playerButton);
        // 把两个按钮撑到上面
        b2.add(Box.createRigidArea(new Dimension(0,600)));
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jf.setVisible(true);
        // 获取画笔对象
        Graphics g = jf.getGraphics();
        GameMouse mouse = new GameMouse(g);
        jf.addMouseListener(mouse);
        ButtonAction buttonAction = new ButtonAction(g, jf, redoButton, restartButton,aiButton,playerButton);
        restartButton.addActionListener(buttonAction);
        redoButton.addActionListener(buttonAction);
        aiButton.addActionListener(buttonAction);
        playerButton.addActionListener(buttonAction);
    }
}



遇到的一些细节上的问题

g.drawLine() 方法无法执行的原因

  • 画不出来的原因是 paint 方法在创建 JFrame 实例的时候自动执行, 和 drawLine 方法是并发的, 画线的线程可能会在 paint 方法之前就执行了.
  • 此时, 通过手动将 drawLine 方法延迟执行, 就可以避免前后颠倒的情况发生
  • 另外, 通过重写 paint 方法, 也可以达到同样的目的
try {
    Thread.sleep(3000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
g.drawLine(20,20,50,50);

单独通过单击改变棋子颜色

  • 未使用数组的情况下
if (flag == 1) {
    gr.setColor(Color.black);
    gr.fillOval(X0 + chessX * SIZE - 25, Y0 + chessY * SIZE - 25, 50, 50);
    System.out.println("下黑棋");
    flag = 2;
}
else if (flag == 2) {
    gr.setColor(Color.white);
    gr.fillOval(X0 + chessX * SIZE - 25, Y0 + chessY * SIZE - 25, 50, 50);
    System.out.println("下白棋");
    flag = 1;
}
System.out.println("棋子坐标为:" + chessX + "," + chessY + " 棋子颜色为: " + flag);
Avatar
Aaron Fan My research interests include machine learning, signal processing, web development and robotics.