任务:实现 BOSS 战交互循环(boss_fight_loop.py)

任务:实现 BOSS 战交互循环(boss_fight_loop.py)

一、功能概述

在《极妙幻境》中,当玩家到达第4、7、10层时,会触发BOSS战。你需要实现一个独立的交互循环模块 boss_fight_loop.py,负责管理整个BOSS战的人机交互流程。该模块将调用已实现的 BossFight 类(来自 boss_fight.py)来控制战斗状态,同时处理玩家的输入(包括战斗指令和系统指令),并显示相应的信息。此外,需要限制 hint 指令的调用次数,每场BOSS战最多使用3次,以避免过高的LLM费用。

二、模块位置与依赖

  • 文件路径src/game_logic/boss_fight_loop.py
  • 依赖模块
    • boss_fight:提供 BossFight 类。
    • commands:提供 handle_system_command 函数。
    • config:可能用于读取配置(如是否调试模式),但不强制。
    • logging 等标准库。
  • 输入BossFight 实例、游戏状态(用于系统指令,但 handle_system_command 需要 game_stateboss_fight 对象)。

三、命令解析器设计

3.1 支持的多语言指令格式

指令类型 英文格式 中文格式 简中格式(单字) 示例
跟牌 follow <数量> <类型> 跟牌 <数量> <类型> 跟 <数量> <类型> follow 2 good, 跟牌 3 恶, 跟 1 中立
质疑 challenge 质疑 challenge, 质疑,
不跟 fold 不跟 fold, 不跟,
  • 类型支持:good/evil/neutral 及其中文对应 //中立(大小写不敏感)。
  • 数量和类型之间用空格分隔,数量为1-3的整数。
  • 指令与参数之间可能有多个空格,需支持 trim 和 split。

3.2 解析函数

def parse_battle_command(cmd: str) -> Optional[Dict]:
    """
    解析玩家输入的战斗指令。

    参数:
        cmd: 用户输入的原始字符串(已 strip)

    返回:
        如果解析成功,返回字典:
            - 对于跟牌:{'action': 'follow', 'num_cards': int, 'claim': CardType}
            - 对于质疑:{'action': 'challenge'}
            - 对于不跟:{'action': 'fold'}
        如果解析失败,返回 None。
    """
    # 实现逻辑见下文

3.3 解析逻辑

  1. 将输入字符串转为小写(方便匹配),并去除首尾空格。
  2. 尝试匹配系统指令(不在此函数处理,由外层先调用 handle_system_command)。
  3. 分割单词:parts = cmd.split()
  4. 根据第一个词判断指令类型:
    • 如果 parts[0]['follow', '跟牌', '跟'] 中,则为跟牌指令。
      • 需要检查 len(parts) == 3,否则返回 None。
      • num_cards 尝试转为整数,且在1-3之间。
      • type_strparts[2],映射为 CardType
        • 'good' 或 '善' -> CardType.GOOD
        • 'evil' 或 '恶' -> CardType.EVIL
        • 'neutral' 或 '中立' -> CardType.NEUTRAL
      • 如果转换失败,返回 None。
      • 返回 {'action': 'follow', 'num_cards': num, 'claim': claim_type}
    • 如果 parts[0]['challenge', '质疑', '疑'] 中,则为质疑指令。
      • 要求 len(parts) == 1,否则返回 None(防止多余参数)。
      • 返回 {'action': 'challenge'}
    • 如果 parts[0]['fold', '不跟', '弃'] 中,则为不跟指令。
      • 要求 len(parts) == 1
      • 返回 {'action': 'fold'}
    • 否则返回 None。

四、BOSS战交互循环函数

4.1 函数签名

def run_boss_fight(
    fight: BossFight,
    game_state: dict
) -> str:
    """
    运行一场完整的BOSS战交互。

    参数:
        fight: 已初始化的 BossFight 实例
        game_state: 当前游戏状态字典(至少包含 'in_boss_fight': True)

    返回:
        战斗结果:'player'(玩家胜利),'boss'(BOSS胜利),或 'quit'(玩家退出)
    """
    # 实现见下文

4.2 内部状态

  • hint_used:记录本场BOSS战已使用的 hint 次数,初始为0,最大3次。
  • 游戏状态字典在每次调用系统指令时可能需要更新?但系统指令一般只读,可传入副本或原对象。建议传入原 game_state,但系统指令中可能会用到 layer_cards 等,这些在BOSS战中不会变化(手牌由 fight 管理),所以没问题。

4.3 循环逻辑

hint_used = 0

