Minimax 井字棋问题;

huangapple go评论73阅读模式
英文:

Minimax Tic-Tac-Toe issue;

问题

我正在制作一个井字游戏,并尝试为其实现minimax人工智能。如果ai先手而玩家后手,一切都运行良好;但如果ai后手,它只会按照一种模式进行[0][0] => [0][1] => [0][2]等。如果玩家已经填充了这个模式,它会跳过并继续相同的顺序。我对这类东西相当陌生,一直在为将其提升到这一点而努力。希望能得到一些建议。

英文:

I'm currently making a Tic-Tac-Toe game and trying to implement the minimax ai for it. Everything is working fine if the ai is going first and the player is going second, but if ai is second it just goes in a pattern [0][0] => [0][1] => [0][2] etc. If the human already filled this pattern it just skips over and continues the same sequence. I am pretty new to this kind of stuff and been struggling with it for a while to get it up to this point. Would appreciate some advice;

function eveluateMove(board) {
    for (let row = 0; row < board.length; row += 1) {
        if (board[row][0] === board[row][1] && board[row][1] === board[row][2]) {
            if (board[row][0] === 1) {
                return +10;
            } if (board[row][0] === 2) {
                return -10;
            }
        }
    }

    for (let column = 0; column < board.length; column += 1) {
        if (board[0][column] === board[1][column] && board[1][column] === board[2][column]) {
            if (board[0][column] === 1) {
                return +10;
            } if (board[0][column] === 2) {
                return -10;
            }
        }
    }

    if (board[0][0] === board[1][1] && board[1][1] === board[2][2]) {
        if (board[0][0] === 1) {
            return +10;
        } if (board[0][0] === 2) {
            return -10;
        }
    }
    if (board[0][2] === board[1][1] && board[1][1] === board[2][0]) {
        if (board[0][2] === 1) {
            return +10;
        } if (board[0][2] === 2) {
            return -10;
        }
    } return 0;
}

function minimax(board, depth, isMaximizer) {
    const score = eveluateMove(board);

    if (score === 10) {
        return score;
    }
    if (score === -10) {
        return score;
    }

    if (isMovesLeft() === false) {
        return 0;
    }

    if (isMaximizer) {
        let best = -1000;

        for (let row = 0; row < board.length; row += 1) {
            for (let column = 0; column < board.length; column += 1) {
                if (board[row][column] === 0) {
                    board[row][column] = 1;
                    best = Math.max(best, minimax(board, depth + 1, false));
                    board[row][column] = 0;
                }
            }
        } return best;
    }

    if (!isMaximizer) {
        let best = 1000;

        for (let row = 0; row < board.length; row += 1) {
            for (let column = 0; column < board.length; column += 1) {
                if (board[row][column] === 0) {
                    board[row][column] = 2;
                    best = Math.min(best, minimax(board, depth + 1, true));
                    board[row][column] = 0;
                }
            }
        } return best;
    }
}

const makeMove = (row, column) => ({ row, column });

function findBestMove(board) {
    let bestValue = -Infinity;
    const bestMove = makeMove;
    bestMove.row = -1;
    bestMove.column = -1;

    for (let row = 0; row < board.length; row += 1) {
        for (let column = 0; column < board.length; column += 1) {
            if (board[row][column] === 0 && aiWeapon === 1) {
                board[row][column] = aiWeapon;
                const moveValue = minimax(board, 0, false);
                board[row][column] = 0;
                if (moveValue > bestValue) {
                    bestMove.row = row;
                    bestMove.column = column;
                    bestValue = moveValue;
                }
            } if (board[row][column] === 0 && aiWeapon === 2) {
                board[row][column] = aiWeapon;
                const moveValue = minimax(board, 0, true);
                board[row][column] = 0;
                if (moveValue > bestValue) {
                    bestMove.row = row;
                    bestMove.column = column;
                    bestValue = moveValue;
                }
            }
        }
    } return bestMove;
}

function isMovesLeft() {
    let movesAvailable = true;
    const movesLeftR1 = board[0].every((value) => value > 0);
    const movesLeftR2 = board[1].every((value) => value > 0);
    const movesLeftR3 = board[2].every((value) => value > 0);
    if (movesLeftR1 === true && movesLeftR2 === true && movesLeftR3 === true) {
        movesAvailable = false;
    } return movesAvailable;
}

I am assuming that the issue is with the function findBestMove, since the minimax and the evaluation parts have to be running correctly for it to work in a situation where the ai makes the first move.

I've tried changing values from the call moveValue = minimax(board, 0, true); but that seems to have no effect.

Other than that I can't seem to put my finger on it, it has to be something with this one line in my head maybe I'm not seeing something.

答案1

得分: 1

findBestMove 函数中,存在至少一个问题,即在 AI 玩家是第二个玩家时,您的代码在最大化值,但在这种情况下,算法应该最小化值,因为第二个玩家希望获得尽可能小的值。

所以进行以下更改:

function findBestMove(board) {
    let bestValue = aiWeapon === 1 ? -Infinity : +Infinity; // <---
    const bestMove = makeMove;
    bestMove.row = -1;
    bestMove.column = -1;

    for (let row = 0; row < board.length; row += 1) {
        for (let column = 0; column < board.length; column += 1) {
            if (board[row][column] === 0 && aiWeapon === 1) {
                board[row][column] = aiWeapon;
                const moveValue = minimax(board, 0, false);
                board[row][column] = 0;
                if (moveValue > bestValue) {
                    bestMove.row = row;
                    bestMove.column = column;
                    bestValue = moveValue;
                }
            }
            if (board[row][column] === 0 && aiWeapon === 2) {
                board[row][column] = aiWeapon;
                const moveValue = minimax(board, 0, true);
                board[row][column] = 0;
                if (moveValue < bestValue) {  // <---
                    bestMove.row = row;
                    bestMove.column = column;
                    bestValue = moveValue;
                }
            }
        }
    }
    return bestMove;
}

在您的代码中存在相当多的代码重复。例如,上面的两个 if 块可以合并为一个...

作为参考,之前我曾发布了一个类似问题的 JavaScript 实现,它使用的代码要少得多。

英文:

There is at least one problem in findBestMove, where your code is maximizing the value even when the AI player is the second player. But in that case the algorithm should minimize the value, as the second player wants the least possible value.

So make this change:

function findBestMove(board) {
    let bestValue = aiWeapon === 1 ? -Infinity : +Infinity; // <---
    const bestMove = makeMove;
    bestMove.row = -1;
    bestMove.column = -1;

    for (let row = 0; row < board.length; row += 1) {
        for (let column = 0; column < board.length; column += 1) {
            if (board[row][column] === 0 && aiWeapon === 1) {
                board[row][column] = aiWeapon;
                const moveValue = minimax(board, 0, false);
                board[row][column] = 0;
                if (moveValue > bestValue) {
                    bestMove.row = row;
                    bestMove.column = column;
                    bestValue = moveValue;
                }
            }
            if (board[row][column] === 0 && aiWeapon === 2) {
                board[row][column] = aiWeapon;
                const moveValue = minimax(board, 0, true);
                board[row][column] = 0;
                if (moveValue < bestValue) {  // <---
                    bestMove.row = row;
                    bestMove.column = column;
                    bestValue = moveValue;
                }
            }
        }
    }
    return bestMove;
}

There is quite some code repetition in your code. Like for instance the above two if blocks could be merged into one...

For reference, some time ago I posted a JavaScript implementation to a similar question. It uses much less code.

huangapple
  • 本文由 发表于 2023年3月3日 21:11:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75627503.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定