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

Python井字棋Min-Max算法递归深度与栈溢出问题优化求助

解决井字棋Min-Max算法的栈溢出与性能优化问题

首先,咱们先搞定导致栈溢出的直接代码错误——这是最紧急的问题:

一、修复代码中的致命bug

你的代码里有几个关键错误,直接引发了无限递归(进而导致栈溢出和递归深度报错):

  1. 赋值运算符误用:在minmax和player1函数中,你用了==(比较运算符)而非=(赋值运算符)来修改棋盘状态。比如:

    board[k] == "x"  # 这只是做比较,根本没修改棋盘!
    

    正确写法应该是:

    board[k] = "x"
    

    这个错误会让递归永远卡在同一个空棋盘状态循环,完全无法终止,必然触发栈溢出。

  2. 获胜判断函数逻辑混乱:wining函数存在多处问题:

    • 获胜条件写法错误:"x" == comb[a] == comb[f] == comb[v]应该改为comb[a] == comb[f] == comb[v] == "x",否则逻辑不成立。
    • 循环中break位置错误,导致只检查第一个获胜组合就返回,忽略了其他可能的获胜情况。
    • 返回值是获胜位置的索引(比如a),但key字典的键是"x"和"O",这会导致score = key[wining(board)]抛出KeyError,让递归无法正确终止。
    • 全局变量game的使用会搅乱游戏状态判断,建议让获胜判断函数直接返回获胜者("x"、"O")或None/"tie",不要修改全局变量。

修复后的获胜判断函数示例:

def check_winner(board):
    for combo in wining_comb:
        a, b, c = combo
        if board[a] == board[b] == board[c] != ".":
            return board[a]  # 返回获胜者"x"或"O"
    # 检查平局
    if "." not in board:
        return "tie"
    return None

二、解决栈溢出与递归深度问题

修复上述bug后,井字棋的递归深度最多只有9(最多走9步),理论上不会触发栈溢出。如果还是遇到问题,可以试试这些优化:

1. 替换为迭代版Min-Max算法

递归虽然简洁,但如果语言的递归栈有限(Python默认递归深度是1000,井字棋肯定够,但复杂游戏可能不够),可以用栈结构模拟递归调用,实现迭代版Min-Max:

def minmax_iterative(board):
    stack = [(board.copy(), 0, True, None)]  # (棋盘, 深度, 是否最大化, 父节点分数)
    memo = {}
    
    while stack:
        current_board, depth, is_max, _ = stack.pop()
        state = tuple(current_board)
        
        if state in memo:
            continue
        
        winner = check_winner(current_board)
        if winner:
            if winner == "x":
                score = 10 - depth
            elif winner == "O":
                score = depth - 10
            else:
                score = 0
            memo[state] = score
            continue
        
        if is_max:
            current_best = float('-inf')
            for i in range(9):
                if current_board[i] == ".":
                    new_board = current_board.copy()
                    new_board[i] = "x"
                    stack.append((new_board, depth+1, False, current_best))
            memo[state] = current_best
        else:
            current_best = float('inf')
            for i in range(9):
                if current_board[i] == ".":
                    new_board = current_board.copy()
                    new_board[i] = "O"
                    stack.append((new_board, depth+1, True, current_best))
            memo[state] = current_best
    
    return memo[tuple(board)]

2. 引入Alpha-Beta剪枝(核心性能优化)

Alpha-Beta剪枝能大幅减少Min-Max需要遍历的状态数,直接降低递归次数和深度,同时提升运行速度。它通过记录当前最优的alpha(最大化玩家的最低可接受分数)和beta(最小化玩家的最高可接受分数),剪掉不可能成为最优解的分支:

修复后的带Alpha-Beta剪枝的Min-Max函数:

def minmax(board, depth, is_max, alpha, beta):
    winner = check_winner(board)
    if winner:
        if winner == "x":
            return 10 - depth  # 考虑深度,让AI优先快速获胜
        elif winner == "O":
            return depth - 10
        else:
            return 0  # 平局
    
    if is_max:
        best_score = float('-inf')
        for i in range(9):
            if board[i] == ".":
                board[i] = "x"
                score = minmax(board, depth+1, False, alpha, beta)
                board[i] = "."  # 回溯棋盘
                best_score = max(best_score, score)
                alpha = max(alpha, best_score)
                if beta <= alpha:
                    break  # 剪枝,无需遍历剩余分支
        return best_score
    else:
        best_score = float('inf')
        for i in range(9):
            if board[i] == ".":
                board[i] = "O"
                score = minmax(board, depth+1, True, alpha, beta)
                board[i] = "."
                best_score = min(best_score, score)
                beta = min(beta, best_score)
                if beta <= alpha:
                    break
        return best_score

调用时初始传入alpha=float('-inf')和beta=float('inf')即可。

三、进一步提升代码运行速度的优化

1. 状态缓存(备忘录模式)

井字棋的很多状态会重复出现,比如不同走法可能到达同一个棋盘状态。用字典缓存已计算过的棋盘状态分数,避免重复计算:

memo = {}

def minmax_with_memo(board, depth, is_max, alpha, beta):
    state = tuple(board)  # 列表不能作为字典键,转为元组
    if state in memo:
        return memo[state]
    
    winner = check_winner(board)
    if winner:
        if winner == "x":
            score = 10 - depth
        elif winner == "O":
            score = depth - 10
        else:
            score = 0
        memo[state] = score
        return score
    
    # 后续Min-Max逻辑和Alpha-Beta剪枝一致...
    # 计算完best_score后存入缓存
    memo[state] = best_score
    return best_score

2. 优化棋盘表示

用整数代替字符串表示棋盘状态,比如0为空、1为"x"、-1为"O",这样计算和比较更快,也更节省内存:

board = [0] * 9  # 0: 空, 1: x, -1: O

3. 提前终止平局判断

在check_winner里先检查是否还有空位置,如果没有且无赢家,直接返回平局,避免不必要的递归。

四、修复后的完整代码示例(简化版)

board = [0] * 9  # 0: 空, 1: X, -1: O
winning_combs = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
memo = {}

def print_board():
    symbols = {0: ".", 1: "x", -1: "O"}
    for i in range(0,9,3):
        print(f"{symbols[board[i]]}|{symbols[board[i+1]]}|{symbols[board[i+2]]}")

def check_winner(board):
    for combo in winning_combs:
        a,b,c = combo
        if board[a] == board[b] == board[c] != 0:
            return board[a]
    if 0 not in board:
        return "tie"
    return None

def minmax(board, depth, is_max, alpha, beta):
    state = tuple(board)
    if state in memo:
        return memo[state]
    
    winner = check_winner(board)
    if winner:
        if winner == 1:
            score = 10 - depth
        elif winner == -1:
            score = depth - 10
        else:
            score = 0
        memo[state] = score
        return score
    
    if is_max:
        best = float('-inf')
        for i in range(9):
            if board[i] == 0:
                board[i] = 1
                score = minmax(board, depth+1, False, alpha, beta)
                board[i] = 0
                best = max(best, score)
                alpha = max(alpha, best)
                if beta <= alpha:
                    break
        memo[state] = best
        return best
    else:
        best = float('inf')
        for i in range(9):
            if board[i] == 0:
                board[i] = -1
                score = minmax(board, depth+1, True, alpha, beta)
                board[i] = 0
                best = min(best, score)
                beta = min(beta, best)
                if beta <= alpha:
                    break
        memo[state] = best
        return best

def ai_move():
    best_score = float('-inf')
    best_move = 0
    for i in range(9):
        if board[i] == 0:
            board[i] = 1
            score = minmax(board, 0, False, float('-inf'), float('inf'))
            board[i] = 0
            if score > best_score:
                best_score = score
                best_move = i
    board[best_move] = 1
    print("\nAI move:")
    print_board()

def player_move():
    while True:
        try:
            pos = int(input("\nEnter your position (1-9): ")) - 1
            if 0 <= pos <9 and board[pos] ==0:
                board[pos] = -1
                break
            else:
                print("Invalid position, try again!")
        except ValueError:
            print("Please enter a number between 1-9!")
    print("Your move:")
    print_board()

print("Tic Tac Toe - AI vs Human")
print_board()
while True:
    ai_move()
    winner = check_winner(board)
    if winner:
        break
    player_move()
    winner = check_winner(board)
    if winner:
        break

if winner ==1:
    print("\nAI wins!")
elif winner ==-1:
    print("\nYou win!")
else:
    print("\nIt's a tie!")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 10:57:46