283 lines
9.4 KiB
Python
283 lines
9.4 KiB
Python
#!/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 <name>")
|
|
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 <name>' 加载场景")
|
|
|
|
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 <filename>")
|
|
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 <filename>")
|
|
# 列出可用的存档
|
|
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() |