while not fight.game_over:
    # 显示当前牌局信息(可选,主循环也可能显示,但这里需要确保显示)
    display_state = fight.get_display_state()
    # 格式化显示,例如:
    print(f"玩家手牌:{display_state['player_hand_count']}张,BOSS手牌:{display_state['boss_hand_count']}张,桌面牌堆:{display_state['table_pile_count']}张")
    if display_state['last_claim']:
        print(f"上一轮宣称:{display_state['last_claim'].value}")
    print(f"当前轮到:{'你' if display_state['current_turn']=='player' else 'BOSS'}")

    if fight.is_player_turn():
        # 玩家回合
        raw_cmd = input("> ").strip()
        if not raw_cmd:
            continue
        
        # 1. 尝试系统指令(包括 hint,需检查次数)
        # 注意:handle_system_command 内部会调用 get_llm_hint,我们需要在外部检查次数
        # 所以我们先单独处理 hint,其他系统指令仍由 commands 处理
        if raw_cmd.upper() in ['HINT', 'H', '提示', '?']:  # 可支持多种形式,简化
            if hint_used >= 3:
                print("你已经使用了3次提示,本场BOSS战无法再使用 hint 指令。")
                continue
            # 调用系统指令处理 hint
            sys_result = handle_system_command('HINT', game_state, fight)
            print(sys_result['output'])
            if sys_result['should_quit']:
                return 'quit'
            # 提示成功,计数+1
            hint_used += 1
            continue  # 不消耗回合
        
        # 其他系统指令(PACK, STATUS, HELP, QUIT)
        sys_result = handle_system_command(raw_cmd, game_state, fight)
        if sys_result['should_quit']:
            return 'quit'
        if sys_result['output']:
            print(sys_result['output'])
            continue  # 不消耗回合
        
        # 2. 如果不是系统指令,尝试解析战斗指令
        battle_cmd = parse_battle_command(raw_cmd)
        if battle_cmd is None:
            print("无效指令。请使用战斗指令(follow/跟牌/跟、challenge/质疑/疑、fold/不跟/弃)或系统指令(HELP/PACK/STATUS/HINT/QUIT)。")
            continue
        
        # 执行玩家动作
        result = fight.player_action(**battle_cmd)
        if not result['success']:
            print(result['message'])
            continue
        print(result['message'])
        if result.get('dialogue'):
            print(result['dialogue'])
        if result['game_over']:
            break
    else:
        # BOSS回合
        result = fight.boss_turn()
        print(result['message'])
        if result.get('dialogue'):
            print(result['dialogue'])
        if result['game_over']:
            break

# 战斗结束,返回胜者
return fight.winner

注意:以上循环中,玩家回合执行战斗指令后,循环会回到开头,重新显示状态并轮到BOSS或玩家(由 fight 内部决定)。这是合理的,因为玩家动作后可能立即轮到BOSS,也可能游戏结束。

五、与现有模块的整合

  • handle_system_command 已经在 commands.py 中实现,需要确保它能够正确处理 BOSS 战中的 HINT 指令(它内部已经使用了 boss_fight.get_hint_contextllm_hint.get_llm_hint)。
  • 在调用 handle_system_command 时,传入的 game_state 应包含 'in_boss_fight': True,以便 HINT 指令识别。
  • 由于 handle_system_command 内部也会调用 get_llm_hint,我们需要在循环外部限制调用次数,所以先单独处理 HINT,检查次数后再调用系统指令。这样可以确保次数限制生效。

六、注意事项

  • 输入大小写:指令应不区分大小写,parse_battle_command 内部可将输入转为小写处理,但保留原始显示。
  • 空格处理split() 默认按空白分割,多个空格也没问题。
  • 类型映射:确保 claim 转换为 CardType 枚举,可直接使用 boss_fight.CardType
  • 系统指令冲突:如果玩家输入 hint 等,可能会被系统指令优先处理。我们设计的是先处理系统指令(包括 hint),再处理战斗指令,这符合用户期望。
  • hint 次数限制:使用一个变量 hint_used 计数,注意每场BOSS战重置。建议在函数内定义,每次调用 run_boss_fight 时从0开始。

七、测试建议

编写单元测试 tests/test_boss_fight_loop.py,至少覆盖:

  1. 战斗指令解析:测试各种合法输入(中英文、简写)返回正确动作;测试非法输入(数量超出范围、类型错误、参数缺失)返回 None。
  2. 系统指令整合:模拟 handle_system_command,验证在循环中被正确调用。
  3. hint 次数限制:模拟连续调用4次 hint,前3次成功,第4次被拒绝。
  4. 战斗流程:模拟玩家和BOSS交替行动,验证游戏结束时的返回值。
  5. 退出指令:输入 quit,验证返回 'quit' 且循环结束。

可以使用 mock 对象模拟 BossFight 的方法,避免真实的游戏逻辑。

八、提交要求

  • 创建 src/game_logic/boss_fight_loop.py,包含 parse_battle_commandrun_boss_fight 函数。
  • 创建 tests/test_boss_fight_loop.py,包含单元测试。
  • 代码注释清晰,关键逻辑有说明。
  • 确保所有测试通过。

开始实现吧!

posted @ 2026-03-17 23:54  神秘园欢迎您  阅读(22)  评论(0)    收藏  举报