miniMax-Algorithm无法正常工作

时间:2016-05-17 16:14:58

标签: java tic-tac-toe minimax

UPDATE3: 我修好了。最后这是一些小错误(参见Update 1和Update2),最后一个错误非常重要:

public static List<int[]> generateMoves() {
    List<int[]> possibleMoves = new ArrayList<int[]>();

    if (notWon() == false) {
        return possibleMoves;
    }
    for (int row = 0; row < 3; ++row) {
        for (int col = 0; col < 3; ++col) {
            if (gameBoard[row][col] == 0) {
                possibleMoves.add(new int[]{row, col});
            }
        }
    }
    return possibleMoves;
}

我在这里犯了一个大错误,在行和col从0迭代到2之前,这意味着计算机实际上只有2x2字段可以使用。这解释了他的不良位置。 现在算法工作得很好,我是一个快乐的雪花。

UPDATE2: 我已经改变了条件运算符: 的

int maxValue = (player == computerTurn) ? Integer.MAX_VALUE : Integer.MIN_VALUE;

int maxValue = (player == computerTurn) ? Integer.MIN_VALUE : Integer.MAX_VALUE;

现在计算机确实进行了几次单独转弯(更改了行和列值),但它没有正确找到获胜的游戏状态(例如:)

 |   |   
 |   |   
 |   |   

1evaluation值 1row 1col

 |   |   
 |  X|   
 |   |   

玩家O,请输入一行(1-3):3 玩家O,请输入一栏(1-3):3

 |   |   
 |  X|   
 |   |  O

19evaluation值 0row 0col

X|   |   
 |  X|   
 |   |  O

玩家O,请输入一行(1-3):3 玩家O,请输入一栏(1-3):2

X|   |   
 |  X|   
 |  O|  O

10evaluation值 1row 0col

X|   |   
X|  X|   
 |  O|  O

玩家O,请输入一行(1-3):2 玩家O,请输入一栏(1-3):3

X|   |   
X|  X|  O
 |  O|  O

1evaluation值 0row 1col

X|  X|   
X|  X|  O
 |  O|  O

玩家O,请输入一行(1-3):1 玩家O,请输入一栏(1-3):3

X|  X|  O
X|  X|  O
 |  O|  O

玩家赢了! 玩家赢了!

----游戏结束了---

处理完成,退出代码为0

更新 好的,我已经在computerInput方法中进行了三次打印输出。

 System.out.println(bestMove[0] + "evaluation-value");
 System.out.println(bestMove[1]+ "row");
 System.out.println(bestMove[2] + "col");

这是奇怪的事情: 首次调用该函数时,它将通过所有minimax方法运行并评估最佳值。但是,它不会更改使用row = 1,col = 1初始化的行和列(col)值。如果我将它们更改为-1,则-1得到“IndexOutOfBound Exception”。这进一步证明了这些值在某种程度上没有使用minimax方法中的适当值。它们旨在在minimax方法的else语句期间重新赋值。有趣的是,控制台总是打印出评估的maxValue和row = 1,col = 1,这意味着计算机可以重复地将他的符号设置到同一个位置。这意味着计算机每转一圈都是相同的(1,1)。

我目前正在开发一个项目,该项目实现了一个带有继承AI(使用minimax)的“TicTacToe”游戏。我目前在寻找语义错误方面遇到了问题,不知怎的,计算机只进行了我的第一次转弯,我将其定义为bestRow = 1,bestCol = 1。似乎算法(miniMax),他选择了他最好的评估位置是不能正常工作的。

我的算法使用类型为int的二维数组(gameBoard)。 computerInput = 1,playerInput = -1,空表示0.我有第二个数组,我称之为consoleGameBoard,此板来自Char类型,使用X转换为gameBoard的数字条目为1,O为-1和空白( '')为0。

这是我的架构:

(0,0)(0,1)(0,2)

(1,0)(1,1)(1,2)

(2,0)(2,1)(2,2)

