Skip to content

Commit 692c776

Browse files
committed
Add Halite3 assets; RobotRumble supports python
1 parent 39c8f5a commit 692c776

9 files changed

Lines changed: 104 additions & 11 deletions

File tree

codeclash/agents/player.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ def __init__(
4747
"agent_stats": {}, # mapping round -> agent stats
4848
}
4949

50+
if "branch_init" in config:
51+
self.logger.info(f"Initializing codebase from branch {config['branch_init']}")
52+
assert_zero_exit_code(self.environment.execute(f"git checkout {config['branch_init']}"), logger=self.logger)
53+
5054
if self.push:
5155
self.logger.info("Will push agent gameplay as branch to remote repository after each round")
5256
token = os.getenv("GITHUB_TOKEN")

codeclash/analysis/transparent/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def analyze_temporal_opponent_access(folder_list):
131131

132132

133133
if __name__ == "__main__":
134+
# `normal` is all 1v1 Halite games from main results
134135
normal = sorted(
135136
[
136137
x.parent

codeclash/analysis/viz/heatmap_win_rates.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ def main(log_dir: Path, unit: str = "rounds", output_file: Path = ASSETS_DIR / "
6868
if i != j and results[(m1, m2)][1] > 0:
6969
matrix[i, j] = results[(m1, m2)][0] / results[(m1, m2)][1]
7070

71+
# Print out average win rate per model
72+
for m1 in models:
73+
total_wins = sum(results[(m1, m2)][0] for m2 in models if m1 != m2)
74+
total_matches = sum(results[(m1, m2)][1] for m2 in models if m1 != m2)
75+
avg_win_rate = total_wins / total_matches if total_matches > 0 else 0
76+
print(f"{MODEL_TO_DISPLAY_NAME[m1.split('/')[-1]]}: {avg_win_rate:.2%} win rate over {total_matches} matches")
77+
7178
# Plot
7279
FONT_BOLD.set_size(18)
7380
_, ax = plt.subplots(figsize=(10, 8))

codeclash/games/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from codeclash.games.dummy.dummy_game import DummyGame
55
from codeclash.games.game import CodeGame
66
from codeclash.games.halite.halite import HaliteGame
7-
from codeclash.games.halite2.halite2 import Halite2Game
7+
8+
# from codeclash.games.halite2.halite2 import Halite2Game # WIP
9+
# from codeclash.games.halite3.halite3 import Halite3Game # WIP
810
from codeclash.games.huskybench.huskybench import HuskyBenchGame
911
from codeclash.games.robocode.robocode import RoboCodeGame
1012
from codeclash.games.robotrumble.robotrumble import RobotRumbleGame
@@ -15,7 +17,6 @@
1517
CoreWarGame,
1618
DummyGame,
1719
HaliteGame,
18-
Halite2Game,
1920
HuskyBenchGame,
2021
RoboCodeGame,
2122
RobotRumbleGame,

codeclash/games/halite3/__init__.py

Whitespace-only changes.

codeclash/games/halite3/halite3.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from codeclash.games.halite.halite import HaliteGame
2+
3+
4+
class Halite3Game(HaliteGame):
5+
name: str = "Halite-III"
6+
description: str = ""
7+
default_args: dict = {}
8+
submission: str = "submission"

codeclash/games/robotrumble/robotrumble.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
from codeclash.constants import RESULT_TIE
1111
from codeclash.games.game import CodeGame, RoundStats
1212

13+
MAP_EXT_TO_HEADER = {
14+
"js": "function robot(state, unit) {",
15+
"py": "def robot(state, unit):",
16+
}
17+
ROBOTRUMBLE_HIDDEN_EXEC = ".codeclash_exec"
18+
1319

1420
class RobotRumbleGame(CodeGame):
1521
name: str = "RobotRumble"
@@ -51,7 +57,10 @@ def _run_single_simulation(self, agents: list[Player], idx: int, cmd: str):
5157

5258
def execute_round(self, agents: list[Player]):
5359
self.logger.info(f"Running game with players: {[agent.name for agent in agents]}")
54-
args = [f"/{agent.name}/{self.submission}" for agent in agents]
60+
args = []
61+
for agent in agents:
62+
executable = agent.environment.execute(f"cat {ROBOTRUMBLE_HIDDEN_EXEC}")["output"].strip()
63+
args.append(f"/{agent.name}/{executable}")
5564
cmd = f"{self.run_cmd_round} {shlex.join(args)}"
5665
self.logger.info(f"Running game: {cmd}")
5766

@@ -131,21 +140,33 @@ def get_results(self, agents: list[Player], round_num: int, stats: RoundStats):
131140
stats.player_stats[player].score = score
132141

133142
def validate_code(self, agent: Player) -> tuple[bool, str | None]:
134-
if self.submission not in agent.environment.execute("ls")["output"]:
135-
return False, f"There should be a `{self.submission}` file"
136-
if "function robot(state, unit) {" not in agent.environment.execute(f"cat {self.submission}")["output"]:
143+
# Determine if robot.js or robot.py exists
144+
ext, exists = None, False
145+
for possible_ext in MAP_EXT_TO_HEADER.keys():
146+
exists_output = agent.environment.execute(f"test -f robot.{possible_ext} && echo 'exists'")["output"]
147+
if "exists" == exists_output.strip():
148+
ext = possible_ext
149+
exists = True
150+
break
151+
if not exists:
152+
return False, "There should be a `robot.js` or `robot.py` file"
153+
agent.environment.execute(f'echo "robot.{ext}" > {ROBOTRUMBLE_HIDDEN_EXEC}')
154+
155+
# Check that the robot function is defined
156+
header = MAP_EXT_TO_HEADER[ext]
157+
if header not in agent.environment.execute(f"cat robot.{ext}")["output"]:
137158
return (
138159
False,
139-
f"{self.submission} does not contain the required robot function. It should be defined as 'function robot(state, unit) {{ ... }}'.",
160+
f"robot.{ext} does not contain the required robot function. It should be defined as '{header}'.",
140161
)
141-
test_run_cmd = f"{self.run_cmd_round} {self.submission} {self.submission} -t 1"
162+
test_run_cmd = f"{self.run_cmd_round} robot.{ext} robot.{ext} -t 1"
142163
try:
143-
test_run = agent.environment.execute(test_run_cmd, timeout=60)["output"]
164+
test_run = agent.environment.execute(test_run_cmd, timeout=10)["output"]
144165
except subprocess.TimeoutExpired:
145166
return (
146167
False,
147-
f"Running {self.submission} (with `{test_run_cmd}`) timed out (60 seconds). Please ensure your code runs efficiently.",
168+
f"Running robot.{ext} (with `{test_run_cmd}`) timed out (10 seconds). Please ensure your code runs efficiently.",
148169
)
149170
if "Some errors occurred:" in test_run:
150-
return False, f"Running {self.submission} (with `{test_run_cmd}`) resulted in errors:\n{test_run}"
171+
return False, f"Running robot.{ext} (with `{test_run_cmd}`) resulted in errors:\n{test_run}"
151172
return True, None
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: Halite-III
5+
sims_per_round: 250
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 an arena called Halite.
14+
In this game, you will write code to control ships that gather resources, build structures, and compete for dominance on a grid-based map.
15+
Victory is achieved by outmaneuvering opponents, optimizing resource collection, and strategically expanding your territory.
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, located in {{working_dir}}.
21+
{{working_dir}} is your codebase, which contains both your bot and supporting assets.

docker/Halite-III.Dockerfile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
FROM ubuntu:22.04
2+
3+
ENV DEBIAN_FRONTEND=noninteractive
4+
5+
# Install Python 3.10 (and alias python→python3.10), pip, and prerequisites
6+
RUN apt-get update \
7+
&& apt-get install -y --no-install-recommends \
8+
curl ca-certificates python3.10 python3.10-venv \
9+
python3-pip python-is-python3 wget git build-essential jq curl locales cmake \
10+
&& rm -rf /var/lib/apt/lists/*
11+
12+
# Install Rust via rustup
13+
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \
14+
&& . "$HOME/.cargo/env" \
15+
&& echo 'source $HOME/.cargo/env' >> /etc/bash.bashrc
16+
ENV PATH="/root/.cargo/bin:${PATH}"
17+
18+
# Install ocaml
19+
RUN apt-get update && apt-get install -y ocaml ocamlbuild
20+
21+
# Clone Halite repository
22+
ARG GITHUB_TOKEN
23+
RUN git clone https://${GITHUB_TOKEN}@github.com/emagedoc/Halite-III.git /workspace \
24+
&& cd /workspace \
25+
&& git remote set-url origin https://github.com/emagedoc/Halite-III.git \
26+
&& unset GITHUB_TOKEN
27+
28+
WORKDIR /workspace
29+
30+
RUN cd game_engine && cmake . && make

0 commit comments

Comments
 (0)