4x4 TicTacToe 보드의 Minimax 알고리즘 (Minimax algorithm in 4x4 TicTacToe board)


문제 설명

4x4 TicTacToe 보드의 Minimax 알고리즘 (Minimax algorithm in 4x4 TicTacToe board)

저는 Minimax 알고리즘을 사용하여 TicTacToe 4X4를 개발하는 인공 지능 프로젝트에서 일하고 있습니다.

Minimax 알고리즘을 실행하는 기존 프로그램이 있습니다. 3x3 TicTacToe 보드.

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!");
        }
    }
}

참조 솔루션

방법 1:

You need to introduce an integer variable, let's say n, which will contain the size of the board(i.e. # of cells = n*n). Before a game starts, the player will be asked for the preferred board size, 3 or 4, and the corresponding board will be created. In order for your program to work with 4x4 like it did with 3x3, we will need to place the variable n wherever we have a method or loop that needs to traverse through the board. In other words, instead of 3 we will place n. This will "generalize" our program. For example, within the display method we have 2 for loops that run as long as i and j are smaller than 3. To "generalize" our program to a semi‑arbitrary board size(3 or 4), we place an n in place of the 3 so that the loop will run as long as i and j are smaller than n(3 or 4). This change from 3 to n will apply wherever we have a 3 that indicates the size of the board. Another thing we will need to change are the checking methods hasXWon() and hasOWon(). Here, we will need to add an extra section for when the board size is 4 and not 3. This will look more or less the same as it's counterpart with the only difference being that it will check the extra row, column and longer diagonals that exist in a 4x4 board. After inserting these changes, the program now looks like this:

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!");
        }
    }
}

Your program is now able to create 4x4 games. There are however two things that you need to do. First, your placeAMove(...) method doesn't check if a cell is occupied before filling it. This may lead to cells being overwritten, so you must change that by checking if a cell is occupied before trying to fill it. The second and more important thing is that although the program is now able to create 4x4 games, it will not be able to carryout a proper game. The reason for this is that the number of nodes(states) created by minimax to find the next move, although manageable in 3x3 games, rises dramatically for 4x4 games. This means that it would take your computer A LOT of time, and by a lot I mean hours, to calculate the computer's second move. Even when I rigged the game(i.e. manually inserted random 1s and 2s in the cells before allowing minimax to be called to choose the computer's move) it still took the computer over 30 seconds to calculate it's move. This renders your game, of course, unplayable in 4x4 mode. There are, of course, ways, such as Memoization or Alpha‑Beta Pruning or both together, to overcome this and speed up your algorithm. Here is a previously asked question to get you started on these topics. The chess programming wiki is also a good source of information on such topics, if a bit more complex in it's structure. Here's their entry on Memoization(Here called transposition tables).

(by MaherOmar Sharaki)

참조 문서

  1. Minimax algorithm in 4x4 TicTacToe board (CC BY‑SA 2.5/3.0/4.0)

#Artificial-Intelligence #minmax #algorithm #java #tic-tac-toe






관련 질문

C++의 Langtons Ant(콘솔) - 코어 덤프됨 (Langtons Ant in C++ (console) - core dumped)

그리드 맵 탐색/채우기 알고리즘 (Algorithm for exploring/filling grid map)

사이먼 마크 투 오픈 소스 코드 (cyman mark two open souce code)

가위바위보 AI 이슈 (Rock paper scissors AI issue)

::pause >nul은 무엇을 의미합니까? (배치로) (What does ::pause >nul mean/do? (in batch))

4x4 TicTacToe 보드의 Minimax 알고리즘 (Minimax algorithm in 4x4 TicTacToe board)

타의 추종을 불허하는 minimax 검사기 알고리즘 (Unbeatable minimax checkers algorithm)

기계 학습을 사용하여 손으로 쓴 서명 이미지의 배경 제거 (Using machine learning to remove background in image of hand-written signature)

유효한 인덱스를 반환하지 않는 Tensorflow 텍스트 생성 (Tensorflow text generation not returning valid index)

NLP의 의미론적 유사성(텍스트 비교) - 최고의 패키지 또는 클라우드 서비스? (Semantic similarity (text comparison) in NLP - best package or cloud service?)

Android 애플리케이션에 RASA 챗봇 통합 (Integration of RASA Chatbot on Android Application)

Python과 함께 스레딩을 사용할 때 발생하는 코루틴 오류, RuntimeWarning: 코루틴 'time_messege'가 기다리지 않았습니다. (Coroutine error when using threading with python, RuntimeWarning: coroutine 'time_messege' was never awaited)







코멘트