用Java写一个简易五子棋游戏

        今天我们来用Java写一个简易的五子棋游戏。

1.UI设计

        首先,我们需要一个显示游戏的UI界面。创建GameUI类,给界面设置合适的标题、大小等属性。设置窗体可见并添加画笔。如此便搭好了UI的整体框架。

public void initUI() {
        Mframe jf = new Mframe();//此处Mframe为继承Jframe的子定义子类,后文会详细解释。
        jf.setSize(1000, 1000);
        jf.setTitle("五子棋游戏");
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(3);
        
        jf.setVisible(true);

        Graphics g = jf.getGraphics();
}

        接下来选取合适的间距画线(注意适当留白),生成一个五子棋棋盘。为了方便之后的功能添加,我们可在空白处设置按钮面板并添加相应的按钮。这里添加了三个功能按钮,分别为“开始”“悔棋”“复盘”。

//设置面板
        JPanel esatPanel = new JPanel();
        esatPanel.setPreferredSize(new Dimension(80, 0));
        jf.add(esatPanel, BorderLayout.EAST);
//添加功能按钮和动作监听器(后文详解)
        GameListener listener = new GameListener();
        String[] name = {"开始", "悔棋", "复盘"};
        for (int i = 0; i < name.length; i++) {
            JButton jbu = new JButton(name[i]);
            esatPanel.add(jbu);
            jbu.addActionListener(listener);
        }

//棋盘绘制
for (int i = 0; i < 9; i++) {
            g.drawLine(100, 100 + i * 100, 900, 100 + i * 100);
            g.drawLine(100 + i * 100, 100, 100 + i * 100, 900);
        }

        添加主函数,创建GameUI的对象并调用initUI方法,便能看到简易的五子棋界面。

2.棋子绘制

        有棋盘之后,我们需要实现下棋功能,即棋子绘制。棋子绘制本质便是在鼠标点击后在相应位置画实心圆和空心圆(画实心圆的方法为fillOval,画空心圆的方法为drawOval,它们都需要四个参数,x y对应外接矩形的左上角坐标,width和height对应外接矩形宽高,设置宽高相等即可得到圆)。因此,我们首先要给窗体加上鼠标监听器来识别点击的位置。但是棋子的中心应该正好落于棋盘线的交点处,所以我们需要对获取的点击坐标进行一定数学运算处理,并设置一个标志位flag来判断应该下黑棋还是白棋。

