还在动手画棋盘 ?20分钟带你用Java写一个井字棋!

前言

上课无聊的时候,肯定会有很多人拿出一张白纸,老师在上面讲的津津有味,我和同桌已经开始下起了井字棋,老师课也讲完了,我也画满了一张白纸。无聊想要玩井字棋,不仅浪费纸张,还要自己动手画棋。,今天小编带着你,用Java编写一个井字棋小游戏。
在这里插入图片描述

设计过程

1.创建窗体类MyGameWindow

创建MyGameWindow类使其继承JFrame类成为窗体类并实现ActionListener接口,并实现ActionListener接口中的actionPerformed(ActionEvent e)方法。

public class MyGameWindow extends JFrame implements ActionListener {
    
    @Override
    public void actionPerformed(ActionEvent e) {
        
    }
}

2.创建窗体的构造器

创建窗体的构造器,进行一些基础的设置,并在主方法中进行实例化。

public class MyGameWindow extends JFrame implements ActionListener {

    public MyGameWindow(){
    	//设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {

    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

此时,我们已经造出了一个空的窗口。
在这里插入图片描述

3.创建棋盘

创建一个三行三列的按钮数组表示棋盘,并将按钮数组实例化添加到窗体中,并为各按钮设置事件监听器。

public class MyGameWindow extends JFrame implements ActionListener {

    //以3行3列数组的形式创建表盘
    private JButton[][] boards = new JButton[3][3];
    
    public MyGameWindow(){
    	//设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //将按钮数组添加到窗体中去并为按钮添加事件监听方法
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                //实例化按钮
                boards[i][j] = new JButton();
                //将按钮添加到窗体中去
                add(boards[i][j]);
                //为按钮添加时间监听方法
                boards[i][j].addActionListener(this);
            }
        }
        
        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {

    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

此时,我们已经可以看到我们造好的棋盘了。
在这里插入图片描述

4.编写下棋方法

棋盘有了,那该怎么下呢。接下来就需要编写下棋的方法。
首先我们需要添加一个标志flag,这个标志将表明此时是谁在下棋。
当flag=0时,表示此时下棋的是执X的玩家,当flag=1时,表示此时下棋的是执O的玩家。我们将标志变量初始化为0,这表明每一局都将是由执X的玩家先手。
接着我们就可以编写下棋方法了。当flag=0时,下X,将flag赋值为1,表示此时该执O的玩家下棋了。同理,当flag=1,时,下O,将flag赋值为0,表示此时该执X的玩家下棋了。

//下棋方法
private void play(JButton clickBtn,int i,int j){
if(flag == 0){
clickBtn.setText(“X”);
flag = 1;
}else if(flag == 1){
clickBtn.setText(“O”);
flag = 0;
}
}

public class MyGameWindow extends JFrame implements ActionListener {

    //以3行3列数组的形式创建表盘
    private JButton[][] boards = new JButton[3][3];
    //X先手
    private int flag = 0;//flag = 0 表示落子X flag = 1表示落子O


    public MyGameWindow(){
        //设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //将按钮数组添加到窗体中去并为按钮添加事件监听方法
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                //实例化按钮
                boards[i][j] = new JButton();
                //将按钮添加到窗体中去
                add(boards[i][j]);
                //为按钮添加时间监听方法
                boards[i][j].addActionListener(this);
            }
        }

        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {

    }
    
    //下棋方法
    private void play(JButton clickBtn,int i,int j){
        if(flag == 0){
            clickBtn.setText("X");
            flag = 1;
        }else if(flag == 1){
            clickBtn.setText("O");
            flag = 0;
        }
    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

5.编写寻找下棋位置的方法

编写完了下棋的方法之后,我们需要准确的知道玩家在棋盘的哪一个位置下了棋。因此需要编写一个寻找下棋位置的方法。
这个方法返回一个长度为2的location数组,location[0]记录玩家下棋位置的行数,location[1]记录玩家下棋位置的列数。
这样我们就能够在棋盘中准确的找到玩家下棋的位置了。

//寻找按钮位置
private int[] location(JButton clickBtn){
int[] location = new int[2];//location[0]表示第i行,location[1]表示第j列
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if(boards[i][j] == clickBtn){
location[0] = i;
location[1] = j;
}
}
}
return location;
}

public class MyGameWindow extends JFrame implements ActionListener {

    //以3行3列数组的形式创建表盘
    private JButton[][] boards = new JButton[3][3];
    //X先手
    private int flag = 0;//flag = 0 表示落子X flag = 1表示落子O


    public MyGameWindow(){
        //设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //将按钮数组添加到窗体中去并为按钮添加事件监听方法
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                //实例化按钮
                boards[i][j] = new JButton();
                //将按钮添加到窗体中去
                add(boards[i][j]);
                //为按钮添加时间监听方法
                boards[i][j].addActionListener(this);
            }
        }

        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
 

    }

