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

通过java语言开发了一个简单的井字棋游戏。主要有6个类,其中有一个是主类(Main.java),一个是抽象类(PiecesMove.java)组成。
在这里插入图片描述
下面对各个类简单介绍一下:

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

  • 16
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是基于博弈树搜索算法井字棋代码示例: ```python import copy # 定义棋盘大小 BOARD_SIZE = 3 # 定义玩家和电脑的棋子 PLAYER = 'X' COMPUTER = 'O' # 定义博弈树搜索算法的深度 SEARCH_DEPTH = 5 # 初始化棋盘 def init_board(): board = [] for i in range(BOARD_SIZE): row = [] for j in range(BOARD_SIZE): row.append('-') board.append(row) return board # 打印棋盘 def print_board(board): for row in board: print(' '.join(row)) # 判断当前棋盘状态(胜利、平局或未结束) def get_game_status(board): for i in range(BOARD_SIZE): # 检查行 if board[i][0] != '-' and len(set(board[i])) == 1: return board[i][0] # 检查列 if board[0][i] != '-' and len(set([board[j][i] for j in range(BOARD_SIZE)])) == 1: return board[0][i] # 检查对角线 if board[0][0] != '-' and len(set([board[i][i] for i in range(BOARD_SIZE)])) == 1: return board[0][0] if board[0][BOARD_SIZE-1] != '-' and len(set([board[i][BOARD_SIZE-1-i] for i in range(BOARD_SIZE)])) == 1: return board[0][BOARD_SIZE-1] # 检查平局 if all([cell != '-' for row in board for cell in row]): return 'Tie' # 未结束 return None # 计算当前棋盘得分 def get_score(board): # 如果玩家获胜,则得分为-10 if get_game_status(board) == PLAYER: return -10 # 如果电脑获胜,则得分为10 elif get_game_status(board) == COMPUTER: return 10 # 平局得分为0 else: return 0 # 实现博弈树搜索算法 def minimax(board, depth, is_maximizing): # 判断是否达到搜索深度或者当前棋盘状态已经结束 game_status = get_game_status(board) if depth == 0 or game_status is not None: return get_score(board) # 如果当前是电脑的回合 if is_maximizing: best_score = float('-inf') for i in range(BOARD_SIZE): for j in range(BOARD_SIZE): if board[i][j] == '-': # 模拟电脑下棋 new_board = copy.deepcopy(board) new_board[i][j] = COMPUTER # 递归搜索 score = minimax(new_board, depth-1, False) # 更新最优得分 best_score = max(best_score, score) return best_score # 如果当前是玩家的回合 else: best_score = float('inf') for i in range(BOARD_SIZE): for j in range(BOARD_SIZE): if board[i][j] == '-': # 模拟玩家下棋 new_board = copy.deepcopy(board) new_board[i][j] = PLAYER # 递归搜索 score = minimax(new_board, depth-1, True) # 更新最优得分 best_score = min(best_score, score) return best_score # 实现电脑下棋 def computer_move(board): best_score = float('-inf') best_move = None for i in range(BOARD_SIZE): for j in range(BOARD_SIZE): if board[i][j] == '-': # 模拟电脑下棋 new_board = copy.deepcopy(board) new_board[i][j] = COMPUTER # 计算当前得分 score = minimax(new_board, SEARCH_DEPTH, False) # 更新最优得分和最优下法 if score > best_score: best_score = score best_move = (i, j) # 执行最优下法 board[best_move[0]][best_move[1]] = COMPUTER print(f"电脑下棋 ({best_move[0]}, {best_move[1]})") print_board(board) # 实现玩家下棋 def player_move(board): while True: try: row = int(input("请输入行号(从1开始):")) - 1 col = int(input("请输入列号(从1开始):")) - 1 if row < 0 or row >= BOARD_SIZE or col < 0 or col >= BOARD_SIZE or board[row][col] != '-': print("请输入合法的行号和列号!") continue board[row][col] = PLAYER print_board(board) break except ValueError: print("请输入合法的行号和列号!") # 实现井字棋游戏 def play_game(): board = init_board() print("游戏开始!") print_board(board) while True: player_move(board) if get_game_status(board) is not None: break computer_move(board) if get_game_status(board) is not None: break game_status = get_game_status(board) if game_status == 'Tie': print("平局!") elif game_status == PLAYER: print("恭喜你获胜!") else: print("很遗憾,你输了。") # 启动游戏 play_game() ``` 在此示例中,我们定义了一个 `minimax()` 函数来实现博弈树搜索算法,并将搜索深度设为了 5。同时,我们还定义了 `computer_move()` 和 `player_move()` 函数来模拟电脑和玩家下棋的过程。最后,我们通过 `play_game()` 函数来实现整个井字棋游戏的流程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值