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

在蒙特卡洛树搜索(MCTS)中如何考量对手的可能走法?

双人交替视角蒙特卡洛树搜索(MCTS)的胜率计算与最优走法实现方案

我刚好有不少双人零和游戏MCTS的开发经验,针对你提到的交替视角节点+基于深层节点胜率选走法的需求,这里分享几个核心要点和实现思路:


核心逻辑:视角交替下的胜率反转

因为你的节点是交替视角(根节点玩家1,子节点玩家2,以此类推),所以不同视角节点的胜率含义完全相反:

  • 玩家1视角的节点胜率:代表玩家1在该节点下获胜的概率
  • 玩家2视角的节点胜率:代表玩家2在该节点下获胜的概率,对玩家1来说,这个值的补集(1 - 玩家2胜率)才是自己的胜率

举个你提到的例子:假设根节点有两个合法走法对应的子节点(玩家2视角):

  • 第一个子节点经过大量模拟后,玩家2胜率70% → 对玩家1来说,这个走法的胜率是30%
  • 第二个子节点玩家2胜率40% → 玩家1胜率60%
    这时候显然要选第二个走法。

关键实现步骤

1. 给节点标记视角信息

每个节点必须明确记录当前属于哪个玩家的回合,这样回溯更新胜率时才不会出错。比如给节点类加一个player_turn字段:

class MCTSNode:
    def __init__(self, player_turn, move=None):
        self.player_turn = player_turn  # 1 或 2
        self.move = move  # 该节点对应的走法
        self.wins = 0
        self.visits = 0
        self.children = []
        self.parent = None

2. 模拟与回溯的胜率更新逻辑

模拟(rollout)结束后,要明确返回结果:比如1代表玩家1胜,-1代表玩家2胜,0代表平局。回溯时根据节点视角更新胜率:

  • 若当前节点是玩家1视角:如果模拟结果是1,则胜场+1;平局则胜场+0.5
  • 若当前节点是玩家2视角:如果模拟结果是-1,则胜场+1;平局则胜场+0.5

对应的回溯函数示例:

def backpropagate(node, result):
    current_node = node
    while current_node is not None:
        current_node.visits += 1
        if result == 1:
            if current_node.player_turn == 1:
                current_node.wins += 1
        elif result == -1:
            if current_node.player_turn == 2:
                current_node.wins += 1
        else:  # 平局
            current_node.wins += 0.5
        current_node = current_node.parent

3. 最优走法的选择逻辑

遍历根节点的所有子节点(玩家2视角),将每个子节点的胜率转换为玩家1的胜率,然后选择胜率最高的走法:

def select_best_move(root_node):
    best_win_rate = -1.0
    best_move = None
    for child in root_node.children:
        if child.visits == 0:
            # 未被模拟的节点暂时胜率设为0.5(或根据需求调整)
            player1_rate = 0.5
        else:
            # 转换为玩家1的胜率:1 - 玩家2的胜率
            player1_rate = 1 - (child.wins / child.visits)
        if player1_rate > best_win_rate:
            best_win_rate = player1_rate
            best_move = child.move
    return best_move

额外注意事项

  • 探索与利用的平衡:在选择扩展节点时,不能只看胜率,要结合UCB1公式平衡探索(访问少的节点)和利用(胜率高的节点),公式示例:
    import math
    def ucb1(child, parent):
      win_rate = child.wins / child.visits if child.visits > 0 else 0.5
      exploration_term = math.sqrt(math.log(parent.visits) / (child.visits + 1e-6))
      return win_rate + math.sqrt(2) * exploration_term
    
  • 模拟的随机性:rollout阶段要保证动作选择的随机性,避免固定路径导致胜率偏差;如果游戏有确定性策略,也可以在模拟后期加入启发式优化。
  • 平局处理:根据游戏规则调整平局的权重,比如有些游戏平局对双方价值相同,就按各加0.5处理;如果平局对某方更有利,可以调整权重。

内容的提问来源于stack exchange,提问作者Bastiaan Reinink

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 02:29:56