我真的希望有人可以帮助我... 我认为语义失误是在minimax-Method中的某个地方,因为bestRow和bestCol似乎没有更新,这意味着它们保持初始分配值(1,1)并且计算机转向基本上会重复整个时间。

我使用本教程作为miniMax算法实现的指南: https://www.ntu.edu.sg/home/ehchua/programming/java/JavaGame_TicTacToe_AI.html

Board.class和Console.class工作正常,我可以与两个人类玩家一起玩游戏而没有任何问题。语义失败在MiniMax.class中。

我的代码:

public class MiniMax extends Board {

static int searchDepth = 4;
static int computerTurn = 1;
static int playerTurn = -1;


public static int[] miniMax(int player, int depth) {
    List<int[]> possibleMoves = generateMoves();

    /////////////// Condition //// if condition = true /// if false = false
    int maxValue = (player == computerTurn) ? Integer.MIN_VALUE : Integer.MAX_VALUE; //  // conditional operator
    int bestRow = 1;
    int bestCol = 1;
    int currentValue;

    if (depth == 0 || possibleMoves.isEmpty()) {
        maxValue = evaluate();
    } else {
        for (int[] tryMove : possibleMoves) { // for-each
            gameBoard[tryMove[0]][tryMove[1]] = player;
            if (player == computerTurn) {
                currentValue = miniMax(playerTurn, depth - 1)[0];
                if (currentValue > maxValue) {
                    maxValue = currentValue;
                    bestRow = tryMove[0];
                    bestCol = tryMove[1];

                }
            } else {
                currentValue = miniMax(computerTurn, depth - 1)[0];
                if (currentValue < maxValue) {
                    maxValue = currentValue;
                    bestRow = tryMove[0];
                    bestCol = tryMove[1];

                }
            }
            undoMove(tryMove);

        }
    }

    return new int[]{maxValue, bestRow, bestCol};
}

/**
 * This method evaluates the expected value for each of the 8 possible connections.
 * 3 rows, 3 columns and 2 diagonals. (See checkForWin for further clarification).
 * It is called by the MiniMax Algorithm in order to find the best move.
 *
 * @return score, a product of the inherit method call.
 * It connects all -1 or 1 entries and sums them up.
 * <p>
 * Possible (sum-)Values for "score":
 * +100(3x), +10(2x), +1(1x) for Computer-Connections.
 * -100(3x), -10(2x), -1 (1x) for Player-Connections.
 * 0 for EMPTY-Spots.
 * <p>
 * Schema:
 * (0,0) | (0,1) | (0,2)
 * (1,0) | (1,1) | (1,2)
 * (2,0) | (2,1) | (2,2)
 */
private static int evaluate() {
    int score = 0;
    score = score + connection(0, 0, 0, 1, 0, 2); // rowOne
    score = score + connection(1, 0, 1, 1, 1, 2); // rowTwo
    score = score + connection(2, 0, 2, 1, 2, 2); // rowThree
    score = score + connection(0, 0, 1, 0, 2, 0); // colOne
    score = score + connection(0, 1, 1, 1, 2, 1); // colTwo
    score = score + connection(0, 2, 1, 2, 2, 2); // colThree
    score = score + connection(0, 0, 1, 1, 2, 2); // diagonal 1
    score = score + connection(0, 2, 1, 1, 2, 0); // diagonal 2
    return score;
}

/**
 * This method shall only be called by the evaluate() Method!
 * It reviews all possible Spots by applying all the possible combinations from the evaluate() method.
 * This Method connects each entry and checks is the currently active player can increase his score.
 *
 * @param rowOne
 * @param colOne
 * @param rowTwo
 * @param colTwo
 * @param rowThree
 * @param colThree
 * @return Score, a value that determines the most effective move.
 */
private static int connection(int rowOne, int colOne, int rowTwo, int colTwo, int rowThree, int colThree) {
    int score = 0;
    // -1 = computerInput
    // 1 = userInput

    // First Spot
    if (gameBoard[rowOne][colOne] == computerTurn) { // First Spot = computerInput
        score = 1;
    } else if (gameBoard[rowOne][colOne] == playerTurn) { // First Spot = playerInput
        score = -1;
    }
    // Second Spot
    if (gameBoard[rowTwo][colTwo] == computerTurn) { // Second Spot = computerInput
        if (score == 1) { // TwoConnected for computerInput
            score = 10;
        } else if (score == -1) {
            return 0;
        } else { // Spot is EMPTY
            score = 1;
        }
    } else if (gameBoard[rowTwo][colTwo] == playerTurn) { // Second Spot = playerInput
        if (score == -1) { // TwoConnected for playerInput
            score = -10;
        } else if (score == 1) {
            return 0;
        } else { // Spot is EMPTY
            score = -1;
        }
    }
    // Third Spot
    if (gameBoard[rowThree][colThree] == computerTurn) { // Third Spot = computerInput
        if (score > 0) { // First Spot and/or Second Spot connected for computerInput
            score = score * 10;
        } else if (score < 0) {
            return 0;
        } else { // First Spot and Second Spot are EMPTY
            score = 1;
        }
    } else if (gameBoard[rowThree][colThree] == playerTurn) { // Third Spot = playerInput
        if (score < 0) { // First Spot and/or Second Spot connected for playerInput
            score = score * 10;
        } else if (score > 1) {
            return 0;
        } else { // First Spot and Second Spot are EMPTY
            score = -1;
        }
    }
    return score;
}

/**
 * Removes the last entry on the gameBoard by changing its value to zero (0) which means EMPTY.
 *
 * @param tryMove
 */

public static void undoMove(int[] tryMove) { 
    gameBoard[tryMove[0]][tryMove[1]] = 0;

}

/**
 * @return possibleMoves, a ArrayList which is discovering the currently possible gameBoard situations.
 */
public static List<int[]> generateMoves() { 
    List<int[]> possibleMoves = new ArrayList<int[]>();

    if (notWon() == false) {
        return possibleMoves;
    }
    for (int row = 0; row < 2; ++row) {
        for (int col = 0; col < 2; ++col) {
            if (gameBoard[row][col] == 0) {
                possibleMoves.add(new int[]{row, col});
            }
        }
    }
    return possibleMoves;
}

/**
 * This method calls minimax and returns the best move possible.
 *
 * @return bestMove, the result of the minimax algorithm (maxValue).
 */
public static int[] computerInput() {
    int[] bestMove = miniMax(computerTurn, searchDepth); // player, depth
    updateBoard(computerTurn, bestMove[1], bestMove[2]);
    convertToChar(bestMove[1], bestMove[2]);

    return new int[]{bestMove[1], bestMove[2]}; // 0 = maxValue, 1 = row, 2 = col.

}

}







