0

我正在从事人工智能项目,以使用Minimax算法开发井字游戏 4X4

我有这个在3x3 TicTacToe 板上运行Minimax算法的现有程序。

我想把它扩展到 4x4 TicTacToe

但我不知道我该怎么做??

   import java.util.*;


//defines the point where to place 1 or 2
class Point {

    int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "[" + x + ", " + y + "]";
    }
}

//defines the score per point -1,0,1
class PointsAndScores {

    int score;
    Point point;

    PointsAndScores(int score, Point point) {
        this.score = score;
        this.point = point;
    }
}

//defince the game board
class Board {

    List<Point> availablePoints;
    Scanner scan = new Scanner(System.in);
    int[][] board = new int[3][3];

    public Board() {
    }

    public boolean isGameOver() {
        //Game is over is someone has won, or board is full (draw)
        return (hasXWon() || hasOWon() || getAvailableStates().isEmpty());
    }
    //check if X have won represented by 1
    public boolean hasXWon() {
        if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
            //System.out.println("O Diagonal Win");
            return true;
        }
        for (int i = 0; i < 3; ++i) {
            if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
                    || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
                // System.out.println("O Row or Column win");
                return true;
            }
        }
        return false;
    }

    //check if O has won represented by 2
    public boolean hasOWon() {
        if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
            // System.out.println("X Diagonal Win");
            return true;
        }
        for (int i = 0; i < 3; ++i) {
            if ((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
                    || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2)) {
                //  System.out.println("X Row or Column win");
                return true;
            }
        }

        return false;
    }

    //check available states in the board
    public List<Point> getAvailableStates() {
        availablePoints = new ArrayList<>();
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                if (board[i][j] == 0) {
                    availablePoints.add(new Point(i, j));
                }
            }
        }
        return availablePoints;
    }

    //put player move in the board
    public void placeAMove(Point point, int player) {
        board[point.x][point.y] = player;   //player = 1 for O, 2 for X..
    }

    //get best movement according to the board state
    public Point returnBestMove() {
        int MAX = -100000;
        int best = -1;

        for (int i = 0; i < rootsChildrenScores.size(); ++i) { 
            if (MAX < rootsChildrenScores.get(i).score) {
                MAX = rootsChildrenScores.get(i).score;
                best = i;
            }
        }

        return rootsChildrenScores.get(best).point;
    }

    //accepts input from user
    void takeHumanInput() {
        System.out.println("Your move: ");
        int x = scan.nextInt();
        int y = scan.nextInt();
        Point point = new Point(x, y);
        placeAMove(point, 2); 
    }

    //display current board
    public void displayBoard() {
        System.out.println();

        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                System.out.print(board[i][j] + " ");
            }
            System.out.println();

        }
    }

    //get min value
    public int returnMin(List<Integer> list) {
        int min = Integer.MAX_VALUE;
        int index = -1;
        for (int i = 0; i < list.size(); ++i) {
            if (list.get(i) < min) {
                min = list.get(i);
                index = i;
            }
        }
        return list.get(index);
    }

    //get max value
    public int returnMax(List<Integer> list) {
        int max = Integer.MIN_VALUE;
        int index = -1;
        for (int i = 0; i < list.size(); ++i) {
            if (list.get(i) > max) {
                max = list.get(i);
                index = i;
            }
        }
        return list.get(index);
    }

    //declares a list for scores
    List<PointsAndScores> rootsChildrenScores;

    //excutes minimax algorithm
    public void callMinimax(int depth, int turn){
        rootsChildrenScores = new ArrayList<>();
        minimax(depth, turn);
    }

    //minimax algorithm
    public int minimax(int depth, int turn) {

        if (hasXWon()) return +1;
        if (hasOWon()) return -1;

        //get available states from the board
        List<Point> pointsAvailable = getAvailableStates();
        if (pointsAvailable.isEmpty()) return 0; 

        //stores scores
        List<Integer> scores = new ArrayList<>(); 

        for (int i = 0; i < pointsAvailable.size(); ++i) {
            Point point = pointsAvailable.get(i);  

            if (turn == 1) { //O's turn select the highest from below minimax() call
                placeAMove(point, 1); 
                int currentScore = minimax(depth + 1, 2);
                scores.add(currentScore);//add scores to the list

                if (depth == 0) 
                    rootsChildrenScores.add(new PointsAndScores(currentScore, point));

            } else if (turn == 2) {//X's turn select the lowest from below minimax() call
                placeAMove(point, 2); 
                scores.add(minimax(depth + 1, 1));
            }
            board[point.x][point.y] = 0; //Reset this point
        }
        return turn == 1 ? returnMax(scores) : returnMin(scores);
    }
}

