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

Minimax算法递归异常:我的井字棋实现未遍历所有状态

井字棋MiniMax算法未遍历所有游戏状态的问题分析与修复

问题概述

你的MiniMax函数能够返回包含index和score的对象,但无法遍历井字棋的所有可能游戏状态,示例执行后仅输出6行内容,递归未正常遍历所有分支。

核心错误原因

问题出在直接修改了传入的gameLayout数组且未做回溯处理。由于数组是JavaScript中的引用类型,在循环中修改数组后,后续的循环迭代和递归调用都会基于被修改后的数组执行,导致大量潜在的游戏状态被跳过——原本空的单元格被提前填充,后续emptyCells函数无法识别这些位置,自然不会探索对应的分支。

修复方案

有两种可行的修复方式,核心都是保证每个递归分支基于独立的游戏状态执行:

方式1:创建数组副本(推荐)

每次循环时创建原游戏布局的副本,修改副本而非原数组,这样不会影响后续分支的状态:

function miniMax (gameLayout, player) {
  const empty = emptyCells(gameLayout)
  if (checkWin(gameLayout) === 1) {
    return { score: 10 }
  } else if (checkWin(gameLayout) === -1) {
    return { score: -10 }
  } else if (empty.length === 0) {
    return { score: 0 }
  }
  const moves = []
  for (let i = 0; i < empty.length; i++) {
    const move = {}
    move.index = empty[i]
    // 创建原数组的副本,避免修改原布局
    const newLayout = [...gameLayout]
    newLayout[empty[i]] = player === 'O' ? 'O' : 'X'
    // 递归调用时传入副本,保证分支独立性
    move.score = player === 'O' ? miniMax(newLayout, human).score : miniMax(newLayout, ai).score
    moves.push(move)
  }
  let bestMove
  if (player === 'O') {
    bestMove = moves.reduce((acc, curr, i) => moves[acc].score > curr.score ? acc : i, 0)
  } else {
    bestMove = moves.reduce((acc, curr, i) => moves[acc].score < curr.score ? acc : i, 0)
  }
  console.log(JSON.stringify(moves))
  return moves[bestMove]
}

方式2:回溯恢复原数组

如果不想创建副本,可以在递归调用完成后,将修改的单元格恢复为空(回溯),还原原数组状态:

function miniMax (gameLayout, player) {
  const empty = emptyCells(gameLayout)
  if (checkWin(gameLayout) === 1) {
    return { score: 10 }
  } else if (checkWin(gameLayout) === -1) {
    return { score: -10 }
  } else if (empty.length === 0) {
    return { score: 0 }
  }
  const moves = []
  for (let i = 0; i < empty.length; i++) {
    const move = {}
    move.index = empty[i]
    // 修改原数组
    gameLayout[empty[i]] = player === 'O' ? 'O' : 'X'
    // 递归调用
    move.score = player === 'O' ? miniMax(gameLayout, human).score : miniMax(gameLayout, ai).score
    // 回溯:将单元格恢复为空,不影响后续分支
    gameLayout[empty[i]] = ''
    moves.push(move)
  }
  let bestMove
  if (player === 'O') {
    bestMove = moves.reduce((acc, curr, i) => moves[acc].score > curr.score ? acc : i, 0)
  } else {
    bestMove = moves.reduce((acc, curr, i) => moves[acc].score < curr.score ? acc : i, 0)
  }
  console.log(JSON.stringify(moves))
  return moves[bestMove]
}

修复效果

采用上述任意一种方式后,MiniMax算法会正确遍历所有可能的游戏状态,控制台输出的内容数量会对应所有分支的探索结果,递归逻辑恢复正常。

内容的提问来源于stack exchange,提问作者Дмитрий Орлов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:53:15