java——博弈算法实现井字棋游戏

通过java语言开发了一个简单的井字棋游戏。主要有6个类,其中有一个是主类(Main.java),一个是抽象类(PiecesMove.java)组成。
java——博弈算法实现井字棋游戏_第1张图片
下面对各个类简单介绍一下:

TicTicToe.java:
主要负责创建棋盘,管理棋盘。

TicTicToeUI.java:
主要由判断谁是先手的对话框、对战界面和提示谁输谁赢三个对话框组成,负责管理用户交互。

PiecesMove.java:
里面有2个方法,move(TicTacToe tict)是抽象方法由子类实现,isWinning(int[][] cur)判断是否游戏结束。

Computer.java:
负责计算机的落子位置计算,核心算法为博弈算法。

Player.java:
负责获取人落子的位置。

Main.java:
负责创建以上类的实例,控制谁先走子,游戏结束是否继续等逻辑处理。

下面贴出源代码:

TicTicToe.java:

/*
*  Created by shphuang on 2020/4/4.
* */

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

public class TicTacToe {
     
    public static int num = 0;//计数走的步数
    //存储棋盘矩阵值
    private int[][] chessBd = new int[3][3];

    //按钮矩阵
    private JButton[][] buttons;
    //判断该谁走
    public boolean PLAYER_MOVE = true;

    //实例化Tictactoe
    private static TicTacToe tict = null;
    public static TicTacToe instanceTict(JButton[][] buttons){
     
        if (tict == null){
     
            tict = new TicTacToe(buttons);
        }
        return tict;
    }

    //初始化棋盘
    private TicTacToe(JButton[][] buttons){
     
        this.buttons = buttons;
        clearBoard();
    }

    //设置棋盘的值
    public void setChessBd(int x , int y , int checkBd) {
     
        this.chessBd[x][y] = checkBd;
    }

    //得到棋盘
    public int[][] getChessBd() {
     
        return chessBd;
    }

    //得到按钮矩阵
    public JButton[][] getButtons() {
     
        return buttons;
    }

    //清空棋盘
    public void clearBoard() {
     
        for (int i=0 ; i < 3 ; i++){
     
            for (int j=0 ; j < 3 ; j++){
     
                chessBd[i][j] = 0 ;
            }
        }

        num = 0;
    }

    //将棋盘画出来
    public void drawBoard(){
     
        updateUI();
    }

    //更新ui
    private boolean updateUI(){
     
        for (int i = 0; i < chessBd.length; i++) {
     
            for (int j = 0; j < chessBd.length; j++) {
     
                switch (chessBd[i][j]){
     
                    case -1:
                        buttons[i][j].setBackground(Color.white);
                        break;
                    case 0:
                        buttons[i][j].setBackground(Color.lightGray);
                        break;
                    case 1:
                        buttons[i][j].setBackground(Color.black);
                        break;
                }
            }
        }


        return false;
    }
}

TicTicToeUI.java:

/*
*  Created by shphuang on 2020/4/4.
* */

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

public class TicTacToeUI {
     

    private JButton[][] buttons = new JButton[3][3];
    private JFrame jf;
    private JPanel jp;

    //实例化TictactoeUI
    private static TicTacToeUI tictactoeUI = null;
    public static TicTacToeUI instanceTictactoeUI(){
     
        if (tictactoeUI == null){
     
            tictactoeUI = new TicTacToeUI();
        }
        return tictactoeUI;
    }
    private TicTacToeUI(){
     
        init();
    }
    private void init(){
     
        //创建并设置frame
        jf = new JFrame();
        jf.setTitle("designed by 爱编程的大鹏");    //设置显示窗口标题
        // 得到显示器屏幕的宽高
        int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
        int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
        int width = 470;int height = 470;
        int offset_x = screenWidth/2-width/2;
        int offset_y = screenHeight/2-height/2;

        jf.setBounds(offset_x,offset_y,width,height);    //设置窗口显示尺寸
        Container c= jf.getContentPane();    //获取当前窗口的内容窗格

        //创建并设置panel
        jp = new JPanel();
        jp.setBackground(new Color(159, 156, 157, 20));    //设置背景色
        jp.setLayout(new GridLayout(3,3,10,10)); //设置布局

        //初始化按钮矩阵
        for (int i = 0; i < buttons.length; i++) {
     
            for (int j = 0; j < buttons.length; j++) {
     
                JButton button = new JButton();
                buttons[i][j] = button;
                button.setBackground(Color.lightGray);
                jp.add(button);
            }
        }

        c.add(jp);    //将panel面板添加到frame
        jf.setVisible(true);    //设置窗口是否可见
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    //置窗口是否可以关闭
    }//初始化TictactoeUI

