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

井字棋Minimax算法未按预期响应,请求问题原因排查

井字棋Minimax算法失效原因分析

我近期尝试实现一个返回井字棋最佳走法的基础API,尽可能自主编写代码。原本认为方案可行,但算法既不会阻止对手获胜,也无法实现预期功能。我并非寻求正确代码,而是想了解代码未达预期的原因。

测试场景:我走中心位置,AI回应走左上;我走右下,AI走中上;我走右上时,AI未按预期走右中阻止我获胜,反而走左中。推测问题出在minmax()的递归调用及数据传递环节,这已是第三次实现该算法,目前难以理清逻辑。了解多数井字棋算法实现采用嵌套列表表示棋盘行,但认为这并非问题根源,若有误也希望得到解释。

代码片段

from copy import deepcopy

def player(board: list[str | None]):
    """Returns the next player for a given board
    
    Assumes that X goes first for any given game
    """
    if board.count("X") > board.count("O"):
        return "O"
    return "X"


def result_of_move(board: list[str | None], move:int) -> list[str | None]:
    """Returns resultant board from a given move, based on current player
        
    <move> parameter must be an integer 0-8 (inclusive)
    Player move determined by player()
    For use by opponent models in determining resultant actions
    """
    if (move not in range(9)) or (board[move] != None):
        raise Exception("Invalid move!")

    board_copy = deepcopy(board)
    board_copy[move] = player(board)
    
    return board_copy


def available_actions(board: list[str | None]) -> list[int]:
    """Returns available move indices given a board state. 
    
    Agnostic to which player's turn it is, for use by opponent models to 
    populate search pahts
    """
    options = []
    for i, x in enumerate(board):
        if x is None:
            options.append(i)
    return options

def winner(board: list[str | None]):
    """Takes a board and returns winner if any, else None"""
    win_states = [
        [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 line in win_states:
        a, b, c = line
        if board[a] and board[a] == board[b] == board[c]:
            return board[a]
    return None
    

def is_terminal(board: list[str | None]) -> bool:
    """Returns whether game is over or not
    
    To be used as a base case check for recursive opponent models
    """
    if None not in board:
        return True
    if winner(board):
        return True
    return False

def board_utility(board: list[str | None]):
    if winner(board) == "X":
        return 1
    elif winner(board) == "O":
        return -1
    else:
        return 0


def minmax(board: list[str | None]):
    """Returns the optimal move value and index based on board input board
    
    This implements the minmax algorithm *without* alpha-beta pruning or depth-, 
    limiting, using full depth-first search. X is assumed the maximizing player
    """

    def max_value(board):
        value = -2
        move = None
        
        for action in available_actions(board):
            res = result_of_move(board, action)
            if is_terminal(res):
                return (board_utility(res), action)
            
            v = max(min_value(res)[0], value)
            
            if v >= value:
                value = v
                move = action
        return (value, move)
    
    def min_value(board):
        value = 2
        move = None
        
        for action in available_actions(board):
            res = result_of_move(board, action)
            if is_terminal(res):
                return (board_utility(res), action)
            
            v = min(max_value(res)[0], value)
            
            if v <= value:
                value = v
                move = action
        return (value, move)
        
    if is_terminal(board):
        return None
    
    if player(board) == "X":
        return max_value(board)[1]
    else:
        return min_value(board)[1]

核心问题分析

1. 终端状态的提前返回破坏遍历逻辑

在max_value和min_value函数中,遍历可用走法时只要遇到终端状态(即走法后游戏结束)就直接return,这会导致算法只检查第一个终端状态的走法,完全跳过后续所有可能的走法。比如,当某个走法能直接结束游戏,但并非当前玩家的最优选择时,函数会直接返回该走法,而不会比较其他走法的结果,这彻底违背了Minimax算法需要遍历所有后续状态并选择最优解的核心逻辑。

2. 效用值更新逻辑错误

  • max_value函数:v = max(min_value(res)[0], value)的写法错误。正确逻辑应该是先获取子节点的效用值,再与当前记录的最大value比较,若子节点值更大则更新value和对应move。当前写法会导致value无法正确累积所有子节点的最优值,而是每次只取当前子节点值和旧value的最大值,后续判断v >= value时永远为真,无法正确筛选出最优走法。
  • min_value函数:存在同样问题,v = min(max_value(res)[0], value)的写法错误,无法正确追踪最小效用值对应的走法。

3. 棋盘表示方式的验证

你采用的一维列表表示棋盘(索引0-8对应3x3网格)是完全可行的,并非问题根源。多数实现用嵌套列表只是为了直观对应行和列,一维列表在逻辑上没有任何问题,无需调整。

内容的提问来源于stack exchange,提问作者Danyal Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:07:25