#!/usr/bin/env python3 """ KARDS战斗引擎交互式测试环境 提供完整的命令行界面用于测试和调试战斗系统 """ import sys import os import pickle import readline from pathlib import Path # 添加项目路径 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from kards_battle.core.battle_engine import BattleEngine from kards_battle.units.unit_loader import load_unit, list_all_units from interactive.battle_visualizer import BattleVisualizer from interactive.command_parser import CommandParser from interactive.test_scenarios import TestScenarios class InteractiveTest: """交互式测试环境主类""" def __init__(self): self.engine = None self.visualizer = None self.parser = None self.scenarios = TestScenarios() self.running = True self.save_dir = Path("interactive/saves") self.save_dir.mkdir(exist_ok=True) # 设置命令历史和自动补全 self.setup_readline() # 初始化引擎 self.reset_engine() def setup_readline(self): """设置readline自动补全和历史""" # 历史文件 histfile = Path("interactive/.history") try: readline.read_history_file(histfile) except FileNotFoundError: pass # 设置自动补全 - 兼容不同系统 try: readline.set_completer(self.completer) # 尝试不同的绑定方式以兼容WSL if 'libedit' in readline.__doc__: readline.parse_and_bind("bind ^I rl_complete") else: readline.parse_and_bind('tab: complete') # WSL额外设置 readline.parse_and_bind('set show-all-if-ambiguous on') readline.parse_and_bind('set completion-ignore-case on') except: # 如果自动补全失败,继续运行但不使用该功能 print("提示: Tab自动补全在当前环境不可用") # 保存历史 import atexit atexit.register(readline.write_history_file, histfile) def completer(self, text, state): """自动补全函数""" if state == 0: # 生成补全选项 if self.parser: self.matches = self.parser.get_command_suggestions(text) else: self.matches = [] try: return self.matches[state] except IndexError: return None def reset_engine(self, player1: str = "Germany", player2: str = "USA"): """重置战斗引擎""" self.engine = BattleEngine(player1, player2, debug_mode=True) self.visualizer = BattleVisualizer(self.engine) self.parser = CommandParser(self.engine) print(f"\n✨ 新游戏开始: {player1} vs {player2}") def run(self): """主循环""" self.show_welcome() self.visualizer.display() while self.running: try: # 获取输入 prompt = self.get_prompt() user_input = input(prompt).strip() # 快速退出 if user_input.lower() in ['q', 'quit', 'exit']: self.running = False continue # 处理命令 if user_input: self.process_command(user_input) except KeyboardInterrupt: print("\n\n使用 'quit' 命令退出") continue except EOFError: self.running = False break self.show_goodbye() def get_prompt(self) -> str: """生成命令提示符 - 无颜色版本以避免显示问题""" player_id = self.engine.active_player player_name = self.engine.player_names[player_id] turn = self.engine.current_turn kredits = self.engine.get_kredits(player_id) kredits_slot = self.engine.get_kredits_slot(player_id) # 使用简洁的无颜色格式 prompt = f"\nP{player_id+1}:{player_name} [回合{turn}] K:{kredits}/{kredits_slot} > " return prompt def process_command(self, user_input: str): """处理用户命令""" # 特殊命令处理 if user_input.startswith('scenario'): self.handle_scenario(user_input) return elif user_input == 'scenarios': self.list_scenarios() return elif user_input.startswith('save'): self.save_state(user_input) return elif user_input.startswith('load'): self.load_state(user_input) return # 使用命令解析器 success, message = self.parser.parse(user_input) # 处理特殊返回值 if message == "RESET_ENGINE": self.reset_engine() self.visualizer.display() elif message == "SHOW_HELP": self.visualizer.display_help() elif message.startswith("detailed="): detailed = message.split('=')[1] == 'True' self.visualizer.display(detailed) elif message.startswith("unit_info:"): index = int(message.split(':')[1]) self.show_unit_info(index) else: # 显示命令结果 if message: print(message) # 如果命令成功,刷新显示 if success and user_input.split()[0] in ['deploy', 'd', 'dp', 'move', 'm', 'mv', 'attack', 'a', 'at', 'endturn', 'end']: self.visualizer.display() def handle_scenario(self, user_input: str): """处理场景命令""" parts = user_input.split() if len(parts) < 2: print("用法: scenario ") self.list_scenarios() return scenario_name = parts[1] if self.scenarios.load_scenario(scenario_name, self.engine): print(f"✅ 成功加载场景: {scenario_name}") self.visualizer.display() else: print(f"❌ 未知场景: {scenario_name}") self.list_scenarios() def list_scenarios(self): """列出所有场景""" print("\n=== 可用测试场景 ===") for name, desc in self.scenarios.list_scenarios().items(): print(f" {name:12} - {desc}") print("\n使用 'scenario ' 加载场景") def show_unit_info(self, index: int): """显示单位信息""" unit = self.parser._get_unit_by_index(index) if unit: self.visualizer.display_unit_info(unit) else: print(f"找不到索引为 {index} 的单位") def save_state(self, user_input: str): """保存当前状态""" parts = user_input.split() if len(parts) < 2: print("用法: save ") return filename = self.save_dir / f"{parts[1]}.sav" try: with open(filename, 'wb') as f: state = { 'engine': self.engine, 'history': self.parser.history } pickle.dump(state, f) print(f"✅ 状态已保存到: {filename}") except Exception as e: print(f"❌ 保存失败: {str(e)}") def load_state(self, user_input: str): """加载保存的状态""" parts = user_input.split() if len(parts) < 2: print("用法: load ") # 列出可用的存档 saves = list(self.save_dir.glob("*.sav")) if saves: print("\n可用存档:") for save in saves: print(f" - {save.stem}") return filename = self.save_dir / f"{parts[1]}.sav" try: with open(filename, 'rb') as f: state = pickle.load(f) self.engine = state['engine'] self.visualizer = BattleVisualizer(self.engine) self.parser = CommandParser(self.engine) self.parser.history = state['history'] print(f"✅ 状态已加载: {filename}") self.visualizer.display() except FileNotFoundError: print(f"❌ 文件不存在: {filename}") except Exception as e: print(f"❌ 加载失败: {str(e)}") def show_welcome(self): """显示欢迎信息""" c = BattleVisualizer.COLORS welcome = f""" {c['BOLD']}{c['CYAN']}{'='*60}{c['RESET']} {c['BOLD']} KARDS 战斗引擎 - 交互式测试环境 v1.0{c['RESET']} {c['CYAN']}{'='*60}{c['RESET']} {c['GREEN']}欢迎使用交互式测试环境!{c['RESET']} 快速开始: • 输入 {c['YELLOW']}help{c['RESET']} 查看所有命令 • 输入 {c['YELLOW']}list{c['RESET']} 查看可用单位 • 输入 {c['YELLOW']}scenarios{c['RESET']} 查看测试场景 • 输入 {c['YELLOW']}quit{c['RESET']} 或 {c['YELLOW']}q{c['RESET']} 退出 {c['CYAN']}提示: 使用 Tab 键自动补全命令{c['RESET']} {c['CYAN']}{'='*60}{c['RESET']} """ print(welcome) def show_goodbye(self): """显示退出信息""" c = BattleVisualizer.COLORS print(f"\n{c['GREEN']}感谢使用!再见!{c['RESET']}\n") def main(): """主函数""" test = InteractiveTest() test.run() if __name__ == "__main__": main()