    //下棋方法
    private void play(JButton clickBtn,int i,int j){
        if(flag == 0){
            clickBtn.setText("X");
            flag = 1;
        }else if(flag == 1){
            clickBtn.setText("O");
            flag = 0;
        }
    }

    //寻找按钮位置
    private int[] location(JButton clickBtn){
        int[] location = new int[2];//location[0]表示第i行,location[1]表示第j列
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(boards[i][j] == clickBtn){
                    location[0] = i;
                    location[1] = j;
                }
            }
        }
        return location;
    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

完善事件监听器

前期的准备工作基本上已经完成。想要让棋子跑到棋盘上去,我们还需要完善按钮的事件监听器。
首先我们需要获取玩家点击的到底是哪一个按钮,然后记录下这个按钮的行数和列数。然后我们就可以在棋盘上落下棋子啦。

@Override
public void actionPerformed(ActionEvent e) {
//获取点击的按钮
JButton clickBtn = (JButton) e.getSource();
//找到落子的位置
int[] places = location(clickBtn);
int i = places[0];
int j = places[1];
//下棋
play(clickBtn, i, j);
}

public class MyGameWindow extends JFrame implements ActionListener {

    //以3行3列数组的形式创建表盘
    private JButton[][] boards = new JButton[3][3];
    //X先手
    private int flag = 0;//flag = 0 表示落子X flag = 1表示落子O


    public MyGameWindow(){
        //设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //将按钮数组添加到窗体中去并为按钮添加事件监听方法
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                //实例化按钮
                boards[i][j] = new JButton();
                //将按钮添加到窗体中去
                add(boards[i][j]);
                //为按钮添加时间监听方法
                boards[i][j].addActionListener(this);
            }
        }

        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        //获取点击的按钮
        JButton clickBtn = (JButton) e.getSource();
        //找到落子的位置
        int[] places = location(clickBtn);
        int i = places[0];
        int j = places[1];
        //下棋
        play(clickBtn, i, j);
    }

    //下棋方法
    private void play(JButton clickBtn,int i,int j){
        if(flag == 0){
            clickBtn.setText("X");
            flag = 1;
        }else if(flag == 1){
            clickBtn.setText("O");
            flag = 0;
        }
    }

    //寻找按钮位置
    private int[] location(JButton clickBtn){
        int[] location = new int[2];//location[0]表示第i行,location[1]表示第j列
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(boards[i][j] == clickBtn){
                    location[0] = i;
                    location[1] = j;
                }
            }
        }
        return location;
    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

在这里插入图片描述
在这里插入图片描述

6.增加限制,一个位置只能下一颗棋子

看看上面的两张图片没有,有没有发现什么问题。
第一张图片是正常的,此时棋盘已满,但是我们仍然可以在棋盘的任意位置下棋,这就导致了第二幅图的情况。满屏的O,执X的玩家表示这不公平!
在这里插入图片描述
这不合理。那么怎么解决呢?
我们定义一个二维数组place,存储棋盘上棋子的情况。
当place[i][j]=1时,表示此位置有棋子X
当place[i][j]=-1时,表示此位置有棋子O
当place[i][j]=0时,表示此位置没有棋子
当玩家想要在已经有棋子的位置下棋时,我们应该给出提示,提醒他不能下在这里。
既然已经做出了规定,我们就需要在玩家下棋的时候记录下棋盘的情况,所以我们对下棋的方法稍作更改,让其及时记录棋盘的情况。