//main class
public class TicTacToe {

    public static void main(String[] args) {
        Board b = new Board();//instantiate board
        Random rand = new Random();//instantiate random value

        b.displayBoard();//display board

        System.out.println("Who's gonna move first? (1)Computer (2)User: ");
        int choice = b.scan.nextInt();
        if(choice == 1){
            Point p = new Point(rand.nextInt(3), rand.nextInt(3));
            b.placeAMove(p, 1);
            b.displayBoard();
        }

        while (!b.isGameOver()) {
            System.out.println("Your move: ");
            Point userMove = new Point(b.scan.nextInt(), b.scan.nextInt());

            b.placeAMove(userMove, 2); //2 for X and X is the user
            b.displayBoard();
            if (b.isGameOver()) {
                break;
            } 
            b.callMinimax(0, 1);
            for (PointsAndScores pas : b.rootsChildrenScores) {
                System.out.println("Point: " + pas.point + " Score: " + pas.score);
            }
            b.placeAMove(b.returnBestMove(), 1);
            b.displayBoard();
        }
        if (b.hasXWon()) {
            System.out.println("Unfortunately, you lost!");
        } else if (b.hasOWon()) {
            System.out.println("You win! This is not going to get printed.");
        } else {
            System.out.println("It's a draw!");
        }
    }
}
4

1 回答 1

0

您需要引入一个整数变量,比如说 n,它将包含棋盘的大小(即单元数 = n*n)。游戏开始前,系统会询问玩家首选的棋盘尺寸,3 或 4,并创建相应的棋盘。为了让您的程序像使用 3x3 一样使用 4x4,我们需要将变量 n 放置在我们需要遍历电路板的方法或循环的任何位置。换句话说,我们将放置 n 而不是 3。这将“概括”我们的程序。例如,在 display 方法中,只要 i 和 j 小于 3,我们就有 2 个 for 循环。为了将我们的程序“推广”到半任意棋盘大小(3 或 4),我们在适当的位置放置一个 n的 3 个,这样只要 i 和 j 小于 n(3 或 4),循环就会运行。这种从 3 到 n 的变化将适用于我们有 3 表示棋盘大小的任何地方。我们需要更改的另一件事是检查方法 hasXWon() 和 hasOWon()。在这里,我们需要为板尺寸为 4 而不是 3 时添加一个额外的部分。这看起来或多或少与它的对应部分相同,唯一的区别是它将检查额外的行、列和更长的对角线存在于 4x4 板上。插入这些更改后,程序现在如下所示:4x4 板中存在的列和更长的对角线。插入这些更改后,程序现在如下所示:4x4 板中存在的列和更长的对角线。插入这些更改后,程序现在如下所示:

import java.util.*;
//defines the point where to place 1 or 2
class Point {

    int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "[" + x + ", " + y + "]";
    }
}

//defines the score per point -1,0,1
class PointsAndScores {

    int score;
    Point point;

    PointsAndScores(int score, Point point) {
        this.score = score;
        this.point = point;
    }
}

//defince the game board
class board {

    List<Point> availablePoints;
    Scanner scan = new Scanner(System.in);
    public int n;
    int[][] board;

    public board(int n) {
        this.n = n;
        board = new int[n][n];
    }

