无法获得 minimax 功能以适用于井字游戏

时间:2021-04-06 23:26:44

标签: javascript algorithm tic-tac-toe minimax

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, depth + 1, true);
      bestVal = Math.min(bestVal, value);

      const moveDetails = {
        index: move,
        evaluation: bestVal,
        depth: depth,
      };
      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", "x", null, null, null, 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. 

1 个答案:

答案 0 :(得分:1)

这不是易于阅读的代码,但是 minimax 函数会复制游戏板状态一次,然后使用 availableMoves.forEach 遍历可能的移动。这意味着在评估每个可能的移动时,它就像之前考虑的每个移动已经一样。将副本移动到 forEach 中,事情应该更有意义。

您已经在 findBestMove 函数中使用了它。我强烈建议统一 findBestMoveminimax(以及 isMaximizingPlayer 内的 minimax 分支的两侧)。在多个地方拥有非常相似的代码会让人很难记住你有什么地方有什么地方没有修复过。

我还建议将 isMaximizingPlayerdepth%2 逻辑替换为 player 变量,该变量可以是“x”或“o”,并将善良分数乘以 -1 作为需要。跟踪起来会更容易。