问题描述
const grabEmptySquares = (array) => {
var emptyGameSquares = [];
for (i = 0; i < 9; i++) {
if (!array[i]) emptyGameSquares.push(i);
}
return emptyGameSquares;
};
function findBestMove(board) {
var bestMove = {
index: null,evaluation: null,};
var availableMoves = grabEmptySquares(board);
availableMoves.forEach((move) => {
const simulGameboard = JSON.parse(JSON.stringify(board));
simulGameboard[move] = "o";
const evaluation = minimax(simulGameboard,1,false);
const moveDetails = {
index: move,evaluation: evaluation,};
console.log(moveDetails)
if (evaluation > bestMove.evaluation || bestMove.evaluation === null) {
bestMove.index = move;
bestMove.evaluation = evaluation;
}
});
return bestMove.index;
}
function evaluate(board,isMaximizingPlayer,depth) {
var gameStatus = isGameOver(board);
if (gameStatus[0] != true) return;
if (gameStatus[1] === "win")
return isMaximizingPlayer ? +10 - depth : -10 + depth;
if (gameStatus[1] === "tie") return 0;
}
function minimax(board,depth,isMaximizingPlayer) {
var gameStatus = isGameOver(board);
if (gameStatus[0] == true) {
const evaluation = evaluate(board,!isMaximizingPlayer,depth);
return evaluation;
}
var simulGameboard = JSON.parse(JSON.stringify(board));
var availableMoves = grabEmptySquares(simulGameboard);
if (isMaximizingPlayer) {
bestVal = -Infinity;
availableMoves.forEach((move) => {
depth % 2 === 0
? (simulGameboard[move] = "o")
: (simulGameboard[move] = "x");
value = minimax(simulGameboard,depth + 1,false);
bestVal = Math.max(bestVal,value);
const moveDetails = {
index: move,evaluation: bestVal,depth: depth,};
console.log(moveDetails);
});
return bestVal;
} else {
bestVal = Infinity;
availableMoves.forEach((move) => {
depth % 2 === 0
? (simulGameboard[move] = "o")
: (simulGameboard[move] = "x");
value = minimax(simulGameboard,true);
bestVal = Math.min(bestVal,};
console.log(moveDetails);
});
return bestVal;
}
}
function isGameOver(array) {
var gameOver = false;
if (
(array[0] && array[0] === array[1] && array[0] === array[2]) ||
(array[3] && array[3] === array[4] && array[3] === array[5]) ||
(array[6] && array[6] === array[7] && array[6] === array[8])
) {
return (gameOver = [true,"win"]);
}
if (
(array[0] && array[0] === array[4] && array[0] === array[8]) ||
(array[2] && array[2] === array[4] && array[2] === array[6])
) {
return (gameOver = [true,"win"]);
}
if (
(array[1] && array[1] === array[4] && array[4] === array[7]) ||
(array[0] && array[0] === array[3] && array[3] === array[6]) ||
(array[2] && array[2] === array[5] && array[5] === array[8])
) {
return (gameOver = [true,"win"]);
}
if ([...array].every((index) => index)) {
return (gameOver = [true,"tie"]);
}
return (gameOver = [false,null]);
}
我按照https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-3-tic-tac-toe-ai-finding-optimal-move/的指示,据我所知,逻辑是一样的。
仍然,我的代码没有提出正确的动作。我的 minimiax 函数对每一步的评估是错误的。这太错误了,我什至无法开始弄清楚代码在哪里关闭。请帮忙。过去两周我一直在研究这个。
例如:
var gameboard = [ null,"o",null,"x",null ]
If I run findBestMove(gameboard),the expected output should be
bestMove = {index: 5,evaluation: 0}
What I get instead is
bestMove = {index: 1,evaluation: -8}.
In fact,every single move has the same evaluation.
解决方法
这不是易于阅读的代码,但是 minimax
函数会复制游戏板状态一次,然后使用 availableMoves.forEach
遍历可能的移动。这意味着在评估每个可能的移动时,它就像之前考虑的每个移动已经一样。将副本移动到 forEach 中,事情应该更有意义。
您已经在 findBestMove
函数中使用了它。我强烈建议统一 findBestMove
和 minimax
(以及 isMaximizingPlayer
内的 minimax
分支的两侧)。在多个地方拥有非常相似的代码会让人很难记住你有什么地方有什么地方没有修复过。
我还建议将 isMaximizingPlayer
和 depth%2
逻辑替换为 player
变量,该变量可以是“x”或“o”,并将善良分数乘以 -1 作为需要。跟踪起来会更容易。