MCPcopy Create free account
hub / github.com/CodeClash-ai/CodeClash / CoreWarArena

Class CoreWarArena

codeclash/arenas/corewar/corewar.py:13–107  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

11COREWAR_LOG = "sim_{idx}.log"
12TRACE_RAW = "trace_raw.txt"
13
14
15class CoreWarArena(CodeArena):
16 name: str = "CoreWar"
17 description: str = """CoreWar is a programming battle where you write "warriors" in an assembly-like language called Redcode to compete within a virtual machine (MARS), aiming to eliminate your rivals by making their code self-terminate.
18Victory comes from crafting clever tactics—replicators, scanners, bombers—that exploit memory layout and instruction timing to control the core.
19
20Reading the logs: each round's score is computed over many simulated battles, but only a sample of them are saved as replay traces (`sim_*.jsonl`) in `/logs/` due to storage limits. The score reflects every battle played, not just the replayed subset, so treat the replays as representative examples rather than the full basis of the result."""
21 submission: str = "warrior.red"
22
23 def __init__(self, config, **kwargs):
24 super().__init__(config, **kwargs)
25 # Always run in brief mode (`-b`). Without it, pmars prints disassembled listing of
26 # every warrior, which leaks opponent
27 self.run_cmd_round: str = "./src/pmars -b"
28 for arg, val in self.game_config.get("args", self.default_args).items():
29 if isinstance(val, bool):
30 if val:
31 self.run_cmd_round += f" -{arg}"
32 else:
33 self.run_cmd_round += f" -{arg} {val}"
34
35 def _record_count(self) -> int:
36 # Bounded subset of scored battles to record for replay (recording is the costly part).
37 sims = self.game_config["sims_per_round"]
38 return max(1, min(sims, self.game_config.get("record_battles", 100)))
39
40 def _run_single_simulation(self, agents: list[Player], idx: int):
41 # Shift agents by idx to vary starting positions
42 agents = agents[idx:] + agents[:idx]
43 args = [f"/{agent.name}/{self.submission}" for agent in agents]
44 n = self.game_config["sims_per_round"]
45 log = self.log_env / COREWAR_LOG.format(idx=idx)
46 if idx == 0:
47 # Record R scored battles with -T + the rest plain; both score blocks append to one
48 # log (get_results sums them), so replays are genuine scored battles. i == agents[i].
49 r = self._record_count()
50 self._trace_agent_names = [agent.name for agent in agents]
51 parts = [f"{self.run_cmd_round} {shlex.join(args)} -r {r} -T {self.log_env / TRACE_RAW} >> {log}"]
52 if n - r > 0:
53 parts.append(f"{self.run_cmd_round} {shlex.join(args)} -r {n - r} >> {log}")
54 cmd = f"rm -f {log}; " + "; ".join(parts) + ";"
55 else:
56 cmd = f"{self.run_cmd_round} {shlex.join(args)} -r {n} > {log};"
57 self.logger.info(f"Running game: {cmd}")
58 response = self.environment.execute(cmd)
59 assert response["returncode"] == 0, response
60
61 def execute_round(self, agents: list[Player]):
62 with ThreadPoolExecutor(self.game_config.get("sim_concurrency", 4)) as executor:
63 futures = [executor.submit(self._run_single_simulation, agents, idx) for idx in range(len(agents))]
64 for future in as_completed(futures):
65 future.result()
66
67 def copy_logs_from_env(self, round_num: int) -> None:
68 # Distill the -T battles into sim_{i}.jsonl + trace.md on the host, then drop the raw stream.
69 super().copy_logs_from_env(round_num)
70 raw = self.log_round(round_num) / TRACE_RAW

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected