任务:实现 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_state和boss_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 解析逻辑
- 将输入字符串转为小写(方便匹配),并去除首尾空格。
- 尝试匹配系统指令(不在此函数处理,由外层先调用
handle_system_command)。 - 分割单词:
parts = cmd.split() - 根据第一个词判断指令类型:
- 如果
parts[0]在['follow', '跟牌', '跟']中,则为跟牌指令。- 需要检查
len(parts) == 3,否则返回 None。 num_cards尝试转为整数,且在1-3之间。type_str为parts[2],映射为CardType:- 'good' 或 '善' ->
CardType.GOOD - 'evil' 或 '恶' ->
CardType.EVIL - 'neutral' 或 '中立' ->
CardType.NEUTRAL
- 'good' 或 '善' ->
- 如果转换失败,返回 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_context和llm_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,至少覆盖:
- 战斗指令解析:测试各种合法输入(中英文、简写)返回正确动作;测试非法输入(数量超出范围、类型错误、参数缺失)返回 None。
- 系统指令整合:模拟
handle_system_command,验证在循环中被正确调用。 - hint 次数限制:模拟连续调用4次 hint,前3次成功,第4次被拒绝。
- 战斗流程:模拟玩家和BOSS交替行动,验证游戏结束时的返回值。
- 退出指令:输入
quit,验证返回'quit'且循环结束。
可以使用 mock 对象模拟 BossFight 的方法,避免真实的游戏逻辑。
八、提交要求
- 创建
src/game_logic/boss_fight_loop.py,包含parse_battle_command和run_boss_fight函数。 - 创建
tests/test_boss_fight_loop.py,包含单元测试。 - 代码注释清晰,关键逻辑有说明。
- 确保所有测试通过。
开始实现吧!

浙公网安备 33010602011771号