    //得到JFrame对象
    public JFrame getJf() {
     
        return jf;
    }
    //得到JPanel对象
    public JPanel getJp() {
     
        return jp;
    }
    //返回buttons对象
    public JButton[][] getButtons() {
     
        return buttons;
    }

    public boolean DialogUI(String msg) {
     
        try {
     
            Thread.sleep(200);
            int temp = JOptionPane.showConfirmDialog(jp,msg,"提示",0,1);
            if (temp == 0){
     
                return true;
            }
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
        return false;
    }
}

PiecesMove.java:

/*
*  Created by shphuang on 2020/4/4.
* */

abstract class PiecesMove {
     
    public abstract void move(TicTacToe tict);

    //返回0表示没有人赢,返回-1表示人赢了,返回1表示计算机赢了,返回2表示平局
    public static int isWinning(int[][] cur){
     
        for (int i = 0; i<3; i++)
        {
     
            if (cur[i][0] == 1 && cur[i][1] == 1 && cur[i][2] == 1)
                return 1;
            if (cur[i][0] == -1 && cur[i][1] == -1 && cur[i][2] == -1)
                return -1;
        }
        for (int i = 0; i<3; i++)
        {
     
            if (cur[0][i] == 1 && cur[1][i] == 1 && cur[2][i] == 1)
                return 1;
            if (cur[0][i] == -1 && cur[1][i] == -1 && cur[2][i] == -1)
                return -1;
        }
        if ((cur[0][0] == 1 && cur[1][1] == 1 && cur[2][2] == 1) || (cur[2][0] == 1 && cur[1][1] == 1 && cur[0][2] == 1))
            return 1;
        if ((cur[0][0] == -1 && cur[1][1] == -1 && cur[2][2] == -1) || (cur[2][0] == -1 && cur[1][1] == -1 && cur[0][2] == -1))
            return -1;

        if(TicTacToe.num >= 9){
     
            return 2;
        }

        return 0;
    }
}

Computer.java:

/*
*  Created by shphuang on 2020/4/4.
* */

public class Computer extends PiecesMove {
     
    //电脑的棋子类型
    private static final int COMPUTER = 1;
    private static final int depth = 3;

    //实例化Player
    private static Computer computer = null;

    public static Computer instanceComputer() {
     
        if (computer == null) {
     
            computer = new Computer();
        }
        return computer;
    }

    @Override
    public void move(TicTacToe tict) {
     
        tict.num += 1;//步数+1
        int[][] curQP = createTempArray(tict.getChessBd());
        int[] xyVal = getXY(curQP);

        if (tict.getChessBd()[xyVal[0]][xyVal[1]] == 0) {
     
            tict.setChessBd(xyVal[0], xyVal[1], COMPUTER);
            tict.drawBoard();
        }
    }

    //得到x、y的坐标
    private int[] getXY(int[][] curQP) {
     
        int[] xy = new int[2];
        int[] val = new int[1];
        val[0] = -10000;
        int m = -10000, dep = 1;

        for (int x = 0; x < 3; x++) {
     
            for (int y = 0; y < 3; y++) {
     
                if (curQP[x][y] == 0) {
     
                    curQP[x][y] = 1;

                    if (isWinning(curQP) == 1) {
     
                        xy[0] = x;
                        xy[1] = y;
                        return xy;
                    }

                    val[0] = cut(curQP, val, dep, true);

                    if (val[0] > m) {
     
                        m = val[0];
                        xy[0] = x;
                        xy[1] = y;
                    }

                    val[0] = -10000;
                    curQP[x][y] = 0;
                }
            }
        }

        return xy;
    }

