井字棋Min Max算法Python实现问题:AI决策不稳健
问题分析与修复方案
你的双人井字棋AI的核心问题出在MinMax算法的实现逻辑上,以及辅助函数的设计错误,具体来说有这几个关键问题:
1. pos_moves() 函数逻辑错误
你用permutations(self.posis())生成所有空位的排列,这会产生大量冗余的对局序列(比如空位[1,4]会生成(1,4)和(4,1)两个完全不同的序列),但井字棋的每一步只需要选择一个空位,而不是预先生成整个对局的走法顺序。这不仅会导致计算量爆炸,还会让min_max函数错误地模拟“固定顺序的对局”,而不是模拟双方每一步都会做出最优选择的过程。
正确的做法是:pos_moves()只需要返回当前棋盘的所有空位即可,也就是直接返回self.posis()的结果。
2. min_max() 不是真正的MinMax算法
当前的min_max是一次性模拟从某个初始走法开始的完整对局,但没有考虑到人类玩家会选择对AI最不利的走法(Min玩家的角色)。真正的MinMax是递归的:
- Max玩家(AI)会选择能让自己得分最高的走法
- Min玩家(人类)会选择能让AI得分最低的走法
你的代码里只是模拟了双方按固定顺序走完全局,完全没有体现“最优选择”的核心逻辑,这才是AI走不出最优步的根本原因。
3. 评分逻辑与决策逻辑的小问题
- 当前评分的计算方式虽然考虑了深度,但没有结合MinMax的递归评估
comp_think里两次调用min_max,做了重复计算- 当多个走法评分相同时,只取第一个,导致AI走法单一且可能错过更优的选择
修复后的完整代码
下面是修正后的核心函数,我会逐一解释改进点:
1. 修正辅助函数
# 查找棋盘上所有空位(这个函数没问题,保留) def posis(self): return [x for x, coordinate in enumerate(self.gameBoard) if coordinate not in ('X', 'O')] # 返回当前棋盘的所有可能走法(修正:只返回空位,不需要排列) def pos_moves(self): return self.posis()
2. 重构为递归的MinMax算法
def min_max(self, is_maximizing, depth, player): # 先判断当前棋盘的终止状态 human_player = 'O' if player == 'X' else 'X' winner = self.win_checker(self.gameBoard) # 终止条件:AI获胜,返回高分(深度越小,得分越高,因为越早赢越好) if winner == player: return 10 - depth # 人类获胜,返回低分 elif winner == human_player: return depth - 10 # 平局,返回0 elif self.board_full(self.gameBoard): return 0 if is_maximizing: best_score = -float('inf') # 遍历所有可能的走法 for move in self.pos_moves(): # 模拟走这一步 self.gameBoard[move] = player # 递归调用,切换为Min玩家(人类),深度+1 score = self.min_max(False, depth + 1, player) # 回溯,撤销这一步 self.gameBoard[move] = str(move) # 更新最高分 best_score = max(best_score, score) return best_score else: best_score = float('inf') for move in self.pos_moves(): self.gameBoard[move] = human_player score = self.min_max(True, depth + 1, player) self.gameBoard[move] = str(move) best_score = min(best_score, score) return best_score
3. 修正AI决策函数
def comp_think(self, player): best_score = -float('inf') best_moves = [] human_player = 'O' if player == 'X' else 'X' # 遍历所有可能的初始走法,评估每个走法的得分 for move in self.pos_moves(): self.gameBoard[move] = player score = self.min_max(False, 1, player) self.gameBoard[move] = str(move) # 如果当前走法得分更高,更新最优走法列表 if score > best_score: best_score = score best_moves = [move] # 如果得分相同,加入最优走法列表 elif score == best_score: best_moves.append(move) # 从所有最优走法中随机选一个(避免每次走法都一样) import random chosen_move = random.choice(best_moves) print(f"AI选择的最优走法: {chosen_move}") return str(chosen_move)
关键改进点说明
- 递归MinMax:通过递归模拟每一步的选择,Max玩家(AI)选最高分,Min玩家(人类)选最低分,真正实现了“AI选择最优走法,人类选择对AI最不利的走法”的逻辑。
- 回溯机制:在模拟走法后,会把棋盘恢复原样,避免影响后续的评估。
- 评分优化:AI获胜得正分,人类获胜得负分,平局得0,同时结合深度调整得分——越早获胜,得分越高,鼓励AI快速赢下对局。
- 多最优走法处理:当多个走法评分相同时,随机选择一个,避免AI走法僵化。
- 避免重复计算:
comp_think里只做一次评估,不再重复调用min_max。
测试验证
用你提供的示例对局测试:
- 人类走位置0后,AI会优先选择位置4(正中间),因为这是井字棋中最优的初始应对,能最大化后续的获胜概率。
- 后续的每一步AI都会选择能阻止人类获胜,同时最大化自己获胜机会的走法。
内容的提问来源于stack exchange,提问作者Kaleba KB Keitshokile
相关产品推荐
相关产品推荐

