Java井字棋MiniMax算法实现异常求助:AI无法阻止玩家获胜
修复井字棋MiniMax算法:让AI正确选择最优落子
你的井字棋AI没能正确阻止玩家获胜,核心问题出在两个关键逻辑错误上——棋盘状态没有同步更新到胜负检查类,以及胜负判断依赖了错误的全局变量。咱们一步步拆解问题,然后给出修复方案:
问题1:AIcheck的状态从未更新
你在makeTurn()里只实例化了一次AIcheck,但在calcScore()的递归过程中,每次模拟落子都会修改currentBoard,可AIcheck始终指向最初的棋盘状态!这意味着checkWin()和checkTie()永远在检查AI还没落子的初始棋盘,递归里的胜负判断完全失效,MiniMax根本没法评估每个落子的真实结果,自然只能按顺序选空位。
问题2:胜负判断依赖全局回合数错误
用turnCounter % 2来判断返回1还是-1完全不靠谱——这个计数器是真实游戏的回合数,而递归里的模拟落子是虚拟的,和真实回合数没有关联。比如递归模拟人类落子后,turnCounter并没有增加,这会导致胜负判断完全颠倒或者错误。正确的做法是直接从当前棋盘判断获胜者是谁,再对应返回分数。
修复后的完整代码
1. 修正calcScore()方法
每次递归时都基于当前棋盘创建新的Check实例,并且直接从棋盘判断获胜者:
private int calcScore(Button[][] currentBoard, int depth, boolean isMax) { // 每次递归都用当前棋盘创建检查实例,确保状态同步 Check currentCheck = new Check(currentBoard); // 判断当前棋盘的胜负 if (currentCheck.checkWin()) { // AI(O)获胜返回+1,人类(X)获胜返回-1 return currentCheck.getWinner().equals("O") ? 1 : -1; } else if (currentCheck.checkTie()) { return 0; } int bestScore = isMax ? Integer.MIN_VALUE : Integer.MAX_VALUE; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (currentBoard[i][j].getText().equals("")) { // 根据当前玩家落子:Max是AI(O),Min是人类(X) String player = isMax ? "O" : "X"; currentBoard[i][j].setText(player); // 递归计算下一层的分数 int score = calcScore(currentBoard, depth + 1, !isMax); // 回溯:撤销落子 currentBoard[i][j].setText(""); // 更新最优分数:Max取最大值,Min取最小值 bestScore = isMax ? Math.max(bestScore, score) : Math.min(bestScore, score); } } } return bestScore; }
2. 简化makeTurn()方法
移除多余的全局AIcheck实例,确保每次模拟落子后正确调用calcScore:
public void makeTurn(Button[][] currentBoard) { int bestScore = Integer.MIN_VALUE; int[] bestMove = new int[2]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (currentBoard[i][j].getText().equals("")) { // 模拟AI落子(O) currentBoard[i][j].setText("O"); // AI落子后轮到人类(Min玩家),所以isMax传false int score = calcScore(currentBoard, 0, false); // 回溯 currentBoard[i][j].setText(""); // 更新最优落子 if (score > bestScore) { bestScore = score; bestMove = new int[]{i, j}; } } } } Board.getInstance().getField(bestMove[0], bestMove[1]).performClick(); }
3. 完善Check类
确保Check类能正确判断获胜者并返回结果:
public class Check { private Button[][] board; private String winner; public Check(Button[][] board) { this.board = board; this.winner = null; } // 判断是否有玩家获胜,并记录获胜者 public boolean checkWin() { // 检查行 for (int i = 0; i < 3; i++) { String val = board[i][0].getText(); if (!val.equals("") && val.equals(board[i][1].getText()) && val.equals(board[i][2].getText())) { winner = val; return true; } } // 检查列 for (int j = 0; j < 3; j++) { String val = board[0][j].getText(); if (!val.equals("") && val.equals(board[1][j].getText()) && val.equals(board[2][j].getText())) { winner = val; return true; } } // 检查对角线 String centerVal = board[1][1].getText(); if (!centerVal.equals("")) { if (centerVal.equals(board[0][0].getText()) && centerVal.equals(board[2][2].getText())) { winner = centerVal; return true; } if (centerVal.equals(board[0][2].getText()) && centerVal.equals(board[2][0].getText())) { winner = centerVal; return true; } } return false; } public String getWinner() { return winner; } // 判断是否平局(棋盘填满且无获胜者) public boolean checkTie() { // 检查是否还有空位 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j].getText().equals("")) { return false; } } } // 无空位且无获胜者则为平局 return !checkWin(); } }
可选优化:加入深度加权
如果想让AI优先选择更快获胜的路径,或者尽量拖延失败,可以在返回分数时加入深度权重:
// 替换calcScore里的胜负返回逻辑 if (currentCheck.checkWin()) { // AI获胜:分数=1-深度(越早获胜分数越高) // 人类获胜:分数=深度-1(越晚失败分数越高) return currentCheck.getWinner().equals("O") ? 1 - depth : depth - 1; }
这样AI会优先选择能最快赢的落子,而不是随便选一个能赢的位置;如果必输,也会尽量拖延回合。
内容的提问来源于stack exchange,提问作者moeux
相关产品推荐
相关产品推荐