    //主算法部分,实现A-B剪枝的算法,val为上一层的评价值,dep为搜索深度,max记录上一层是否为极大层
    private int cut(int[][] curQP, int[] val, int dep, boolean max){
     
        //如果搜索深度达到最大深度,或者深度加上当前棋子数已经达到9,就直接调用评价函数
        if (dep == depth || dep + TicTacToe.num == 9){
     
            return value(createTempArray(curQP));
        }

        int i, j , temp;
        int[] flag = new int[1];

        boolean out = false;         //out记录是否剪枝,初始为false

        //如果用户玩家输了,就置上一层的评价值为无穷(用很大的值代表无穷)
        if (isWinning(curQP) == 1){
     
            val[0] = 10000;
            return 0;
        }

        if (max)                    //如果上一层是极大层,本层则需要是极小层,记录flag为无穷大;反之,则为记录为负无穷大
            flag[0] = 10000;            //flag记录本层节点的极值
        else
            flag[0] = -10000;

        for (i = 0; i < 3 && !out; i++)      //两重循环,遍历棋盘所有位置
        {
     
            for (j = 0; j < 3 && !out; j++) {
     
                if (curQP[i][j] == 0)     //如果该位置上没有棋子
                {
     
                    if (max)                //并且为上一层为极大层,即本层为极小层,轮到用户玩家走了。
                    {
     
                        curQP[i][j] = -1;     //该位置填上用户玩家棋子

                        if (isWinning(curQP) == -1)    //如果用户玩家赢了
                            temp = -10000;    //置棋盘评价值为负无穷
                        else
                            temp = cut(curQP,flag, dep + 1, !max);  //否则继续调用ab剪枝函数
                        if (temp < flag[0])              //如果下一步棋盘的评价值小于本层节点的极值,则置本层极值为更小者,即后辈结点极小值<=祖先节点极大值
                            flag[0] = temp;
                    } else{
                     //如果上一层为极小层,算法与上面刚好相反
                        curQP[i][j] = 1;//该位置填上电脑玩家棋子

                        if (isWinning(curQP) == 1)//如果电脑玩家赢了,置棋盘评价值为正无穷
                            temp = 10000;
                        else
                            temp = cut(curQP,flag, dep + 1, !max); // 否则继续调用ab剪枝函数

                        if (temp > flag[0])     //如果下一步棋盘的评价值大于本层节点的极值,则置本层极值为更小者,即后辈结点极大值>=祖先节点极小值
                            flag[0] = temp;

                        if (flag[0] >= val[0])     //如果本层的极值已经大于上一层的评价值,则不需要搜索下去,剪枝 B剪枝
                            out = true;
                    }
                    curQP[i][j] = 0;   //把模拟下的一步棋还原,回溯
                }
            }
        }
        if (max){
           //根据上一层是否为极大层,用本层的极值修改上一层的评价值

            if (flag[0] > val[0])
                val[0] = flag[0];
        } else {
     
            if (flag[0] < val[0])
                val[0] = flag[0];
        }

        return flag[0];   //函数返回的是本层的极值
    }

    //评估函数
    private int value(int[][] curQP) {
     
        int p = 0;
        int q = 0;
        int[][] tmpQP = new int[3][3];
        for (int i = 0; i < 3; i++) {
     
            for (int j = 0; j < 3; j++) {
     
                tmpQP[i][j] = curQP[i][j];
            }
        }
        //将棋盘中的空格填满自己的棋子,既将棋盘数组中的0变为1
        for (int i = 0; i<3; i++)
            for (int j = 0; j<3; j++)
                if (curQP[i][j] == 0)
                    tmpQP[i][j] = 1;
                else
                    tmpQP[i][j] = curQP[i][j];

        //电脑一方
        //计算每一行中有多少行的棋子连成3个的
        for (int i = 0; i<3; i++)
            p += (tmpQP[i][0] + tmpQP[i][1] + tmpQP[i][2]) / 3;
        //计算每一列中有多少列的棋子连成3个的
        for (int i = 0; i<3; i++)
            p += (tmpQP[0][i] + tmpQP[1][i] + tmpQP[2][i]) / 3;
        //斜行有没有连成3个的?
        p += (tmpQP[0][0] + tmpQP[1][1] + tmpQP[2][2]) / 3;
        p += (tmpQP[2][0] + tmpQP[1][1] + tmpQP[0][2]) / 3;

        //将棋盘中的空格填满对方的棋子,既将棋盘数组中的0变为-1
        for (int i = 0; i<3; i++)
            for (int j = 0; j<3; j++)
                if (curQP[i][j] == 0)tmpQP[i][j] = -1;
                else tmpQP[i][j] = curQP[i][j];

        //对方
        //计算每一行中有多少行的棋子连成3个的
        for (int i = 0; i<3; i++)
            q += (tmpQP[i][0] + tmpQP[i][1] + tmpQP[i][2]) / 3;
        //计算每一列中有多少列的棋子连成3个的
        for (int i = 0; i<3; i++)
            q += (tmpQP[0][i] + tmpQP[1][i] + tmpQP[2][i]) / 3;
        //斜行有没有连成3个的?
        q += (tmpQP[0][0] + tmpQP[1][1] + tmpQP[2][2]) / 3;
        q += (tmpQP[2][0] + tmpQP[1][1] + tmpQP[0][2]) / 3;

        return p + q;
    }