public void mouseClicked(MouseEvent e) {
//定义x y存储点击的坐标值
        int x = e.getX();
        int y = e.getY();

//对x y进行处理使坐标能落在棋盘线交点
        if (x % 100 <= 50) {
            x1 = x - x % 100;
        }
        else {
            x1 = x + 100 - x % 100;
        }
        if (y % 100 <= 50) {
            y1 = y - y % 100;
        }
        else {
            y1 = y + 100 - y % 100;
        }
//边缘情况处理        
        if (x > 900) x1 = 900;
        if (x < 100) x1 = 100;
        if (y > 900) y1 = 900;
        if (y < 100) y1 = 100;
 
//提前创建整形二维数组Chess[][]用于记录棋子位置,黑棋置一,白棋置二
        if (Chess[y1 / 100 - 1][x1 / 100 - 1] != 0) {
            System.out.println("该位置已有棋子!");
            return;
        }
        if (flag % 2 == 0) {
            g.fillOval(x1 - 25, y1 - 25, 50, 50);
            Chess[y1 / 100 - 1][x1 / 100 - 1] = 1;
        } else {
            g.drawOval(x1 - 25, y1 - 25, 50, 50);
            Chess[y1 / 100 - 1][x1 / 100 - 1] = 2;
        }

3.输赢判断

        实现下棋功能后,我们来进一步实现输赢判断。当有一方在横竖左右任一方向下出五个连续棋子时,此人获胜。在代码层面,若是每次都遍历二维数组判断连续棋子数则过于麻烦,所以我们选择计算最后落下的棋子各方向的连续同色棋子数。计算时需要注意坐标和对应数组位置的转换和数组越界问题。此处以计算横向棋子数的方法Iswin1为例。

 public int Iswin1(int x1, int y1) {
        int c = 1;//用c记录棋子数量,初始值为1,即棋子本身
        int a = Chess[y1 / 100 - 1][x1 / 100 - 1];//进行坐标转换并用a的值代表黑白
        for (int i = y1 / 100 - 2; i >= 0 && i >= y1 / 100 - 5; i--) {

            if (Chess[i][x1 / 100 - 1] == a) {
                c++;

            } else break;
        }

        for (int i = y1 / 100; i <= 8 && i <= y1 / 100 + 4; i++) {
            if (Chess[i][x1 / 100 - 1] == a) {
                c++;
            } else break;
        }

        return c;//返回棋子数量
    }

        当Iswin1到Iswin4任一值为5时,即可通过flag的奇偶判断胜负。此时我们定义变量flag1,当它由0变为1时表示胜负已分,游戏结束,再点击棋盘将显示“游戏已结束!”。

4.图形重绘

        在实现游戏基本功能之后,我们应添加图形重绘功能,使窗体状态改变后仍能保留棋盘棋子。定义Shape类以保存棋子数据和画棋方法。

public class Shape {
    public int x1,y1,flag;
//保存棋子数据    
    public Shape(int x1,int y1,int flag){
        this.x1 = x1;
        this.y1 = y1;
        this.flag = flag;
    }
//画棋方法
    public void drawShape(Graphics g){
        if (flag % 2 == 0) {
            g.fillOval(x1 - 25, y1 - 25, 50, 50);

        } else {
            g.drawOval(x1 - 25, y1 - 25, 50, 50);
        }
    }
}

​

        为了实现重绘,我们需用MPanel类去继承JPanel类,重写paint方法,在保留父类功能的基础上加入画棋子棋盘的功能。

public class Mframe extends JFrame {
    public Shape[] shapeArr;

    public void paint(Graphics g) {
        super.paint(g);//保留父类功能
//画棋盘
        for (int i = 0; i < 9; i++) {
            g.drawLine(100, 100 + i * 100, 900, 100 + i * 100);
            g.drawLine(100 + i * 100, 100, 100 + i * 100, 900);
        }
//画棋子
        for (int i = 0; i < shapeArr.length; i++) {
            Shape shape = shapeArr[i];
            if (shape == null) break;
            shape.drawShape(g);
        }
    }
}

        接着我们在Gamelistener类中创建Shape类的数组shapeArr存储棋子数据,并把数据传递给GameUI类。窗体状态改变时即会自动执行重绘功能。

5.功能添加

        在此基础上,我还添加了“开始”“悔棋”“复盘”三种功能。

1.开始

        点击“开始”之后,才可以开始下棋,而且此按钮带有清空棋盘的功能,即重新开始游戏。我们设置boolean型变量start,初始值为false,这时点击棋盘将显示“请点击开始!”。点击“开始”后,start置为true,数组Chess和shapeArr以及标志位全部置零,方可开始游戏。

2.悔棋

        “悔棋”的功能是撤销上一个棋子,并可以做到连续撤销。此过程需要将Chess和shapeArr相应位置的数据清空并让索引下标减一,之后进行一次图形重绘,就能达到清除上一步棋子的效果。

3.复盘

        “复盘”的功能是从头再现下棋的过程。实现复盘的第一步是清空棋盘。我们可以令设置shapeArr1数组保存和shapeArr相同的图形数据。在复盘时,我们先将shapeArr清空执行重绘,再遍历shapeArr1一个个画出棋子。为了防止画棋子的速度过快,我们需要设计合理的睡眠时间使下两颗棋有一定的间隔。

public void actionPerformed(ActionEvent e) {
        String str = e.getActionCommand();
        switch (str) {
            case "开始":
                flag1 = 0;
                start = true;
                for (int i = 0; i < 9; i++) {
                    for (int j = 0; j < 9; j++) {
                        Chess[i][j] = 0;
                    }
                }
                for (int i = 0; i < flag; i++) {
                    shapeArr[i] = null;
                }
                flag = 0;
                mf.paint(g);
                break;
            case "悔棋":
                if (flag >= 1) {
                    shapeArr[flag - 1] = null;
                    Chess[Y[--Index] / 100 - 1][X[--Index] / 100 - 1] = 0;
                    index1--;
                    flag--;
                }
                mf.paint(g);
                break;
            case "复盘":
                for (int i = 0; i < shapeArr.length; i++) {
                    shapeArr[i] = null;
                }
                mf.paint(g);
                for (int i = 0; i < shapeArr0.length; i++) {
                    Shape shape = shapeArr0[i];
                    if (shape == null) break;
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                    shape.drawShape(g);
                }
                break;
        }

    }

  6.GameUI与GameListener完整代码

          以下是经过功能添加与细化后的GameUI类与GameListener类的完整代码。

public class GameUI {
    public void initUI() {
        Mframe jf = new Mframe();
        jf.setSize(1000, 1000);
        jf.setTitle("五子棋游戏");
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(3);

        JPanel esatPanel = new JPanel();
        esatPanel.setPreferredSize(new Dimension(80, 0));
        jf.add(esatPanel, BorderLayout.EAST);

        GameListener listener = new GameListener();
        String[] name = {"开始", "悔棋", "复盘"};
        for (int i = 0; i < name.length; i++) {
            JButton jbu = new JButton(name[i]);
            esatPanel.add(jbu);
            jbu.addActionListener(listener);
        }

        jf.setVisible(true);
        Graphics g = jf.getGraphics();


        jf.addMouseListener(listener);
        listener.setG(g);
        jf.shapeArr = listener.shapeArr;
        listener.mf = jf;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        for (int i = 0; i < 9; i++) {
            g.drawLine(100, 100 + i * 100, 900, 100 + i * 100);
            g.drawLine(100 + i * 100, 100, 100 + i * 100, 900);
        }

    }

    public static void main(String[] args) {
        GameUI ui = new GameUI();
        ui.initUI();
    }
}
public class GameListener extends MouseAdapter implements ActionListener {
    private Graphics g;
    public int[][] Chess = new int[9][9];
    public int flag = 0;
    int flag1 = 0;
    int index1 = 0;
    int index2 = 0;
    public Shape[] shapeArr = new Shape[100];
    public Shape[] shapeArr0 = new Shape[100];
    public boolean start = false;
    public Mframe mf;
    int x1, y1;
    public int[] X = new int[81];
    public int[] Y = new int[81];
    public int Index = 0;

    public void setG(Graphics g) {
        this.g = g;
    }


    public void actionPerformed(ActionEvent e) {
        String str = e.getActionCommand();
        switch (str) {
            case "开始":
                flag1 = 0;
                start = true;
                for (int i = 0; i < 9; i++) {
                    for (int j = 0; j < 9; j++) {
                        Chess[i][j] = 0;
                    }
                }
                for (int i = 0; i < flag; i++) {
                    shapeArr[i] = null;
                }
                flag = 0;
                mf.paint(g);
                break;
            case "悔棋":
                if (flag >= 1) {
                    shapeArr[flag - 1] = null;
                    Chess[Y[--Index] / 100 - 1][X[--Index] / 100 - 1] = 0;
                    index1--;
                    flag--;
                }
                mf.paint(g);
                break;
            case "复盘":
                for (int i = 0; i < shapeArr.length; i++) {
                    shapeArr[i] = null;
                }
                mf.paint(g);
                for (int i = 0; i < shapeArr0.length; i++) {
                    Shape shape = shapeArr0[i];
                    if (shape == null) break;
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                    shape.drawShape(g);
                }
                break;
        }

    }

    public void mouseClicked(MouseEvent e) {
        if (flag1 == 1) {
            System.out.println("游戏已结束!");
            return;
        }
        if (!start) {
            System.out.println("请点击开始!");
            return;
        }
        int x = e.getX();
        int y = e.getY();

        if (x % 100 <= 50) {
            x1 = x - x % 100;
        } else {
            x1 = x + 100 - x % 100;
        }
        if (y % 100 <= 50) {
            y1 = y - y % 100;
        } else {
            y1 = y + 100 - y % 100;
        }
        if (x > 900) x1 = 900;
        if (x < 100) x1 = 100;
        if (y > 900) y1 = 900;
        if (y < 100) y1 = 100;
        X[Index++] = x1;
        Y[Index++] = y1;
        if (Chess[y1 / 100 - 1][x1 / 100 - 1] != 0) {
            System.out.println("该位置已有棋子!");
            return;
        }
        if (flag % 2 == 0) {
            g.fillOval(x1 - 25, y1 - 25, 50, 50);
            Chess[y1 / 100 - 1][x1 / 100 - 1] = 1;
        } else {
            g.drawOval(x1 - 25, y1 - 25, 50, 50);
            Chess[y1 / 100 - 1][x1 / 100 - 1] = 2;
        }
        if (Iswin1(x1, y1) == 5 || Iswin2(x1, y1) == 5 || Iswin3(x1, y1) == 5 || Iswin4(x1, y1) == 5) {
            if (flag % 2 == 0) System.out.println("黑棋赢");
            else System.out.println("白棋赢");
            flag1 = 1;
        }
        Shape shape = new Shape(x1, y1, flag);
        flag++;
        shapeArr[index1++] = shape;
        shapeArr0[index2++] = shape;
    }

    public int Iswin1(int x1, int y1) {
        int c = 1;
        int a = Chess[y1 / 100 - 1][x1 / 100 - 1];
        for (int i = y1 / 100 - 2; i >= 0 && i >= y1 / 100 - 5; i--) {

            if (Chess[i][x1 / 100 - 1] == a) {
                c++;

            } else break;
        }
        for (int i = y1 / 100; i <= 8 && i <= y1 / 100 + 4; i++) {
            if (Chess[i][x1 / 100 - 1] == a) {
                c++;
            } else break;
        }

        return c;
    }

    public int Iswin2(int x1, int y1) {
        int c = 1;
        int a = Chess[y1 / 100 - 1][x1 / 100 - 1];
        for (int i = x1 / 100 - 2; i >= 0 && i >= x1 / 100 - 5; i--) {

            if (Chess[y1 / 100 - 1][i] == a) {
                c++;

            } else break;
        }
        for (int i = x1 / 100; i <= 8 && i <= x1 / 100 + 4; i++) {
            if (Chess[y1 / 100 - 1][i] == a) {
                c++;
            } else break;
        }
        return c;
    }

    public int Iswin3(int x1, int y1) {
        int c = 1;
        int a = Chess[y1 / 100 - 1][x1 / 100 - 1];
        for (int i = 1; i <= 4 && y1 / 100 - 1 - i >= 0 && x1 / 100 - 1 - i >= 0; i++) {

            if (Chess[y1 / 100 - 1 - i][x1 / 100 - 1 - i] == a) {
                c++;

            } else break;
        }
        for (int i = 1; i <= 4 && y1 / 100 - 1 + i <= 8 && x1 / 100 - 1 + i <= 8; i++) {
            if (Chess[y1 / 100 - 1 + i][x1 / 100 - 1 + i] == a) {
                c++;
            } else break;
        }
        return c;
    }

    public int Iswin4(int x1, int y1) {
        int c = 1;
        int a = Chess[y1 / 100 - 1][x1 / 100 - 1];
        for (int i = 1; i <= 4 && y1 / 100 - 1 - i >= 0 && x1 / 100 - 1 + i <= 8; i++) {

            if (Chess[y1 / 100 - 1 - i][x1 / 100 - 1 + i] == a) {
                c++;

            } else break;
        }
        for (int i = 1; i <= 4 && y1 / 100 - 1 + i <= 8 && x1 / 100 - 1 - i >= 0; i++) {
            if (Chess[y1 / 100 - 1 + i][x1 / 100 - 1 - i] == a) {
                c++;
            } else break;
        }
        return c;
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值