井字棋MinMax算法返回不合逻辑走法,请求排查代码逻辑错误
井字棋MinMax算法逻辑错误排查
问题描述
实现井字棋MinMax3x3类的getBestMove方法时出现逻辑错误:传入正确棋盘后,算法返回不合逻辑的走法,未拦截玩家的获胜趋势(预期走法为[2,1],实际返回[0,2])。已确认棋盘传递正确,代码如下:
public class MinMax3x3 { public int[] getBestMove(char[][] board) { char computerSymbol = Symbol.X; int bestScore = Integer.MIN_VALUE; int row = -1; int col = -1; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j] == Symbol.EMPTY_FIELD) { board[i][j] = computerSymbol; int score = minimax(board, 0, false); board[i][j] = Symbol.EMPTY_FIELD; if (score > bestScore) { bestScore = score; row = i; col = j; } } } } int[] bestMove = {row, col}; return bestMove; } public int minimax(char[][] board, int depth, boolean isMaximizing) { char playerSymbol = Symbol.O; char computerSymbol = Symbol.X; int result = analyze3x3(board, playerSymbol, computerSymbol); if (result != 0) { return result; } if (isMaximizing) { int bestScore = Integer.MIN_VALUE; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j] == Symbol.EMPTY_FIELD) { board[i][j] = computerSymbol; int score = minimax(board, depth + 1, false); board[i][j] = Symbol.EMPTY_FIELD; bestScore = Math.max(score, bestScore); } } } return bestScore; } else { int bestScore = Integer.MAX_VALUE; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j] == Symbol.EMPTY_FIELD) { board[i][j] = playerSymbol; int score = minimax(board, depth + 1, true); board[i][j] = Symbol.EMPTY_FIELD; bestScore = Math.min(score, bestScore); } } } return bestScore; } } private int analyze3x3(char[][] board, char playerSymbol, char computerSymbol) { // Check rows for (int row = 0; row < 3; row++) { if (board[row][0] == playerSymbol && board[row][1] == playerSymbol && board[row][2] == playerSymbol) { return 1; // Player wins } else if (board[row][0] == computerSymbol && board[row][1] == computerSymbol && board[row][2] == computerSymbol) { return -1; // Computer wins } } // Check columns for (int column = 0; column < 3; column++) { if (board[0][column] == playerSymbol && board[1][column] == playerSymbol && board[2][column] == playerSymbol) { return 1; // Player wins } else if (board[0][column] == computerSymbol && board[1][column] == computerSymbol && board[2][column] == computerSymbol) { return -1; // Computer wins } } // Check diagonals if ((board[0][0] == playerSymbol && board[1][1] == playerSymbol && board[2][2] == playerSymbol) || (board[0][2] == playerSymbol && board[1][1] == playerSymbol && board[2][0] == playerSymbol)) { return 1; // Player wins } else if ((board[0][0] == computerSymbol && board[1][1] == computerSymbol && board[2][2] == computerSymbol) || (board[0][2] == computerSymbol && board[1][1] == computerSymbol && board[2][0] == computerSymbol)) { return -1; // Computer wins } // It's a draw return 0; } } public class GameRunner { public static void main(String[] args) { char board[][] = {{'x', 'o', ' '}, {' ', 'o', ' '}, {' ', ' ', ' '}}; MinMax3x3 bestMove = new MinMax3x3(); int[] lol = bestMove.getBestMove(board); System.out.printf(lol[0] + ", " + lol[1]); }
核心错误点分析
- 胜负分数逻辑颠倒:
analyze3x3中玩家获胜返回1、电脑获胜返回-1,但MinMax算法中电脑是最大化方,应追求更高分数,当前逻辑会让电脑主动避开获胜走法,反而优先选择对玩家有利的走法。 - 未处理平局终止条件:
minimax方法仅在有胜负时返回分数,棋盘填满无胜负时会继续递归,导致逻辑混乱。 - 符号大小写不匹配:测试棋盘使用小写
'x'/'o',但代码中Symbol.X/Symbol.O应为大写,导致空位判断和胜负识别出错。
修正后的代码
public class MinMax3x3 { public int[] getBestMove(char[][] board) { char computerSymbol = 'X'; int bestScore = Integer.MIN_VALUE; int row = -1; int col = -1; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j] == ' ') { board[i][j] = computerSymbol; int score = minimax(board, 0, false); board[i][j] = ' '; if (score > bestScore) { bestScore = score; row = i; col = j; } } } } return new int[]{row, col}; } public int minimax(char[][] board, int depth, boolean isMaximizing) { char playerSymbol = 'O'; char computerSymbol = 'X'; int result = analyze3x3(board, playerSymbol, computerSymbol); if (result != 0) { // 加入深度权重,优先选择最快获胜/最慢失败的走法 return result * (10 - depth); } // 判断棋盘是否已满(平局) if (isBoardFull(board)) { return 0; } if (isMaximizing) { int bestScore = Integer.MIN_VALUE; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j] == ' ') { board[i][j] = computerSymbol; int score = minimax(board, depth + 1, false); board[i][j] = ' '; bestScore = Math.max(score, bestScore); } } } return bestScore; } else { int bestScore = Integer.MAX_VALUE; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j] == ' ') { board[i][j] = playerSymbol; int score = minimax(board, depth + 1, true); board[i][j] = ' '; bestScore = Math.min(score, bestScore); } } } return bestScore; } } private int analyze3x3(char[][] board, char playerSymbol, char computerSymbol) { // 检查行 for (int row = 0; row < 3; row++) { if (board[row][0] == playerSymbol && board[row][1] == playerSymbol && board[row][2] == playerSymbol) { return -1; // 玩家获胜对电脑是负面结果,返回负分 } else if (board[row][0] == computerSymbol && board[row][1] == computerSymbol && board[row][2] == computerSymbol) { return 1; // 电脑获胜返回正分 } } // 检查列 for (int column = 0; column < 3; column++) { if (board[0][column] == playerSymbol && board[1][column] == playerSymbol && board[2][column] == playerSymbol) { return -1; } else if (board[0][column] == computerSymbol && board[1][column] == computerSymbol && board[2][column] == computerSymbol) { return 1; } } // 检查对角线 if ((board[0][0] == playerSymbol && board[1][1] == playerSymbol && board[2][2] == playerSymbol) || (board[0][2] == playerSymbol && board[1][1] == playerSymbol && board[2][0] == playerSymbol)) { return -1; } else if ((board[0][0] == computerSymbol && board[1][1] == computerSymbol && board[2][2] == computerSymbol) || (board[0][2] == computerSymbol && board[1][1] == computerSymbol && board[2][0] == computerSymbol)) { return 1; } return 0; } private boolean isBoardFull(char[][] board) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[i][j] == ' ') { return false; } } } return true; } } public class GameRunner { public static void main(String[] args) { // 统一符号大小写为大写,与代码匹配 char board[][] = {{'X', 'O', ' '}, {' ', 'O', ' '}, {' ', ' ', ' '}}; MinMax3x3 bestMove = new MinMax3x3(); int[] result = bestMove.getBestMove(board); System.out.printf("%d, %d", result[0], result[1]); // 输出应为2,1 } }
修正说明
- 颠倒胜负分数逻辑:电脑获胜返回
1,玩家获胜返回-1,让最大化方(电脑)优先选择获胜走法。 - 添加
isBoardFull方法处理平局,避免无限递归。 - 统一符号大小写,确保代码与测试棋盘符号匹配。
- 加入深度权重,让算法优先选择最快获胜或最慢失败的走法,优化决策逻辑。
内容的提问来源于stack exchange,提问作者reizorwins
相关产品推荐
相关产品推荐

