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/abstract.py b/codeclash/games/abstract.py index 95ad7a56..7eb5c869 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 new file mode 100644 index 00000000..663f71f7 --- /dev/null +++ b/codeclash/games/battlecode/main.py @@ -0,0 +1,41 @@ +import re +from typing import Any + +from codeclash.constants import DIR_WORK, RESULT_TIE +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" + 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 -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]): + 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 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 diff --git a/configs/battlecode.yaml b/configs/battlecode.yaml new file mode 100644 index 00000000..4ef5e401 --- /dev/null +++ b/configs/battlecode.yaml @@ -0,0 +1,10 @@ +game: + name: BattleCode + rounds: 2 + args: + maps: quack +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