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

Flutter中Minimax算法在4/5/6格井字棋中无限运行问题求助

井字棋Minimax算法在大棋盘(4x4/5x5/6x6)上无限运行问题排查

我在Flutter中实现了一款简易井字棋游戏,采用Minimax算法计算AI玩家的走法。该算法在3x3棋盘上运行完全正常,但适配到4x4、5x5、6x6棋盘时,即便添加了alpha-beta剪枝优化,Minimax函数仍会无限运行,无法返回有效走法。以下是核心代码:

核心函数

Minimax算法实现

int minimax(List<List<String>> board, bool isMaximizing, BuildContext context, int alpha, int beta) {
  // 检查终端状态
  if (checkWin(board, 'O', context)) {
    return 1;
  } else if (checkWin(board, 'X', context)) {
    return -1;
  } else if (checkDraw(board)) {
    return 0;
  }

  // 递归遍历游戏树
  if (isMaximizing) {
    int bestScore = -1000;
    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < 4; j++) {
        if (board[i][j].isEmpty) {
          board[i][j] = 'O';
          int score = minimax(board, false, context, alpha, beta);
          board[i][j] = '';
          bestScore = max(score, bestScore);

          alpha = max(alpha, score);
          if (beta <= alpha) {
            break; // 剪枝子树
          }
        }
      }
    }
    return bestScore;
  } else {
    int bestScore = 1000;
    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < 4; j++) {
        if (board[i][j].isEmpty) {
          board[i][j] = 'X';
          int score = minimax(board, true, context, alpha, beta);
          board[i][j] = '';
          bestScore = min(score, bestScore);

          beta = min(beta, score);
          if (beta <= alpha) {
            break; // 剪枝子树
          }
        }
      }
    }
    return bestScore;
  }
}

胜负判断函数

bool checkWin(List<List<String>> board, String player, BuildContext context) {
  for (int i = 0; i < 4; i++) {
    // 横向/纵向四连判断
    if ((board[i][0] == player && board[i][1] == player && board[i][2] == player && board[i][3] == player) || 
        (board[0][i] == player && board[1][i] == player && board[2][i] == player && board[3][i] == player)) {
      // 获胜格子着色逻辑
      if(context.read<SharedPrefProvider>().hardLevel % 2 != 0) {
        checkWinColors(board, player, i, context);
      }
      return true;
    }
  }
  // 对角线四连判断
  if ((board[0][0] == player && board[1][1] == player && board[2][2] == player && board[3][3] == player) ||
      (board[0][3] == player && board[1][2] == player && board[2][1] == player && board[3][0] == player)) {
    if(context.read<SharedPrefProvider>().hardLevel % 2 != 0) {
      checkWinColors(board, player, 0, context);
    }
    return true;
  }
  return false;
}

平局判断函数

bool checkDraw(List<List<String>> board) {
  // 检查所有格子是否填满
  for (var row in board) {
    for (var cell in row) {
      if (cell.isEmpty) {
        return false; // 存在空格子,不是平局
      }
    }
  }
  return true; // 所有格子填满,平局
}

AI走法调用函数

void makeAIMove(board) {
  int bestScore = -1000;
  int bestMoveRow = -1;
  int bestMoveCol = -1;

  for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {
      if (board[i][j].isEmpty) {
        board[i][j] = 'O';
        int score = minimax(board, false, context, -1000, 1000);
        board[i][j] = '';

        if (score > bestScore) {
          bestScore = score;
          bestMoveRow = i;
          bestMoveCol = j;
        }
      }
    }
  }

  // 执行AI走法
  if (bestMoveRow != -1 && bestMoveCol != -1) {
    setState(() {
      board[bestMoveRow][bestMoveCol] = "O";
    });
  }
}

问题排查与修复方案

1. 硬编码棋盘大小导致适配失败

所有函数中均直接使用4作为棋盘的行列数,当切换到5x5/6x6棋盘时:

  • 循环仅遍历前4行/列,剩余格子未被处理,导致递归无法覆盖所有可能状态
  • checkWin函数仅检查固定位置的4连,无法适配大棋盘的获胜判断逻辑

修复方式:将所有硬编码的4替换为动态获取的棋盘尺寸:

// 循环时使用board.length获取行数,board[i].length获取列数
for (int i = 0; i < board.length; i++) {
  for (int j = 0; j < board[i].length; j++) {
    // ... 原有逻辑
  }
}

2. 递归中混入UI逻辑导致性能异常