//下棋方法
private void play(JButton clickBtn,int i,int j){
if(flag == 0){
clickBtn.setText(“X”);
flag = 1;
place[i][j] = 1;
}else if(flag == 1){
clickBtn.setText(“O”);
flag = 0;
place[i][j] = -1;
}
}

//判断当前位置是否落子
if(place[i][j] == 1 || place[i][j] == -1){
JOptionPane.showMessageDialog(this, “该处已经落子,请选择其他位置!”);
return ;
}

public class MyGameWindow extends JFrame implements ActionListener {

    //以3行3列数组的形式创建表盘
    private JButton[][] boards = new JButton[3][3];
    //X先手
    private int flag = 0;//flag = 0 表示落子X flag = 1表示落子O
    private int[][] place = new int[3][3];//落子X将对应的place设置为1,落子O设置为-1

    public MyGameWindow(){
        //设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //将按钮数组添加到窗体中去并为按钮添加事件监听方法
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                //实例化按钮
                boards[i][j] = new JButton();
                //将按钮添加到窗体中去
                add(boards[i][j]);
                //为按钮添加时间监听方法
                boards[i][j].addActionListener(this);
            }
        }

        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        //获取点击的按钮
        JButton clickBtn = (JButton) e.getSource();
        //找到落子的位置
        int[] places = location(clickBtn);
        int i = places[0];
        int j = places[1];
        //判断当前位置是否落子
        if(place[i][j] == 1 || place[i][j] == -1){
            JOptionPane.showMessageDialog(this, "该处已经落子,请选择其他位置!");
            return ;
        }
        //下棋
        play(clickBtn, i, j);
    }

    //下棋方法
    private void play(JButton clickBtn,int i,int j){
        if(flag == 0){
            clickBtn.setText("X");
            flag = 1;
            place[i][j] = 1;
        }else if(flag == 1){
            clickBtn.setText("O");
            flag = 0;
            place[i][j] = -1;
        }
    }

    //寻找按钮位置
    private int[] location(JButton clickBtn){
        int[] location = new int[2];//location[0]表示第i行,location[1]表示第j列
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(boards[i][j] == clickBtn){
                    location[0] = i;
                    location[1] = j;
                }
            }
        }
        return location;
    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

在这里插入图片描述

7.编写胜利方法

通过上面的运行结果我们又发现了什么问题?对,就是明明X已经赢了,可是游戏仍然在继续。这就需要我们编写胜利方法来判断谁是赢家。
胜利的方式有几种?横着的三种,竖着的三种,斜着的两种。那么一共就是八种方法。

int first = place[0][0] + place[0][1] + place[0][2];
int second = place[1][0] + place[1][1] + place[1][2];
int third = place[2][0] + place[2][1] + place[2][2];
int forth = place[0][0] + place[1][0] + place[2][0];
int fifth = place[0][1] + place[1][1] + place[2][1];
int sixth = place[0][2] + place[1][2] + place[2][2];
int seventh = place[0][0] + place[1][1] + place[2][2];
int eighth = place[0][2] + place[1][1] + place[2][0];

那么我们怎么判断是谁赢了呢?前面我们规定,下X的位置places数组的值被设为1,下O的位置places数组的值被设为-1。
当此上八种方法中的一种的值为3,则表明执X的玩家胜利。
当此上八种方法中的一种的值为-3,则表明执O的玩家胜利。

if(first == 3 || second == 3 || third == 3 || forth == 3 ||fifth == 3||sixth == 3||seventh == 3||eighth ==3){
return 1;//X胜利
}
else if(first == -3 || second == -3 || third == -3 || forth == -3 ||fifth == -3||sixth == -3||seventh == -3||eighth ==-3){
return -1;//O胜利
}

若棋盘未满,则继续游戏。

