You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

井字棋Minimax算法JavaScript实现异常:AI无法选最优落子

井字棋Minimax算法AI无法智能决策问题

我花了好几个小时给井字棋实现Minimax算法作为AI,但AI根本不会做智能决策,只会选第一个可行的落子位置。这是我第一次实现这类算法,参考了GeeksforGeeks的文章和YouTube视频。其中'O'是最大化玩家(AI),'X'是最小化玩家(人类)。findBestMove函数接收棋盘数组,返回最优落子的索引,用来更新网页上的可视化棋盘。代码如下:

const check = (n1, n2, n3) => {
    if (n1 === '') return;
    if (n1 === n2 && n2 === n3) {
      value = n1;
    }
  }

  const checkResult = () => {
    // Check all possible win scenarios
    check(board[0][0], board[0][1], board[0][2]);
    check(board[1][0], board[1][1], board[1][2]);
    check(board[2][0], board[2][1], board[2][2]);
    check(board[0][0], board[1][0], board[2][0]);
    check(board[0][1], board[1][1], board[2][1]);
    check(board[0][2], board[1][2], board[2][2]);
    check(board[0][0], board[1][1], board[2][2]);
    check(board[0][2], board[1][1], board[2][0]);
    if (value === 'X') {
      return -10;
    } else if (value === 'O') {
      return 10;
    }
  }

function findBestMove(board) {

  let bestMove = {};
  let bestVal = -Infinity;

  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {

      if (board[i][j] === '') {

        board[i][j] = 'O';
        let moveVal = minimax(board, 0, false);
        board[i][j] = '';

        if (moveVal > bestVal) {
          bestVal = moveVal;
          bestMove = { i, j };
        }

      }
    }
  }

  return bestMove;
}

function minimax(board, depth, maxPlayer) {

  let score = GameBoard.checkResult();

  if (!!score) {

    if (score < 0) {
      return score + depth;
    } else {
      return score - depth;
    }

  }
  if (!isMovesLeft(board)) {
    return 0;
  }

  if (maxPlayer) {

    let maxEval = -Infinity;
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        
        if (board[i][j] === '') {

          board[i][j] = 'O';
          let eval = minimax(board, depth + 1, false);
          board[i][j] = '';
          maxEval = Math.max(maxEval, eval);
          console.log(maxEval + ' ' + eval);

        }
      }
    }

    return maxEval;

  } else {

    let minEval = Infinity;
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {

        if (board[i][j] === '') {

          board[i][j] = 'X';
          let eval = minimax(board, depth + 1, true);
          board[i][j] = '';
          minEval = Math.min(minEval, eval);

        }
      }
    }

    return minEval;

  }
}

function isMovesLeft(board) {
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (board[i][j] === '') {
        return true;
      }
    }
  }
  return false;
}

问题根源

你的代码存在三个核心错误,导致Minimax算法无法正确评估局面:

  1. 全局变量value污染:check函数使用未声明的全局变量value,每次调用checkResult时不会重置该变量,残留的旧值会导致获胜判断完全错误。
  2. checkResult上下文错误:函数直接引用外部全局board变量,而非Minimax递归中传入的模拟棋盘,导致评估的始终是原始棋盘状态。
  3. checkResult返回逻辑缺失:无获胜情况时函数未返回值,虽不影响布尔判断,但结合全局变量问题会进一步加剧评估混乱。

修复后的代码

// 重构check函数,直接返回获胜者或null,避免全局变量
const check = (n1, n2, n3) => {
  if (n1 === '') return null;
  return n1 === n2 && n2 === n3 ? n1 : null;
};

// 重构checkResult,接收棋盘参数,遍历所有获胜模式
const checkResult = (board) => {
  const winPatterns = [
    [0,0, 0,1, 0,2], [1,0, 1,1, 1,2], [2,0, 2,1, 2,2],
    [0,0, 1,0, 2,0], [0,1, 1,1, 2,1], [0,2, 1,2, 2,2],
    [0,0, 1,1, 2,2], [0,2, 1,1, 2,0]
  ];

  for (const [x1,y1,x2,y2,x3,y3] of winPatterns) {
    const winner = check(board[x1][y1], board[x2][y2], board[x3][y3]);
    if (winner) {
      return winner === 'X' ? -10 : 10;
    }
  }
  return null;
};

function findBestMove(board) {
  let bestMove = {};
  let bestVal = -Infinity;

  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (board[i][j] === '') {
        board[i][j] = 'O';
        const moveVal = minimax(board, 0, false);
        board[i][j] = '';

        if (moveVal > bestVal) {
          bestVal = moveVal;
          bestMove = { i, j };
        }
      }
    }
  }

  return bestMove;
}

function minimax(board, depth, maxPlayer) {
  const score = checkResult(board);

  if (score !== null) {
    // 深度调整分数:优先选择最快获胜/最慢失败的路径
    return score < 0 ? score + depth : score - depth;
  }

  if (!isMovesLeft(board)) {
    return 0; // 平局
  }

  if (maxPlayer) {
    let maxEval = -Infinity;
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        if (board[i][j] === '') {
          board[i][j] = 'O';
          const eval = minimax(board, depth + 1, false);
          board[i][j] = '';
          maxEval = Math.max(maxEval, eval);
        }
      }
    }
    return maxEval;
  } else {
    let minEval = Infinity;
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        if (board[i][j] === '') {
          board[i][j] = 'X';
          const eval = minimax(board, depth + 1, true);
          board[i][j] = '';
          minEval = Math.min(minEval, eval);
        }
      }
    }
    return minEval;
  }
}

function isMovesLeft(board) {
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (board[i][j] === '') {
        return true;
      }
    }
  }
  return false;
}

修复说明

  1. 移除全局变量:通过让check函数直接返回结果,彻底避免全局变量污染问题。
  2. 绑定棋盘上下文:checkResult现在接收传入的棋盘参数,确保递归中每个模拟局面都被正确评估。
  3. 优化获胜判断逻辑:用数组存储所有获胜坐标模式,代码更清晰易维护。
  4. 修正Minimax调用:将GameBoard.checkResult()改为checkResult(board),确保使用当前递归的棋盘状态。

修改后,AI会正确执行Minimax算法,优先选择能直接获胜的位置,其次阻止人类获胜,最终实现智能落子。

内容的提问来源于stack exchange,提问作者Zack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 17:00:04