Skip to content

Commit 9e5892d

Browse files
committed
Add Huskybench basic scaffolding
1 parent 5721961 commit 9e5892d

7 files changed

Lines changed: 90 additions & 6 deletions

File tree

codeclash/games/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from codeclash.games.corewar.corewar import CoreWarGame
66
from codeclash.games.dummy.dummy_game import DummyGame
77
from codeclash.games.game import CodeGame
8+
from codeclash.games.huskybench.huskybench import HuskyBenchGame
89
from codeclash.games.robocode.robocode import RoboCodeGame
910
from codeclash.games.robotrumble.robotrumble import RobotRumbleGame
1011

@@ -18,6 +19,7 @@ def get_game(config: dict, *, tournament_id: str, local_output_dir: Path) -> Cod
1819
BattleSnakeGame,
1920
CoreWarGame,
2021
DummyGame,
22+
HuskyBenchGame,
2123
RoboCodeGame,
2224
RobotRumbleGame,
2325
]

codeclash/games/dummy/main.py

Whitespace-only changes.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from pathlib import Path
2+
3+
from codeclash.agents.player import Player
4+
from codeclash.games.game import CodeGame, RoundData, RoundStats
5+
6+
7+
class HuskyBenchGame(CodeGame):
8+
name: str = "HuskyBench"
9+
10+
def __init__(self, config, *, tournament_id: str, local_output_dir: Path):
11+
super().__init__(config, tournament_id=tournament_id, local_output_dir=local_output_dir)
12+
self.run_cmd_round: str = (
13+
f"python engine/main.py --port 8000 --sim --sim-rounds {self.game_config['sims_per_round']}"
14+
)
15+
for arg, val in self.game_config.get("args", {}).items():
16+
if isinstance(val, bool):
17+
if val:
18+
self.run_cmd_round += f" --{arg}"
19+
else:
20+
self.run_cmd_round += f" --{arg} {val}"
21+
22+
def get_stats(self, result_outputs: list[str], agents: list[Player]) -> RoundStats:
23+
return RoundStats(winner="N/A", scores={})
24+
25+
def execute_round(self, agents: list[Player]) -> RoundData:
26+
try:
27+
self.logger.debug("Starting game servers")
28+
self.environment.execute(self.run_cmd_round + " > engine/output/std_out.log &")
29+
for agent in agents:
30+
self.environment.execute("python client/main.py --port 8000 &", cwd=f"/{agent.name}")
31+
32+
# Save logs to response
33+
self.logger.info(self.environment.execute("cat engine/output/std_out.log")["output"])
34+
35+
return RoundData(logs=[], results=[])
36+
finally:
37+
# Kill all python servers when done
38+
self.environment.execute("pkill -f 'python client/main.py' || true")
39+
self.environment.execute("pkill -f 'python engine/main.py' || true")

codeclash/tournaments/pvp.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616

1717

1818
class PvpTournament(AbstractTournament):
19-
def __init__(self, config: dict, *, cleanup: bool = False, push_agent: bool = False):
19+
def __init__(self, config: dict, *, cleanup: bool = False, push: bool = False):
2020
super().__init__(config, name="PvpTournament")
2121
self.cleanup_on_end = cleanup
22-
self.push_agent = push_agent
22+
self.push = push
2323
self.game: CodeGame = get_game(
2424
self.config,
2525
tournament_id=self.tournament_id,
@@ -124,6 +124,6 @@ def end(self) -> None:
124124
"""Save output files, clean up game resources and push agents if requested."""
125125
(self.local_output_dir / "metadata.json").write_text(json.dumps(self.get_metadata(), indent=2))
126126
self.game.end(self.cleanup_on_end)
127-
if self.push_agent:
127+
if self.push:
128128
for agent in self.agents:
129129
agent.push()

configs/test/dummy_huskybench.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tournament:
2+
rounds: 3
3+
game:
4+
name: HuskyBench
5+
sims_per_round: 1
6+
players:
7+
- agent: dummy
8+
name: p1
9+
- agent: dummy
10+
name: p2
11+
prompts:
12+
game_description: |
13+
You are a software developer ({{player_id}}) competing in a coding game called CoreWar.
14+
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.
15+
Victory comes from crafting clever tactics—replicators, scanners, bombers—that exploit memory layout and instruction timing to control the core.
16+
17+
The game is played in {{rounds}} rounds. For every round, you (and your competitor) edit program code that controls your bot. This is round {{round}}.
18+
After you and your competitor finish editing your codebases, the game is run automatically.
19+
20+
Your task: improve the bot in `warriors/warrior.red`, located in {{working_dir}}.
21+
{{working_dir}} is your codebase, which contains both your bot and supporting assets.

docker/HuskyBench.Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM python:3.10-slim
2+
3+
ARG DEBIAN_FRONTEND=noninteractive
4+
ENV TZ=Etc/UTC
5+
6+
RUN apt update && apt install -y \
7+
wget \
8+
git \
9+
build-essential \
10+
unzip \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
ARG GITHUB_TOKEN
14+
RUN git clone https://${GITHUB_TOKEN}@github.com/emagedoc/HuskyBench.git /testbed \
15+
&& cd /testbed \
16+
&& git remote set-url origin https://github.com/emagedoc/HuskyBench.git \
17+
&& unset GITHUB_TOKEN
18+
19+
WORKDIR /testbed
20+
21+
RUN pip install -r engine/requirements.txt
22+
RUN mkdir -p /testbed/engine/output

main.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
from codeclash.utils.yaml_utils import resolve_includes
99

1010

11-
def main(config_path: Path, *, cleanup: bool = False, push_agent: bool = False):
11+
def main(config_path: Path, *, cleanup: bool = False, push: bool = False):
1212
yaml_content = config_path.read_text()
1313
preprocessed_yaml = resolve_includes(yaml_content, base_dir=CONFIG_DIR)
1414
config = yaml.safe_load(preprocessed_yaml)
15-
training = PvpTournament(config, cleanup=cleanup, push_agent=push_agent)
15+
training = PvpTournament(config, cleanup=cleanup, push=push)
1616
training.run()
1717

1818

@@ -31,7 +31,7 @@ def main_cli(argv: list[str] | None = None):
3131
)
3232
parser.add_argument(
3333
"-p",
34-
"--push_agent",
34+
"--push",
3535
action="store_true",
3636
help="If set, push each agent's codebase to a new repository after running.",
3737
)

0 commit comments

Comments
 (0)