    public boolean isGameOver() {
        //Game is over is someone has won, or board is full (draw)
        return (hasXWon() || hasOWon() || getAvailableStates().isEmpty());
    }
    //check if X have won represented by 1
    public boolean hasXWon() {
        if(n==3){
            if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
                //System.out.println("O Diagonal Win");
                return true;
            }
            for (int i = 0; i < n; ++i) {
                if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
                        || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
                    // System.out.println("O Row or Column win");
                    return true;
                }
            }
            return false;
        }
        else {
            if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == board[3][3]&& board[0][0] == 1) || (board[0][3] == board[1][2] && board[0][3] == board[2][1] && board[0][3] == board[3][0] && board[0][3] == 1)) {
                //System.out.println("O Diagonal Win");
                return true;
            }
            for (int i = 0; i < n; ++i) {
                if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
                        || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
                    // System.out.println("O Row or Column win");
                    return true;
                }
            }
            return false;
        }
    }

    //check if O has won represented by 2
    public boolean hasOWon() {
        if(n==3){
            if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
                //System.out.println("O Diagonal Win");
                return true;
            }
            for (int i = 0; i < n; ++i) {
                if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
                        || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2))) {
                    // System.out.println("O Row or Column win");
                    return true;
                }
            }
            return false;
        }
        else {
            if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == board[3][3]&& board[0][0] == 2) || (board[0][3] == board[1][2] && board[0][3] == board[2][1] && board[0][3] == board[3][0] && board[0][3] == 2)) {
                //System.out.println("O Diagonal Win");
                return true;
            }
            for (int i = 0; i < n; ++i) {
                if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
                        || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2))) {
                    // System.out.println("O Row or Column win");
                    return true;
                }
            }
            return false;
        }
    }

    //check available states in the board
    public List<Point> getAvailableStates() {
        availablePoints = new ArrayList<>();
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] == 0) {
                    availablePoints.add(new Point(i, j));
                }
            }
        }
        return availablePoints;
    }

    //put player move in the board
    public void placeAMove(Point point, int player) {
        board[point.x][point.y] = player;   //player = 1 for O, 2 for X..
    }

    //get best movement according to the board state
    public Point returnBestMove() {
        int MAX = -100000;
        int best = -1;

        for (int i = 0; i < rootsChildrenScores.size(); ++i) { 
            if (MAX < rootsChildrenScores.get(i).score) {
                MAX = rootsChildrenScores.get(i).score;
                best = i;
            }
        }

        return rootsChildrenScores.get(best).point;
    }

    //accepts input from user
    void takeHumanInput() {
        System.out.println("Your move: ");
        int x = scan.nextInt();
        int y = scan.nextInt();
        Point point = new Point(x, y);
        placeAMove(point, 2); 
    }

    //display current board
    public void displayboard() {
        System.out.println();

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                System.out.print(board[i][j] + " ");
            }
            System.out.println();

        }
    }

    //get min value
    public int returnMin(List<Integer> list) {
        int min = Integer.MAX_VALUE;
        int index = -1;
        for (int i = 0; i < list.size(); ++i) {
            if (list.get(i) < min) {
                min = list.get(i);
                index = i;
            }
        }
        return list.get(index);
    }

    //get max value
    public int returnMax(List<Integer> list) {
        int max = Integer.MIN_VALUE;
        int index = -1;
        for (int i = 0; i < list.size(); ++i) {
            if (list.get(i) > max) {
                max = list.get(i);
                index = i;
            }
        }
        return list.get(index);
    }

    //declares a list for scores
    List<PointsAndScores> rootsChildrenScores;

    //excutes minimax algorithm
    public void callMinimax(int depth, int turn){
        rootsChildrenScores = new ArrayList<>();
        minimax(depth, turn);
    }

    //minimax algorithm
    public int minimax(int depth, int turn) {

        if (hasXWon()) return +1;
        if (hasOWon()) return -1;

        //get available states from the board
        List<Point> pointsAvailable = getAvailableStates();
        if (pointsAvailable.isEmpty()) return 0; 

        //stores scores
        List<Integer> scores = new ArrayList<>(); 

        for (int i = 0; i < pointsAvailable.size(); ++i) {
            Point point = pointsAvailable.get(i);  

            if (turn == 1) { //O's turn select the highest from below minimax() call
                placeAMove(point, 1); 
                int currentScore = minimax(depth + 1, 2);
                scores.add(currentScore);//add scores to the list

                if (depth == 0) 
                    rootsChildrenScores.add(new PointsAndScores(currentScore, point));

            } else if (turn == 2) {//X's turn select the lowest from below minimax() call
                placeAMove(point, 2); 
                scores.add(minimax(depth + 1, 1));
            }
            board[point.x][point.y] = 0; //Reset this point
        }
        return turn == 1 ? returnMax(scores) : returnMin(scores);
    }
}