for (int i = 0; i < 3 ; i++) {
for (int j = 0; j < 3; j++) {
if(place[i][j] == 0)
return -2;//表示继续进行
}
}

否则,双方平局。

rerturn 0;

public class MyGameWindow extends JFrame implements ActionListener {

    //以3行3列数组的形式创建表盘
    private JButton[][] boards = new JButton[3][3];
    //X先手
    private int flag = 0;//flag = 0 表示落子X flag = 1表示落子O
    private int[][] place = new int[3][3];//落子X将对应的place设置为1,落子O设置为-1

    public MyGameWindow(){
        //设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //将按钮数组添加到窗体中去并为按钮添加事件监听方法
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                //实例化按钮
                boards[i][j] = new JButton();
                //将按钮添加到窗体中去
                add(boards[i][j]);
                //为按钮添加时间监听方法
                boards[i][j].addActionListener(this);
            }
        }

        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        //获取点击的按钮
        JButton clickBtn = (JButton) e.getSource();
        //找到落子的位置
        int[] places = location(clickBtn);
        int i = places[0];
        int j = places[1];
        //判断当前位置是否落子
        if(place[i][j] == 1 || place[i][j] == -1){
            JOptionPane.showMessageDialog(this, "该处已经落子,请选择其他位置!");
            return ;
        }
        //下棋
        play(clickBtn, i, j);
        //判断输赢
        int judge = judge();
        if(judge == 1){
            JOptionPane.showMessageDialog(this, "X胜利!");
        }else if(judge == -1){
            JOptionPane.showMessageDialog(this, "O胜利!");
        }else if(judge == -2){
            return;
        }else{
            JOptionPane.showMessageDialog(this, "平局!");
        }
    }
    //判断输赢
    private  int judge(){
        int first = place[0][0] + place[0][1] + place[0][2];
        int second = place[1][0] + place[1][1] + place[1][2];
        int third = place[2][0] + place[2][1] + place[2][2];
        int forth = place[0][0] + place[1][0] + place[2][0];
        int fifth = place[0][1] + place[1][1] + place[2][1];
        int sixth = place[0][2] + place[1][2] + place[2][2];
        int seventh = place[0][0] + place[1][1] + place[2][2];
        int eighth = place[0][2] + place[1][1] + place[2][0];

        if(first == 3 || second == 3 || third == 3 || forth == 3 ||fifth == 3||sixth == 3||seventh == 3||eighth ==3){
            return 1;//X胜利
        }

        else if(first == -3 || second == -3 || third == -3 || forth == -3 ||fifth == -3||sixth == -3||seventh == -3||eighth ==-3){
            return -1;//O胜利
        }

        for (int i = 0; i < 3 ; i++) {
            for (int j = 0; j < 3; j++) {
                if(place[i][j] == 0)
                    return -2;//表示继续进行
            }
        }
        return 0;//表示平局

    }

    //下棋方法
    private void play(JButton clickBtn,int i,int j){
        if(flag == 0){
            clickBtn.setText("X");
            flag = 1;
            place[i][j] = 1;
        }else if(flag == 1){
            clickBtn.setText("O");
            flag = 0;
            place[i][j] = -1;
        }
    }

    //寻找按钮位置
    private int[] location(JButton clickBtn){
        int[] location = new int[2];//location[0]表示第i行,location[1]表示第j列
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(boards[i][j] == clickBtn){
                    location[0] = i;
                    location[1] = j;
                }
            }
        }
        return location;
    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.编写清楚数据方法

以上我们已经基本上实现了井字棋游戏了,但是我们每玩一局都需要重新启动一次程序,这很麻烦,这不是我们想要的。
我们想要每一次游戏结束后,棋盘会自动刷新为最开始的样子,那么我们就可以随心所欲的想下几局就下几局,不想玩的时候直接关闭程序即可。

我们需要清楚两个数据,一个棋盘中的棋子,一个是存储棋子位置的数组。
将其重新初始化,就可以进行下一局游戏了。

//清屏方法
private void clear(){
flag = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
boards[i][j].setText("");
place[i][j] = 0;
}
}
}