public class Board {

public static int[][] gameBoard;
private static char[][] consoleGameBoard;
private static boolean continueGame = true;

/**
 * This is the constructor for the Board-Class
 */
public Board() {
    gameBoard = new int[3][3];
    consoleGameBoard = new char[3][3];

    for (int row = 0; row < gameBoard.length; row++) {
        Arrays.fill(gameBoard[row], 0);
    }
} // end of constructor

public void displayBoard() {
    for (int row = 0; row < consoleGameBoard.length; row++) {
        for (int col = 0; col < consoleGameBoard.length; col++) {
            System.out.print("\t" + consoleGameBoard[row][col]);
            if (col == 0 || col == 1)
                System.out.print("|");

        }
        System.out.print("\n_________________\n");
    }
}


public static boolean updateBoard(int player, int row, int col) {
    if (row >= 0 && row <= 2 && col >= 0 && col <= 2) {
        if (gameBoard[row][col] != 0) {
            return false;
        } else {
            gameBoard[row][col] = player;
            return true;
        }
    } else {
        return false;
    }
}

public void userInput(int player) {
    int row, col;
    Scanner keyboard = new Scanner(System.in);

    do {

        System.out.printf("Player %s, please enter a row (1-3):", 'O');
        row = keyboard.nextInt();
        System.out.printf("Player %s, please enter a column (1-3):", 'O');
        col = keyboard.nextInt();

    } while (notValid(row, col));
    updateBoard(player, row - 1, col - 1);
    convertToChar(row - 1, col - 1);
}

public static boolean notValid(int row, int col) {
    if (row > 3 || row < 1 || col > 3 || col < 1 || !isBlank(row, col)) {
        return true;
    } else {
        return false;
    }
}

public static boolean notFull() {
    if (gameBoard[0][0] != 0 && gameBoard[0][1] != 0 && gameBoard[0][2] != 0 && gameBoard[1][0] != 0 && gameBoard[1][1] != 0 && gameBoard[1][2] != 0 &&
            gameBoard[2][0] != 0 && gameBoard[2][1] != 0 && gameBoard[2][2] != 0) {
        return false;
    } else {
        return true;
    }
}

public static boolean isBlank(int row, int col) {
    if (gameBoard[row - 1][col - 1] == 0) {
        return true;
    } else {
        System.out.println(" -- INVALID! The Position is already taken! --");
        System.out.println(" ------------- Make another Move -------------");
        return false;
    }
}

public static boolean notWon() {
    // loop over each row and check for winner
    for (int row = 0; row < gameBoard.length; row++) {
        if (gameBoard[row][0] + gameBoard[row][1] +  gameBoard[row][2] == 3 ||gameBoard[row][0] + gameBoard[row][1] +  gameBoard[row][2] == -3) {
            System.out.println("Player " + consoleGameBoard[row][0] + " has won!");
            return continueGame = false;
        }
    }
    // loop over each column and check for winner
    for (int col = 0; col < gameBoard[0].length; col++) {
        if (gameBoard[0][col] + gameBoard[1][col] + gameBoard[2][col] == 3 || gameBoard[0][col] + gameBoard[1][col] + gameBoard[2][col] == -3) {
            System.out.println("Player " + consoleGameBoard[col][0] + " has won!");
            return continueGame = false;
        }
    }
    // check diagonal 1
    if (gameBoard[0][0] + gameBoard[1][1] + gameBoard[2][2] == 3 || gameBoard[0][0] + gameBoard[1][1] + gameBoard[2][2] == -3) {
        System.out.println("Player " + consoleGameBoard[0][0] + " has won!");
        return continueGame = false;
    }
    // check for diagonal 2
    if (gameBoard[2][0] + gameBoard[1][1] + gameBoard[0][2] == 3 ||gameBoard[2][0] + gameBoard[1][1] + gameBoard[0][2] == -3) {
        System.out.println("Player " + consoleGameBoard[2][0] + " has won!");
        return continueGame = false;
    } else {
        return continueGame = true;
    }
}

public static void convertToChar(int row, int col) {
    char X = 'X';
    char O = 'O';

    if (gameBoard[row][col] == 1) {
        consoleGameBoard[row][col] = X;
    } else {
        consoleGameBoard[row][col] = O;
    }
}


}







public class Console {

public static void main(String[] args) {

    structurePack.Board myGame = new structurePack.Board();

    int playerSwitch = 2;

    myGame.displayBoard();

    while (myGame.notWon() == true) {
        if (playerSwitch % 2 == 0)
            computePack.MiniMax.computerInput();
        else {
            myGame.userInput(-1);

        }
        playerSwitch++;
        System.out.println("\n");
        myGame.displayBoard();
        myGame.notWon();
        if (myGame.notWon() == false) {
            System.out.println("\n");
            System.out.println(" ---- The Game is OVER --- ");
            break;
        }
        myGame.notFull();
        if (myGame.notFull() == false) {
            System.out.println("\n");
            System.out.println(" --- This Game is a DRAW --- ");
            break;
        }  

    }

}
}

0 个答案:

没有答案