LovecaSim / ai /utils /battle_benchmark.py
trioskosmos's picture
Upload ai/utils/battle_benchmark.py with huggingface_hub
ae55ffb verified
import os
import time
import engine_rust
def run_battle():
# Load DB
db_path = "data/cards_compiled.json"
if not os.path.exists(db_path):
print(f"Error: {db_path} not found")
return
with open(db_path, "r", encoding="utf-8") as f:
db_json = f.read()
db = engine_rust.PyCardDatabase(db_json)
# Models
models = {
"Original (New)": "original",
"Simple (Old)": "simple",
}
# Battle setup
contestants = list(models.keys())
results = {c: {"wins": 0, "games": 0} for c in contestants}
# Deck setup (Standard starter)
lives0 = [30000, 30001, 30002]
deck0 = [
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
] + lives0
deck1 = deck0.copy()
lives1 = lives0.copy()
print(f"{'Battle':<20} | {'Winner':<20} | {'Duration'}")
print("-" * 60)
# Round robin (partial for speed, can adjust)
for i in range(len(contestants)):
for j in range(i + 1, len(contestants)):
p0_name = contestants[i]
p1_name = contestants[j]
p0_agent = models[p0_name]
p1_agent = models[p1_name]
# Run 1 game
start = time.time()
game = engine_rust.PyGameState(db)
game.initialize_game(deck0, deck1, [], [], lives0, lives1)
step_count = 0
while not game.is_terminal() and step_count < 1000:
curr_p = game.current_player
phase = game.phase
turn = game.turn
agent_name = p0_name if curr_p == 0 else p1_name
agent = p0_agent if curr_p == 0 else p1_agent
print(f"\n{'=' * 40}")
print(f"Step {step_count} | Turn {turn} | Player {curr_p} ({agent_name}) | Phase {phase}")
# Show Legal Actions
legal_ids = game.get_legal_action_ids()
print(f" Legal Actions ({len(legal_ids)}): {legal_ids}")
if phase in [-1, 0]:
sel = game.get_player(curr_p).mulligan_selection
print(f" Mulligan Selection Mask: {sel:016b}")
if isinstance(agent, engine_rust.PyHybridMCTS):
stats = agent.get_suggestions(game, 0, 0.5)
else:
stats = game.search_mcts(0, 0.5, agent)
# Show Top 5
stats.sort(key=lambda x: x[2], reverse=True)
print(" Top Actions (MCTS Visits/Score):")
for act, score, visits in stats[:5]:
print(f" - Action {act:<4}: Score {score:.4f}, Visits {visits}")
# Trace Best Path (Next 3 steps)
if stats and step_count % 10 == 0:
print(" Predicted Best Path (Simulated):")
try:
# Attempt to use copy() or fallback to manual property copy
if hasattr(game, "copy"):
temp_game = game.copy()
else:
temp_game = engine_rust.PyGameState(db)
temp_game.current_player = game.current_player
temp_game.first_player = game.first_player
temp_game.phase = game.phase
temp_game.turn = game.turn
temp_game.set_player(0, game.get_player(0))
temp_game.set_player(1, game.get_player(1))
path_str = []
trace_curr = temp_game
for depth in range(3):
p = trace_curr.current_player
ph = trace_curr.phase
t = trace_curr.turn
# For the first step, use the MCTS top action
if depth == 0:
best_act = stats[0][0]
else:
# Quick MCTS search for lookahead (100 sims)
t_stats = trace_curr.search_mcts(100, 0.0, "original")
if not t_stats:
break
t_stats.sort(key=lambda x: x[2], reverse=True)
best_act = t_stats[0][0]
path_str.append(f"[T{t} P{p} Ph{ph} Act{best_act}]")
trace_curr.step(best_act)
if trace_curr.is_terminal():
break
print(f" {' -> '.join(path_str)}")
except Exception as e:
print(f" (Path trace failed: {e})")
action = stats[0][0] if stats else 0
game.step(action)
step_count += 1
# Check for progress
if step_count % 10 == 0:
p0 = game.get_player(0)
p1 = game.get_player(1)
print(
f"--- P0: Lives {len(p0.success_lives)}, Hand {len(p0.hand)} | P1: Lives {len(p1.success_lives)}, Hand {len(p1.hand)}"
)
winner_idx = game.get_winner()
winner_name = "Draw"
if winner_idx == 0:
winner_name = p0_name
results[p0_name]["wins"] += 1
elif winner_idx == 1:
winner_name = p1_name
results[p1_name]["wins"] += 1
results[p0_name]["games"] += 1
results[p1_name]["games"] += 1
elapsed = time.time() - start
print(f"{p0_name} vs {p1_name:<10} | {winner_name:<20} | {elapsed:.1f}s")
print("\nFinal Scoreboard:")
for name, stats in results.items():
wr = (stats["wins"] / stats["games"] * 100) if stats["games"] > 0 else 0
print(f"{name:<20}: {stats['wins']}/{stats['games']} ({wr:.1f}% Win Rate)")
if __name__ == "__main__":
run_battle()