最终源代码

这样所有的代码都写完了,是不是很简单。
在这里插入图片描述
代码整理如下:

package com.zhang.game;

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

/**
 * @author nanqi-code
 * @create 2021/9/1-23:11
 */
public class MyGameWindow extends JFrame implements ActionListener {

    //以3行3列数组的形式创建表盘
    private JButton[][] boards = new JButton[3][3];
    //X先手
    private int flag = 0;//flag = 0 表示落子X flag = 1表示落子O
    private int[][] place = new int[3][3];//落子X将对应的place设置为1,落子O设置为-1

    public MyGameWindow(){
        //设置窗口的标题
        setTitle("井字棋");
        //设置窗体的位置和大小
        setBounds(100,100,600,600);
        //设置窗体的布局方式
        setLayout(new GridLayout(3,3));
        //将按钮数组添加到窗体中去并为按钮添加事件监听方法
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                //实例化按钮
                boards[i][j] = new JButton();
                //将按钮添加到窗体中去
                add(boards[i][j]);
                //为按钮添加时间监听方法
                boards[i][j].addActionListener(this);
            }
        }

        //设置窗体的关闭方式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //设置窗体可见
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        //获取点击的按钮
        JButton clickBtn = (JButton) e.getSource();
        //找到落子的位置
        int[] places = location(clickBtn);
        int i = places[0];
        int j = places[1];
        //判断当前位置是否落子
        if(place[i][j] == 1 || place[i][j] == -1){
            JOptionPane.showMessageDialog(this, "该处已经落子,请选择其他位置!");
            return ;
        }
        //下棋
        play(clickBtn, i, j);
        //判断输赢
        int judge = judge();
        if(judge == 1){
            JOptionPane.showMessageDialog(this, "X胜利!");
            clear();
        }else if(judge == -1){
            JOptionPane.showMessageDialog(this, "O胜利!");
            clear();
        }else if(judge == -2){
            return;
        }else{
            JOptionPane.showMessageDialog(this, "平局!");
            clear();
        }
    }
    //判断输赢
    private  int judge(){
        int first = place[0][0] + place[0][1] + place[0][2];
        int second = place[1][0] + place[1][1] + place[1][2];
        int third = place[2][0] + place[2][1] + place[2][2];
        int forth = place[0][0] + place[1][0] + place[2][0];
        int fifth = place[0][1] + place[1][1] + place[2][1];
        int sixth = place[0][2] + place[1][2] + place[2][2];
        int seventh = place[0][0] + place[1][1] + place[2][2];
        int eighth = place[0][2] + place[1][1] + place[2][0];

        if(first == 3 || second == 3 || third == 3 || forth == 3 ||fifth == 3||sixth == 3||seventh == 3||eighth ==3){
            return 1;//X胜利
        }

        else if(first == -3 || second == -3 || third == -3 || forth == -3 ||fifth == -3||sixth == -3||seventh == -3||eighth ==-3){
            return -1;//O胜利
        }

        for (int i = 0; i < 3 ; i++) {
            for (int j = 0; j < 3; j++) {
                if(place[i][j] == 0)
                    return -2;//表示继续进行
            }
        }
        return 0;//表示平局

    }

    //下棋方法
    private void play(JButton clickBtn,int i,int j){
        if(flag == 0){
            clickBtn.setText("X");
            flag = 1;
            place[i][j] = 1;
        }else if(flag == 1){
            clickBtn.setText("O");
            flag = 0;
            place[i][j] = -1;
        }
    }

    //寻找按钮位置
    private int[] location(JButton clickBtn){
        int[] location = new int[2];//location[0]表示第i行,location[1]表示第j列
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(boards[i][j] == clickBtn){
                    location[0] = i;
                    location[1] = j;
                }
            }
        }
        return location;
    }

    //清屏方法
    private  void clear(){
        flag = 0;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                boards[i][j].setText("");
                place[i][j] = 0;
            }
        }
    }

    public static void main(String[] args) {
        new MyGameWindow();
    }
}

在这里插入图片描述

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