用于TicTacToe游戏的Minimax功能无法正常工作。我总是赢

时间:2018-02-15 19:57:33

标签: javascript tic-tac-toe minimax

我正在尝试使用minimax算法在JavaScript中实现TicTacToe游戏。我不能为我的生活弄清楚我的代码有什么问题。调试递归函数很难: - /嗯,这是完整的代码,我觉得需要包含所有这些,包括GUI逻辑。其中一些有点乱,我没有正确抽象一切 - 但除了minimax功能以外的所有其他工作正常。

var cnv, ctx, x_spacing, y_spacing;
var board = [0,1,2,3,4,5,6,7,8];
var player = 'X', ai = 'O';

window.onload = function() {
    cnv = document.getElementById("cnv");
    ctx = cnv.getContext("2d");

    x_spacing = cnv.width / 3;
    y_spacing = cnv.height / 3;

    drawGrid();

    cnv.addEventListener("click", playerMove);
}

function drawGrid() {
    ctx.lineWidth = 4;
    ctx.strokeStyle = '#fff';
    ctx.beginPath();

    for (let i=1; i<3; i++) {
        ctx.moveTo(i*x_spacing, 4);
        ctx.lineTo(i*x_spacing, cnv.height-4);
        ctx.moveTo(4, i*y_spacing);
        ctx.lineTo(cnv.width-4, i*y_spacing);
    }

    ctx.stroke();
}

function playerMove(evt) {
    var rect = cnv.getBoundingClientRect();
    var x = Math.floor((evt.clientX - rect.left) / x_spacing);
    var y = Math.floor((evt.clientY - rect.top) / y_spacing);
    var spot = y*3 + x;

    if (board[spot] === player || board[spot] === ai) return false;

    board[spot] = 'X';

    drawX(x,y);
    var bestMove = minimax(board, 0, true);

    board[bestMove.index] = 'O';

    y = Math.floor(bestMove.index / 3);
    x = bestMove.index % 3;
    drawO(x,y);
}

function drawX(x, y) {
    ctx.beginPath();
    ctx.moveTo(x * x_spacing + 10, y * y_spacing + 10);
    ctx.lineTo((x+1) * x_spacing - 10, (y+1) * y_spacing - 10);
    ctx.moveTo(x * x_spacing + 10, (y+1) * y_spacing - 10);
    ctx.lineTo((x+1) * x_spacing - 10, y * y_spacing + 10);
    ctx.stroke();
}

function drawO(x, y) {
    ctx.beginPath();
    ctx.arc((x + 0.5) * x_spacing, (y + 0.5) * y_spacing,
         (x_spacing/2)-10,0, 2*Math.PI);
    ctx.stroke();
}

function getAvailSpots(board) {
    return board.filter(spot => spot !== ai && spot !== player);
}

function minimax(board, depth, isMaximizer) {
    if (checkIfWin(ai)) return { score: 10 - depth }; 
    else if (checkIfWin(player)) return { score: depth -10 };

    var availSpots = getAvailSpots(board);
    if (!availSpots.length) return  { score: 0 };

    var bestScore = (isMaximizer) ? -Infinity : Infinity;
    var bestMove;

    availSpots.forEach(spot => {
        // make move on board
        board[spot] = (isMaximizer) ? ai : player;

        var result = minimax(board, depth+1, !isMaximizer);

        bestScore = (isMaximizer)
            ? Math.max(result.score, bestScore)
            : Math.min(result.score, bestScore);

        bestMove = { index: spot, score: bestScore }

        // undo move on board
        board[spot] = spot;
    });

    return bestMove;
}

function checkIfWin(p) {
    if (
        (board[0] == p && board[1] == p && board[2] == p) ||
        (board[3] == p && board[4] == p && board[5] == p) ||
        (board[6] == p && board[7] == p && board[8] == p) ||
        (board[0] == p && board[3] == p && board[6] == p) ||
        (board[1] == p && board[4] == p && board[7] == p) ||
        (board[2] == p && board[5] == p && board[8] == p) ||
        (board[0] == p && board[4] == p && board[8] == p) ||
        (board[2] == p && board[4] == p && board[6] == p)
    ) return true;

    return false;
}

问题是minimax选择了非常差的点并且我总是赢,这肯定不是目的 - 我试图实现无与伦比的TicTacToe AI!<​​/ p>

1 个答案:

答案 0 :(得分:0)

已解决!事实证明我的极小极大算法是错误的,因为它总是选择板上最后一个可用的位置。我忘记在存储最佳移动时添加一些条件逻辑,如下所示:

function minimax(board, depth, aiTurn) {
    if (checkIfWin(ai)) return { score: 10 - depth }; 
    else if (checkIfWin(player)) return { score: depth -10 };

    var availSpots = getAvailSpots(board);
    if (!availSpots.length) return  { score: 0 };

    var bestScore = (aiTurn) ? -Infinity : Infinity;
    var bestMove;

    availSpots.forEach(spot => {
        // make move on board
        board[spot] = (aiTurn) ? ai : player;

        var result = minimax(board, depth+1, !aiTurn);

        if (aiTurn) { 
            if (result.score > bestScore) {
                bestScore = result.score;
                bestMove = { index: spot, score: bestScore };
            }
        }
        else {
            if (result.score < bestScore) {
                bestScore = result.score;
                bestMove = { index: spot, score: bestScore };
            }
        }

        board[spot] = spot;
    });

    return bestMove;
}