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

如何修复Mancala游戏中minimax算法的异常计算问题

问题描述

尝试独立实现Minimax算法,已完成Node类与search_ahead搜索方法,但未编写AI类,目前遇到以下异常:

  • 计算得到的所有估值不合理地偏向先手玩家;
  • 手动推演分支后,计算值与实际对局结果不符;
  • 当搜索深度大于3时,所有返回值均为负数。

深度为3时的示例输出

数值代表bottom玩家与top玩家的得分差:

pocket 8: [-1, -1, -1, -1, -1]
pocket 9: [0, -1, -1, -1, -1]
pocket 10: [-1, -1, -2, -2, -2]
pocket 11: [-1, -1, -2, -2, -2]
pocket 12: [-8, -1, -2, -2, -2]
pocket 13: [-1, -3, -2, -2, -2]
pocket 1: [-1, -1, -2, -2, -8, -3].
问题排查与修复

1. 估值偏向先手的核心原因

Minimax算法中玩家视角与max/min角色的对应逻辑错误:原代码所有层级均使用bottom得分 - top得分作为估值,未考虑top玩家(min方)的目标是最小化该差值,导致估值始终偏向先手的bottom玩家。修复时需根据当前玩家视角调整估值计算逻辑。

2. 推演结果不符的问题点

  • Board.move_pieces中无效移动的move_again状态设置错误,导致无效移动被错误允许再次行动;
  • 落子后的吃子逻辑存在边界判断错误,未正确处理对方口袋无棋子的情况;
  • 递归中字符串与布尔值的判断不统一(如useful_list[1]==False与useful_list[1]=="False"混用),导致回合切换逻辑混乱。

3. 深度大于3返回全负的问题

递归终止时的估值未根据玩家视角调整,且高层级节点的max/min选择逻辑未正确跟随回合切换,导致错误的估值累加,最终出现全负结果。

修复后的完整代码
import copy

class Board():
    def __init__(self, turn, position, pockets):
        # 移除冗余的game_state参数,简化初始化
        self.turn = turn
        self.position = position
        self.pockets = pockets.copy() if pockets else {}

    def make_pieces(self):
        # 初始化棋盘棋子数
        for i in range(1, 7):
            self.pockets[f"pocket_{i}"] = 4
        for i in range(8, 14):
            self.pockets[f"pocket_{i}"] = 4
        self.pockets["pocket_7"] = 0
        self.pockets["pocket_14"] = 0

    def move_pieces(self):
        starting_position = self.position
        valid_move = "True"
        move_again = "False"

        # 基础无效移动判断
        if starting_position in (7, 14):
            valid_move = "False"
            return [valid_move, move_again]
        if self.turn == "top" and 1 <= starting_position <=7:
            valid_move = "False"
            return [valid_move, move_again]
        if self.turn == "bottom" and 8 <= starting_position <=14:
            valid_move = "False"
            return [valid_move, move_again]
        if self.pockets[f"pocket_{starting_position}"] == 0:
            valid_move = "False"
            return [valid_move, move_again]

        # 执行棋子移动逻辑
        pieces = self.pockets[f"pocket_{starting_position}"]
        self.pockets[f"pocket_{starting_position}"] = 0
        current_pos = starting_position

        while pieces > 0:
            current_pos += 1
            # 跳过对方的得分口袋
            if self.turn == "bottom" and current_pos == 14:
                current_pos = 1
            if self.turn == "top" and current_pos ==7:
                current_pos =8
            if current_pos >14:
                current_pos =1

            self.pockets[f"pocket_{current_pos}"] +=1
            pieces -=1

        # 判断是否可以再次行动
        if (self.turn == "bottom" and current_pos ==7) or (self.turn == "top" and current_pos ==14):
            move_again = "True"
        else:
            # 修正后的吃子逻辑
            if self.turn == "bottom" and 1<= current_pos <=6 and self.pockets[f"pocket_{current_pos}"] ==1:
                opposite_pocket = 14 - current_pos
                if self.pockets[f"pocket_{opposite_pocket}"] >0:
                    self.pockets["pocket_7"] += self.pockets[f"pocket_{current_pos}"] + self.pockets[f"pocket_{opposite_pocket}"]
                    self.pockets[f"pocket_{current_pos}"] =0
                    self.pockets[f"pocket_{opposite_pocket}"] =0
            if self.turn == "top" and 8<= current_pos <=13 and self.pockets[f"pocket_{current_pos}"] ==1:
                opposite_pocket = 14 - (current_pos -7)
                if self.pockets[f"pocket_{opposite_pocket}"] >0:
                    self.pockets["pocket_14"] += self.pockets[f"pocket_{current_pos}"] + self.pockets[f"pocket_{opposite_pocket}"]
                    self.pockets[f"pocket_{current_pos}"] =0
                    self.pockets[f"pocket_{opposite_pocket}"] =0

        return [valid_move, move_again]

class Node():
    def __init__(self, game_state, pocket, depth, parent=None):
        self.game_state = game_state
        self.pocket = pocket
        self.depth = depth
        self.parent = parent
        self.children = []
        self.value = None  # 用单个值替代列表,简化估值传递逻辑

    def search_ahead(self):
        # 复制棋盘,避免修改原状态
        test_board = copy.deepcopy(self.game_state)
        test_board.position = self.pocket
        valid_move, move_again = test_board.move_pieces()

        if valid_move == "False":
            # 无效移动返回对应极差估值
            self.value = -float('inf') if self.game_state.turn == "bottom" else float('inf')
            if self.parent:
                return
            else:
                return self.value

        # 递归终止条件:搜索深度为1
        if self.depth ==1:
            bottom_score = test_board.pockets["pocket_7"]
            top_score = test_board.pockets["pocket_14"]
            # 根据玩家视角计算估值
            if self.game_state.turn == "bottom":
                self.value = bottom_score - top_score
            else:
                self.value = top_score - bottom_score
            if self.parent:
                self.parent.children.append(self)
            return self.value

        # 确定下一个玩家
        next_turn = test_board.turn if move_again == "True" else ("top" if test_board.turn == "bottom" else "bottom")
        test_board.turn = next_turn

        # 扩展子节点并递归搜索
        if next_turn == "bottom":
            for i in range(1,7):
                child_node = Node(test_board, i, self.depth-1, self)
                self.children.append(child_node)
                child_node.search_ahead()
            # 筛选有效估值并取最大值(bottom为max方)
            valid_values = [child.value for child in self.children if child.value != -float('inf')]
            self.value = max(valid_values) if valid_values else -float('inf')
        else:
            for i in range(8,14):
                child_node = Node(test_board, i, self.depth-1, self)
                self.children.append(child_node)
                child_node.search_ahead()
            # 筛选有效估值并取最小值(top为min方)
            valid_values = [child.value for child in self.children if child.value != float('inf')]
            self.value = min(valid_values) if valid_values else float('inf')

        # 传递估值给父节点或返回根节点值
        if self.parent:
            return self.value
        else:
            return self.value

# 测试代码
game_board = Board("bottom", 1, {})
game_board.make_pieces()

# 测试深度3的根节点
root_node = Node(game_board, 1, 3)
result = root_node.search_ahead()
print(f"Root node value (depth 3): {result}")

# 打印子节点估值
print("\nChild node values:")
for child in root_node.children:
    print(f"Pocket {child.pocket}: {child.value}")

内容的提问来源于stack exchange,提问作者Chris Porter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:27:32