Skip to content

Commit 61c8ba6

Browse files
committed
Add support for robotrumble json output parsing
1 parent 8b1910b commit 61c8ba6

1 file changed

Lines changed: 53 additions & 22 deletions

File tree

codeclash/games/robotrumble/robotrumble.py

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import shlex
23
import subprocess
34
from collections import Counter
@@ -14,17 +15,26 @@ class RobotRumbleGame(CodeGame):
1415
name: str = "RobotRumble"
1516
description: str = """RobotRumble is a turn-based coding battle where you program a team of robots in Python to move, attack, and outmaneuver your opponent on a grid.
1617
Every decision is driven by your code, and victory comes from crafting logic that positions robots smartly, times attacks well, and adapts over the 100-turn match."""
18+
default_args: dict = {"raw": True}
1719
submission: str = "robot.js"
1820

1921
def __init__(self, config, **kwargs):
2022
super().__init__(config, **kwargs)
2123
assert len(config["players"]) == 2, "RobotRumble is a two-player game"
2224
self.run_cmd_round: str = "./rumblebot run term"
25+
self.sim_ext = "txt"
26+
for arg, val in self.game_config.get("args", self.default_args).items():
27+
if isinstance(val, bool):
28+
if val:
29+
self.run_cmd_round += f" --{arg}"
30+
if arg == "raw":
31+
self.sim_ext = "json"
32+
else:
33+
self.run_cmd_round += f" --{arg} {val}"
2334

24-
def _run_single_simulation(self, agents: list[Player], idx: int) -> str:
35+
def _run_single_simulation(self, agents: list[Player], idx: int, cmd: str) -> str:
2536
"""Run a single robotrumble simulation and return the output."""
26-
args = [f"/{agent.name}/{self.submission}" for agent in agents]
27-
cmd = f"{self.run_cmd_round} {shlex.join(args)} > {self.log_env / f'sim_{idx}.txt'}"
37+
cmd = f"{cmd} > {self.log_env / f'sim_{idx}.{self.sim_ext}'}"
2838

2939
# https://github.com/emagedoc/CodeClash/issues/62 (timeouts)
3040
try:
@@ -40,42 +50,63 @@ def _run_single_simulation(self, agents: list[Player], idx: int) -> str:
4050

4151
def execute_round(self, agents: list[Player]):
4252
self.logger.info(f"Running game with players: {[agent.name for agent in agents]}")
53+
args = [f"/{agent.name}/{self.submission}" for agent in agents]
54+
cmd = f"{self.run_cmd_round} {shlex.join(args)}"
4355

4456
with ThreadPoolExecutor(20) as executor:
4557
# Submit all simulations to the thread pool
4658
futures = [
47-
executor.submit(self._run_single_simulation, agents, idx)
59+
executor.submit(self._run_single_simulation, agents, idx, cmd)
4860
for idx in range(self.game_config.get("sims_per_round", 100))
4961
]
5062

63+
# Log command being run
64+
self.logger.info(f"Running game: {cmd}")
65+
5166
# Collect results as they complete
5267
for future in tqdm(as_completed(futures), total=len(futures)):
5368
future.result()
5469

70+
def _get_winner_txt(self, output_file: str, agents: list[Player]) -> str:
71+
with open(output_file) as f:
72+
lines = f.read().strip().split("\n")
73+
74+
# Get the last 2 lines which contain the game result (same as original)
75+
relevant_lines = lines[-2:] if len(lines) >= 2 else lines
76+
log_text = "\n".join(relevant_lines)
77+
78+
if "Blue won" in log_text:
79+
return agents[0].name
80+
elif "Red won" in log_text:
81+
return agents[1].name
82+
elif "it was a tie" in log_text:
83+
return RESULT_TIE
84+
return RESULT_TIE
85+
86+
def _get_winner_json(self, output_file: str, agents: list[Player]) -> str:
87+
with open(output_file) as f:
88+
data = json.load(f)
89+
if "winner" in data:
90+
if data["winner"] == "Blue":
91+
return agents[0].name
92+
elif data["winner"] == "Red":
93+
return agents[1].name
94+
else:
95+
return RESULT_TIE
96+
return RESULT_TIE
97+
5598
def get_results(self, agents: list[Player], round_num: int, stats: RoundStats):
5699
winners = []
57100
for idx in range(self.game_config.get("sims_per_round", 100)):
58-
output_file = self.log_round(round_num) / f"sim_{idx}.txt"
101+
output_file = self.log_round(round_num) / f"sim_{idx}.{self.sim_ext}"
59102
if not output_file.exists():
60103
self.logger.warning(f"Simulation {idx} not found, skipping")
61104
continue
62-
with open(output_file) as f:
63-
lines = f.read().strip().split("\n")
64-
65-
# Get the last 2 lines which contain the game result (same as original)
66-
relevant_lines = lines[-2:] if len(lines) >= 2 else lines
67-
log_text = "\n".join(relevant_lines)
68-
69-
if "Blue won" in log_text:
70-
winner = agents[0].name
71-
winners.append(winner)
72-
elif "Red won" in log_text:
73-
winner = agents[1].name
74-
winners.append(winner)
75-
elif "it was a tie" in log_text:
76-
winners.append(RESULT_TIE)
77-
else:
78-
winners.append(RESULT_TIE)
105+
winners.append(
106+
self._get_winner_txt(output_file, agents)
107+
if self.sim_ext == "txt"
108+
else self._get_winner_json(output_file, agents)
109+
)
79110

80111
# Count occurrences of each winner
81112
counts = Counter(winners)

0 commit comments

Comments
 (0)