Python3实现Minimax算法遇阻:井字棋Bot开发求助
嘿,我懂这种感觉!Minimax听起来玄乎,第一次看确实像看天书,但其实拆解开就是个很直白的思路——说白了就是模拟所有可能的走法,然后选对自己最有利的那一步。咱们拿井字棋来拆解,绝对能给你讲明白。
Minimax是个零和博弈算法——你赢我就输,反之亦然。井字棋里,假设你是X,Bot是O:你要最大化自己的胜率,Bot要最小化你的胜率,这就是Minimax(Max和Min的结合)名字的由来。
第一步:给局面打“分”
首先得给每个最终局面定个得分,算法才知道哪步好哪步坏:
- 如果Bot(O)赢了,得**+10分**
- 如果玩家(X)赢了,得**-10分**
- 平局的话,得0分
第二步:递归模拟所有走法
算法会递归遍历棋盘上所有空位置,分别假设自己(Max方,Bot)和玩家(Min方)走这一步,然后计算每个分支的得分:
- Max层(Bot走):遍历所有空位,选能得到最高得分的那步
- Min层(玩家走):遍历所有空位,选能得到最低得分的那步(因为玩家会尽量让Bot得分低)
- 递归到终点(有人赢或者平局),就返回对应的得分
第三步:可选优化——Alpha-Beta剪枝
如果你的棋盘不大(井字棋只有9格),剪枝不是必须,但知道了能让算法更快。它的核心是提前砍掉那些不可能影响最终结果的分支:比如Max已经找到一个10分的走法,后面的分支哪怕再高也超不过10,就不用浪费时间算了。
咱们写个极简版的核心函数,你一看就懂:
首先写个判断胜负的辅助函数:
def check_winner(board): # 定义所有赢棋的模式:行、列、对角线 win_patterns = [ [0,1,2], [3,4,5], [6,7,8], # 横向 [0,3,6], [1,4,7], [2,5,8], # 纵向 [0,4,8], [2,4,6] # 对角线 ] # 检查是否有人赢 for pattern in win_patterns: if board[pattern[0]] == board[pattern[1]] == board[pattern[2]] != '': return board[pattern[0]] # 检查是否平局 if '' not in board: return 'tie' return None
然后是Minimax核心函数:
def minimax(board, is_maximizing): winner = check_winner(board) # 终止条件:走到终点就返回对应得分 if winner == 'O': # Bot赢了 return 10 elif winner == 'X': # 玩家赢了 return -10 elif winner == 'tie': # 平局 return 0 if is_maximizing: best_score = -float('inf') # 遍历所有空位,模拟Bot走棋 for i in range(9): if board[i] == '': board[i] = 'O' # Bot走一步 # 切换到玩家回合(Min层),递归计算得分 score = minimax(board, False) board[i] = '' # 回溯,恢复棋盘原样 best_score = max(best_score, score) # Max选最高分 return best_score else: best_score = float('inf') # 遍历所有空位,模拟玩家走棋 for i in range(9): if board[i] == '': board[i] = 'X' # 玩家走一步 # 切换到Bot回合(Max层),递归计算得分 score = minimax(board, True) board[i] = '' # 回溯 best_score = min(best_score, score) # Min选最低分 return best_score
最后是Bot找最优走法的函数:
def find_best_move(board): best_score = -float('inf') best_move = -1 # 遍历所有空位,找到得分最高的走法 for i in range(9): if board[i] == '': board[i] = 'O' score = minimax(board, False) board[i] = '' if score > best_score: best_score = score best_move = i return best_move
很多StackOverflow的最优答案会把代码写得很简洁,甚至加上很多进阶优化(比如缓存局面得分、更复杂的剪枝逻辑),但核心就是上面这些基础逻辑。你可以先从这个极简版开始跑,测试没问题后再慢慢加优化。
比如你可以初始化一个空棋盘:board = ['', '', '', '', '', '', '', '', ''],调用find_best_move(board)就能得到Bot应该走的位置索引(0-8对应九宫格的每个位置)。跑起来你会发现,Bot再也不会轻易输了——甚至根本不会输,最多和你平局!
内容的提问来源于stack exchange,提问作者defunct-user