    //返回一个局部变量的数组,防止干扰棋盘值
    private int[][] createTempArray(int[][] array) {
     
        int[][] temp = new int[3][3];
        for (int i = 0; i < 3; i++) {
     
            for (int j = 0; j < 3; j++) {
     
                temp[i][j] = array[i][j];
            }
        }
        return temp;
    }

}

Player.java:

/*
 *  Created by shphuang on 2020/4/4.
 * */

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

public class Player extends PiecesMove {
     
    private static final int PLAYER = -1;

    //实例化Player
    private static Player player = null;
    public static Player instancePlayer(){
     
        if (player == null){
     
            player = new Player();
        }
        return player;
    }

    //Player走一步
    @Override
    public void move(TicTacToe tict) {
     
        while(true){
     
            int[] xy = getXY(tict);
            int position_x = xy[0];
            int position_y = xy[1];

            if (tict.getChessBd()[position_x][position_y] == 0){
     
                tict.num += 1;//步数+1
                tict.setChessBd(position_x, position_y ,  PLAYER);
                tict.drawBoard();
                break;
            }
        }

    }

    //监听用户走的位置
    private int[] getXY(TicTacToe tict) {
     
        int[] xy = new int[2];
        xy[0] = -1;xy[1] = -1;
        JButton[][] buttons = tict.getButtons();
        while (xy[0]==-1 || xy[1] == -1){
     
            for (int i = 0; i < buttons.length; i++) {
     
                for (int j = 0; j < buttons.length; j++) {
     
                    int finalI = i;
                    int finalJ = j;
                    buttons[i][j].addActionListener(new ActionListener() {
     
                        @Override
                        public void actionPerformed(ActionEvent actionEvent) {
     
                            if (actionEvent.getSource() == buttons[finalI][finalJ]) {
     
                                xy[0] = finalI;
                                xy[1] = finalJ;
                            }
                        }
                    });

                }
            }
        }
        return xy;
    }
}

Main.java:

/*
* Created by shphuang on 2020/4/4.
*
*
* 1、产生棋盘
* 2、判断谁先(默认player先)
* 3、player走一步
* 4、判断player是否胜利
* 5、computer走一步
* 6、判断computer是否胜利
* */


public class Main {
     

    public static void main(String[] args) {
     
        //实例化TictactoeUI、Tictactoe、Player和Computer
        TicTacToeUI ui = TicTacToeUI.instanceTictactoeUI();
        TicTacToe tict = TicTacToe.instanceTict(ui.getButtons());
        Player player = Player.instancePlayer();
        Computer computer = Computer.instanceComputer();

        //记录是否进行游戏
        boolean flag = true;
        while (flag){
     
            //清空棋盘
            tict.clearBoard();
            //画出棋盘
            tict.drawBoard();
            //记录谁先走
            boolean wfm = ui.DialogUI("你要先走吗?");
            //记录游戏是否结束,flag=false则游戏结束
            flag = startGame(tict,computer,player,wfm);

            //记录游戏结束时谁赢。-1:player胜利  1:computer胜利  2:平局
            int whoWin = PiecesMove.isWinning(tict.getChessBd());

            if (whoWin == -1){
     
                flag = ui.DialogUI("恭喜,你赢了 !\n\t\t你还要继续?");

            }else if (whoWin == 1){
     
                flag = ui.DialogUI("很抱歉,你输了 !\n\t\t你还要继续?");

            }else if(whoWin == 2){
     
                flag = ui.DialogUI("你真厉害,居然打成平手了 !\n\t\t你还要继续?");

            }
        }

        //关闭窗口
        ui.getJf().dispose();
    }

    //游戏开始
    private static boolean startGame(TicTacToe tict, Computer computer, Player player, boolean wfm){
     
        while (true){
     
            if (wfm){
     
                player.move(tict);
                switch (PiecesMove.isWinning(tict.getChessBd())){
     
                    case -1:
                        return false;
                    case 1:
                        return false;
                    case 2:
                        return false;
                }

                computer.move(tict);
                switch (PiecesMove.isWinning(tict.getChessBd())){
     
                    case -1:
                        return false;
                    case 1:
                        return false;
                    case 2:
                        return false;
                }
            } else {
     
                computer.move(tict);
                switch (PiecesMove.isWinning(tict.getChessBd())){
     
                    case -1:
                        return false;
                    case 1:
                        return false;
                    case 2:
                        return false;
                }

                player.move(tict);
                switch (PiecesMove.isWinning(tict.getChessBd())){
     
                    case -1:
                        return false;
                    case 1:
                        return false;
                    case 2:
                        return false;
                }
            }
        }
    }
}

百度网盘链接:传送门 提取码:xmy7

你可能感兴趣的:(Java,游戏,算法,游戏开发,java)