checkWin函数中在递归过程中调用context.read<SharedPrefProvider>()和checkWinColors,属于UI层操作:

  • 递归过程中频繁操作上下文会大幅降低性能,甚至引发状态异常
  • Minimax算法仅需判断胜负状态,无需处理UI着色逻辑

修复方式:分离算法与UI逻辑,将着色操作移到AI走法完成后执行,修改后的checkWin仅保留胜负判断:

bool checkWin(List<List<String>> board, String player) {
  int boardSize = board.length;
  int winLength = 4; // 根据游戏规则设定获胜所需的连续棋子数

  // 横向检查
  for (int i = 0; i < boardSize; i++) {
    for (int j = 0; j <= boardSize - winLength; j++) {
      bool win = true;
      for (int k = 0; k < winLength; k++) {
        if (board[i][j + k] != player) {
          win = false;
          break;
        }
      }
      if (win) return true;
    }
  }

  // 纵向检查
  for (int j = 0; j < boardSize; j++) {
    for (int i = 0; i <= boardSize - winLength; i++) {
      bool win = true;
      for (int k = 0; k < winLength; k++) {
        if (board[i + k][j] != player) {
          win = false;
          break;
        }
      }
      if (win) return true;
    }
  }

  // 正对角线检查
  for (int i = 0; i <= boardSize - winLength; i++) {
    for (int j = 0; j <= boardSize - winLength; j++) {
      bool win = true;
      for (int k = 0; k < winLength; k++) {
        if (board[i + k][j + k] != player) {
          win = false;
          break;
        }
      }
      if (win) return true;
    }
  }

  // 反对角线检查
  for (int i = winLength - 1; i < boardSize; i++) {
    for (int j = 0; j <= boardSize - winLength; j++) {
      bool win = true;
      for (int k = 0; k < winLength; k++) {
        if (board[i - k][j + k] != player) {
          win = false;
          break;
        }
      }
      if (win) return true;
    }
  }

  return false;
}

3. 大棋盘状态空间过大导致无限递归

4x4棋盘的状态数为16!(约2e13),远超出计算机实时计算能力,即使有alpha-beta剪枝也无法遍历所有状态,导致递归无法终止。

修复方式:添加递归深度限制+启发式评分函数:

// 修改Minimax函数,加入深度参数和启发式评分
int minimax(List<List<String>> board, bool isMaximizing, int depth, int alpha, int beta) {
  int boardSize = board.length;
  int winLength = 4;

  // 终端状态判断
  if (checkWin(board, 'O')) {
    return 1000 + depth; // 深度越深,得分越低(鼓励尽快获胜)
  } else if (checkWin(board, 'X')) {
    return -1000 - depth;
  } else if (checkDraw(board) || depth == 6) { // 限制最大递归深度为6
    return evaluateBoard(board, winLength); // 启发式评分
  }

  if (isMaximizing) {
    int bestScore = -1000;
    for (int i = 0; i < boardSize; i++) {
      for (int j = 0; j < boardSize; j++) {
        if (board[i][j].isEmpty) {
          board[i][j] = 'O';
          int score = minimax(board, false, depth + 1, alpha, beta);
          board[i][j] = '';
          bestScore = max(score, bestScore);
          alpha = max(alpha, score);
          if (beta <= alpha) break;
        }
      }
    }
    return bestScore;
  } else {
    int bestScore = 1000;
    for (int i = 0; i < boardSize; i++) {
      for (int j = 0; j < boardSize; j++) {
        if (board[i][j].isEmpty) {
          board[i][j] = 'X';
          int score = minimax(board, true, depth + 1, alpha, beta);
          board[i][j] = '';
          bestScore = min(score, bestScore);
          beta = min(beta, score);
          if (beta <= alpha) break;
        }
      }
    }
    return bestScore;
  }
}

// 启发式评分函数:评估当前棋盘对AI的有利程度
int evaluateBoard(List<List<String>> board, int winLength) {
  int score = 0;
  // 可根据横向、纵向、对角线的连续棋子数加权评分,例如:
  // 3连未被阻挡得10分,2连得2分,以此类推
  // 具体逻辑可根据游戏规则调整
  return score;
}

4. alpha-beta剪枝的break逻辑优化

原代码中仅在内层列循环break,外层行循环仍会继续,这本身没问题,但可优化遍历顺序(优先检查角落、中心等关键位置),进一步提升剪枝效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 17:39:52