【问题标题】:minimax algorithm in javaScript is not working as expected and returns the wrong movejavaScript 中的 minimax 算法未按预期工作并返回错误的移动
【发布时间】:2020-10-10 16:53:39
【问题描述】:

我正在尝试使用极小极大算法在 javaScript 中制作井字游戏,但似乎我做错了什么,极小极大算法没有检测到最佳移动。代码如下:

const board = ["X", null, null, null, null, "X", "X", "O", "O"];
/*
    X   _   _
    _   _   X
    X   O   O

*/

// duplicate passed board and return the new board state
const makeAIMove = (currentBoard, square, player) => {
    const nextBoard = [...currentBoard];
    nextBoard[square] = player;
    return nextBoard;
};

// find empty squares
const emptySquares = (sqBoard) =>
    sqBoard
        .map((sq, idx) => (sq === null ? idx : null))
        .filter((sq) => sq !== null);

// check if no empty squares are available
const isFinished = (sqBoard) => (emptySquares(sqBoard).length ? false : true);

// check winner
const checkWinner = (sqBoard) => {
    const winConditions = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6],
    ];

    for (const winCondition of winConditions) {
        [a, b, c] = winCondition;
        if (sqBoard[a] && sqBoard[a] === sqBoard[b] && sqBoard[a] === sqBoard[c])
            return sqBoard[a];
    }

    return false;
};

// minimax algorithm
const minimax = (sqBoard, depth, isMaximizer) => {
    // terminal checker
    const theWinner = checkWinner(sqBoard);
    // we have a winner
    if (theWinner) {
        return theWinner === "X" ? -10 : 10;
    }
    // it's a tie
    if (isFinished(sqBoard)) {
        return 0;
    }

    let bestScore;
    if (isMaximizer) {
        bestScore = -1000;
        emptySquares(sqBoard).forEach((square) => {
            // make a sample move
            let nextBoard = makeAIMove(sqBoard, square, "O");

            // recursion
            let score = minimax(nextBoard, depth + 1, false);
            bestScore = Math.max(bestScore, score);
        });
    } else {
        bestScore = 1000;
        emptySquares(sqBoard).forEach((square) => {
            let nextBoard = makeAIMove(sqBoard, square, "X");
            let score = minimax(nextBoard, depth + 1, true);
            bestScore = Math.min(bestScore, score);
        });
    }
    return bestScore;
};

// find the best move
const nextBestMove = (sqBoard) => {
    let nextMoveArray = [];
    let remainedSquares = emptySquares(sqBoard);
    remainedSquares.forEach((square) => {
        let nextBoard = makeAIMove(sqBoard, square, "O");
        let theScore = minimax(nextBoard, 0, true);
        nextMoveArray.push({
            sq: square,
            sc: theScore,
        });
    });

    nextMoveSorted = nextMoveArray.sort((a, b) => (a.sc < b.sc ? 1 : -1));
    return nextMoveSorted[0].sq;
};

console.log(nextBestMove(board));

在上述情况下,最好的做法是阻止 X 获胜,方法是用“O”填充棋盘[3],但它总是会检测到另一个得分更高的动作。

谁能帮我理解我的代码出了什么问题?

谢谢。

【问题讨论】:

    标签: javascript algorithm recursion tic-tac-toe minimax


    【解决方案1】:

    从您的代码中,我了解到 X 是最小化玩家,O 是最大化玩家。但后来我看到了这段代码:

        let nextBoard = makeAIMove(sqBoard, square, "O");
        let theScore = minimax(nextBoard, 0, true);
    

    所以在 O 移动后,您调用 minimax 并将 isMaximizer 设置为 true。但这会使minimax 下一个O 动作,而O 已经下过。你想得到 X 的最佳回复动作,所以你应该在这里传递false

        let theScore = minimax(nextBoard, 0, false);
    

    现在,对于每个这样的调用(所以对于 O 的每一步),这将返回 -10,因为对于 O,游戏已经处于失败状态,无论它做什么,X 都会赢。如果 O 移动到 3,那么 X 将使用 2 进行双重攻击。

    如果你想区分快胜和慢胜,那么你应该在每次回溯时调整分数。

    例如,您可以将return bestScore 语句替换为一个接近零的值的返回值。所以例如 -10 变成 -9,5 变成 4,0 仍然是 0:

        return bestScore - Math.sign(bestScore);
    

    随着这一变化,O 将在 3 处下棋,因为它的得分是 -7(仍然输),而其他动作都得分 -9(从 X 开始一招立即输)。

    【讨论】:

    • 太棒了!我至少花了三个小时,但我没有注意到我连续两次将“O”传递给代码。非常感谢您快速而有帮助的回答。这让我很开心。
    猜你喜欢
    • 2016-08-13
    • 2022-11-04
    • 1970-01-01
    • 2018-06-16
    • 2023-03-20
    • 1970-01-01
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多