//main class
public class TicTacToe {

    public static void main(String[] args) {
        //board b = new board();//instantiate board
        Random rand = new Random();//instantiate random value
        Point p;
        Scanner s = new Scanner(System.in);
        System.out.println("Choose board size: 3 or 4?");
        int n=s.nextInt();
        board b = new board(n);    //Instantiating board after value of n has been read. This is important because the value of n is required for the instantiation to take place.
        System.out.println(b.n);
        b.displayboard();//display board
        System.out.println("Who's gonna move first? (1)Computer (2)User: ");
        int choice = b.scan.nextInt();
        if(choice == 1){
            if(b.n==3)
                p = new Point(rand.nextInt(3), rand.nextInt(3));
            else 
                p = new Point(rand.nextInt(4), rand.nextInt(4));
            b.placeAMove(p, 1);
            b.displayboard();
        }

        while (!b.isGameOver()) {
            System.out.println("Your move: ");
            Point userMove = new Point(b.scan.nextInt(), b.scan.nextInt());

            b.placeAMove(userMove, 2); //2 for X and X is the user
            b.displayboard();
            if (b.isGameOver()) {
                break;
            } 
            b.callMinimax(0, 1);
            for (PointsAndScores pas : b.rootsChildrenScores) {
                System.out.println("Point: " + pas.point + " Score: " + pas.score);
            }
            b.placeAMove(b.returnBestMove(), 1);
            b.displayboard();
        }
        if (b.hasXWon()) {
            System.out.println("Unfortunately, you lost!");
        } else if (b.hasOWon()) {
            System.out.println("You win! This is not going to get printed.");
        } else {
            System.out.println("It's a draw!");
        }
    }
}

您的程序现在能够创建 4x4 游戏。但是,您需要做两件事。首先,您的 placeAMove(...) 方法在填充单元格之前不会检查单元格是否被占用。这可能会导致单元格被覆盖,因此您必须通过在尝试填充单元格之前检查单元格是否被占用来更改它。第二个也是更重要的一点是,虽然该程序现在能够创建 4x4 游戏,但它无法执行正确的游戏。原因是极小极大为寻找下一步行动而创建的节点(状态)的数量虽然在 3x3 游戏中是可控的,但在 4x4 游戏中却显着增加。这意味着您的计算机将花费大量时间,我的意思是数小时,来计算计算机的第二步。即使我操纵了游戏(即 在允许调用 minimax 来选择计算机的移动之前,手动在单元格中插入随机的 1 和 2)它仍然需要计算机超过 30 秒来计算它的移动。当然,这会使您的游戏无法在 4x4 模式下玩。当然也有办法,比如记忆化Alpha-Beta 修剪或两者结合,以克服这一点并加快您的算法。是一个先前提出的问题,可帮助您开始了解这些主题。国际象棋编程 wiki也是此类主题的良好信息来源,如果它的结构有点复杂的话。这是他们关于记忆的条目这里称为转置表)。

于 2015-12-02T19:40:48.273 回答