From dca3698dc62d473a1a52d19f1fbd5495ea6dbf54 Mon Sep 17 00:00:00 2001 From: John Yang Date: Tue, 12 Aug 2025 00:29:10 +0000 Subject: [PATCH 1/3] Add battlecode basic code --- codeclash/games/__init__.py | 12 ++++++++---- codeclash/games/battlecode/main.py | 22 ++++++++++++++++++++++ configs/battlecode.yaml | 8 ++++++++ docker/BattleCode.Dockerfile | 12 ++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 codeclash/games/battlecode/main.py create mode 100644 configs/battlecode.yaml create mode 100644 docker/BattleCode.Dockerfile diff --git a/codeclash/games/__init__.py b/codeclash/games/__init__.py index 776b2448..d996b8bf 100644 --- a/codeclash/games/__init__.py +++ b/codeclash/games/__init__.py @@ -1,4 +1,5 @@ from codeclash.games.abstract import CodeGame +from codeclash.games.battlecode.main import BattleCodeGame from codeclash.games.battlesnake.main import BattleSnakeGame from codeclash.games.corewar.main import CoreWarGame from codeclash.games.robocode.main import RoboCodeGame @@ -8,10 +9,13 @@ # might consider postponing imports to avoid loading things we don't need def get_game(config: dict) -> CodeGame: game = { - BattleSnakeGame.name: BattleSnakeGame, - CoreWarGame.name: CoreWarGame, - RoboCodeGame.name: RoboCodeGame, - RobotRumbleGame.name: RobotRumbleGame, + x.name: x for x in [ + BattleCodeGame, + BattleSnakeGame, + CoreWarGame, + RoboCodeGame, + RobotRumbleGame + ] }.get(config["game"]["name"]) if game is None: raise ValueError(f"Unknown game: {config['game']['name']}") diff --git a/codeclash/games/battlecode/main.py b/codeclash/games/battlecode/main.py new file mode 100644 index 00000000..0dd98ce9 --- /dev/null +++ b/codeclash/games/battlecode/main.py @@ -0,0 +1,22 @@ +from typing import Any + +from codeclash.games.abstract import CodeGame + + +class BattleCodeGame(CodeGame): + name: str = "BattleCode" + + def __init__(self, config): + super().__init__(config) + assert len(config["players"]) == 2, "BattleCode is a two-player game" + self.run_cmd_round: str = "python run.py run" + + def determine_winner(self, agents: list[Any]): + response = self.environment.execute(f"tail -2 {self.round_log_path}") + self.scoreboard.append((self.round, "BLAH")) # TODO + + def execute_round(self, agents: list[Any]): + args = [f"/{agent.name}/src/" for agent in agents] + cmd = f"{self.run_cmd_round}" # TODO + response = self.environment.execute(cmd) + assert response["returncode"] == 0, response \ No newline at end of file diff --git a/configs/battlecode.yaml b/configs/battlecode.yaml new file mode 100644 index 00000000..4805237a --- /dev/null +++ b/configs/battlecode.yaml @@ -0,0 +1,8 @@ +game: + name: BattleCode + rounds: 2 +players: +- agent: dummy + name: p1 +- agent: dummy + name: p2 diff --git a/docker/BattleCode.Dockerfile b/docker/BattleCode.Dockerfile new file mode 100644 index 00000000..b884dbcf --- /dev/null +++ b/docker/BattleCode.Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.12-slim-bookworm + +ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential git curl && \ + rm -rf /var/lib/apt/lists/* + +RUN git clone https://github.com/emagedoc/BattleCode.git /testbed + +WORKDIR /testbed + +RUN python run.py update \ No newline at end of file From cfed8f799520a0d23260f9a7f2ffa53c3af743c6 Mon Sep 17 00:00:00 2001 From: John Yang Date: Tue, 12 Aug 2025 07:16:32 +0000 Subject: [PATCH 2/3] Determine winner works --- codeclash/games/battlecode/main.py | 28 +++++++++++++++++++++------- configs/battlecode.yaml | 2 ++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/codeclash/games/battlecode/main.py b/codeclash/games/battlecode/main.py index 0dd98ce9..586bdb4d 100644 --- a/codeclash/games/battlecode/main.py +++ b/codeclash/games/battlecode/main.py @@ -1,5 +1,7 @@ +import re from typing import Any +from codeclash.constants import RESULT_TIE from codeclash.games.abstract import CodeGame @@ -10,13 +12,25 @@ def __init__(self, config): super().__init__(config) assert len(config["players"]) == 2, "BattleCode is a two-player game" self.run_cmd_round: str = "python run.py run" - + for arg, val in config.get("args", {}).items(): + if isinstance(val, bool): + if val: + self.run_cmd_round += f" --{arg}" + else: + self.run_cmd_round += f" --{arg} {val}" + def determine_winner(self, agents: list[Any]): - response = self.environment.execute(f"tail -2 {self.round_log_path}") - self.scoreboard.append((self.round, "BLAH")) # TODO - + response = self.environment.execute(f"tail -3 {self.round_log_path} | head -1") + winner = re.search(r"\s\((.*)\)\swins\s\(", response["output"]).group(1) + winner = {"A": agents[0].name, "B": agents[1].name}.get(winner, RESULT_TIE) + self.scoreboard.append((self.round, winner)) + def execute_round(self, agents: list[Any]): - args = [f"/{agent.name}/src/" for agent in agents] - cmd = f"{self.run_cmd_round}" # TODO + args = [ + f" --p{idx+1}-dir /{agent.name}/src/ --p{idx+1} {agent.name}" + for idx, agent in enumerate(agents) + ] + cmd = f"{self.run_cmd_round} {' '.join(args)} > {self.round_log_path}" + print(f"Running command: {cmd}") response = self.environment.execute(cmd) - assert response["returncode"] == 0, response \ No newline at end of file + assert response["returncode"] == 0, response diff --git a/configs/battlecode.yaml b/configs/battlecode.yaml index 4805237a..4ef5e401 100644 --- a/configs/battlecode.yaml +++ b/configs/battlecode.yaml @@ -1,6 +1,8 @@ game: name: BattleCode rounds: 2 + args: + maps: quack players: - agent: dummy name: p1 From 32aa2981b03b13756b3d199b92116d5ad1063fbf Mon Sep 17 00:00:00 2001 From: John Yang Date: Tue, 12 Aug 2025 22:01:47 +0000 Subject: [PATCH 3/3] BattleCode working --- codeclash/games/abstract.py | 2 ++ codeclash/games/battlecode/main.py | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/codeclash/games/abstract.py b/codeclash/games/abstract.py index 82cee7cd..641b5dcf 100644 --- a/codeclash/games/abstract.py +++ b/codeclash/games/abstract.py @@ -140,6 +140,8 @@ def run_round(self, agents: list[Any]): self._pre_round_setup(agents) self.execute_round(agents) self.determine_winner(agents) + last_winner = self.scoreboard[-1][1] + print(f"Round {self.round} winner: {last_winner}") self._post_round_setup(agents) @property diff --git a/codeclash/games/battlecode/main.py b/codeclash/games/battlecode/main.py index 586bdb4d..663f71f7 100644 --- a/codeclash/games/battlecode/main.py +++ b/codeclash/games/battlecode/main.py @@ -1,7 +1,7 @@ import re from typing import Any -from codeclash.constants import RESULT_TIE +from codeclash.constants import DIR_WORK, RESULT_TIE from codeclash.games.abstract import CodeGame @@ -26,8 +26,13 @@ def determine_winner(self, agents: list[Any]): self.scoreboard.append((self.round, winner)) def execute_round(self, agents: list[Any]): + for agent in agents: + src, dest = f"/{agent.name}/src/mysubmission/", str( + DIR_WORK / "src" / agent.name + ) + self.environment.execute(f"cp -r {src} {dest}") args = [ - f" --p{idx+1}-dir /{agent.name}/src/ --p{idx+1} {agent.name}" + f"--p{idx+1}-dir src --p{idx+1} {agent.name}" for idx, agent in enumerate(agents) ] cmd = f"{self.run_cmd_round} {' '.join(args)} > {self.round_log_path}"