Skip to content

Commit db27002

Browse files
committed
Add BattleSnake human solutions
1 parent 5709d5c commit db27002

3 files changed

Lines changed: 167 additions & 6 deletions

File tree

codeclash/arenas/battlesnake/BattleSnake.Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ RUN apt-get update \
99
&& apt-get install -y --no-install-recommends \
1010
curl ca-certificates python3.10 python3.10-venv \
1111
python3-pip python-is-python3 wget git build-essential jq curl locales \
12+
nodejs npm ruby-full psmisc \
1213
&& rm -rf /var/lib/apt/lists/*
1314

1415
# Set architecture and install Go 1.22
@@ -26,3 +27,7 @@ WORKDIR /workspace
2627

2728
RUN cd game && go build -o battlesnake ./cli/battlesnake/main.go
2829
RUN pip install -r requirements.txt
30+
31+
# Rust toolchain (stable) for compiling Rust submissions from source (never commit binaries)
32+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
33+
ENV PATH=/root/.cargo/bin:$PATH

codeclash/arenas/battlesnake/battlesnake.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def __init__(self, config, **kwargs):
3434
self.run_cmd_round += f" --{arg} {val}"
3535
self._failed_to_start_player = []
3636

37-
def _wait_for_ports(self, requested_ports: list[int], timeout: float = 60.0) -> list[int]:
37+
def _wait_for_ports(self, requested_ports: list[int], timeout: float = 180.0) -> list[int]:
3838
"""Wait for ports to be served, up to timeout seconds.
3939
4040
Returns:
@@ -84,6 +84,16 @@ def _run_single_simulation(self, player2port: dict[str, int], idx: int) -> str:
8484
)
8585
return response["output"]
8686

87+
def _start_cmd(self, agent: Player) -> str:
88+
"""Command to start the submission's server on $PORT. Submissions are either the
89+
Python starter (main.py) or any other language via a run.sh launch script, which
90+
compiles from source and starts the server (we commit source, never binaries).
91+
run.sh runs from the copied codebase, so slow compiles are covered by the
92+
(generous) port-wait timeout rather than a separate build step."""
93+
if "run.sh" in self.environment.execute(f"ls /{agent.name}")["output"]:
94+
return "bash run.sh"
95+
return f"python {self.submission}"
96+
8797
def execute_round(self, agents: list[Player]):
8898
self._failed_to_start_player = []
8999
assert len(agents) > 1, "Battlesnake requires at least two players"
@@ -92,9 +102,9 @@ def execute_round(self, agents: list[Player]):
92102
for idx, agent in enumerate(agents):
93103
port = 8001 + idx
94104
player2port[agent.name] = port
95-
# Surprisingly slow despite using &
96-
# Start server in background - just add & to run in background!
97-
self.environment.execute(f"PORT={port} python {self.submission} &", cwd=f"/{agent.name}")
105+
# Start server in background (& ). Submission may be Python (main.py) or any
106+
# other language via a run.sh launch script.
107+
self.environment.execute(f"PORT={port} {self._start_cmd(agent)} &", cwd=f"/{agent.name}")
98108

99109
self.logger.debug(f"Waiting for ports: {player2port}")
100110
available_ports = self._wait_for_ports(list(player2port.values()))
@@ -129,8 +139,12 @@ def execute_round(self, agents: list[Player]):
129139
for future in tqdm(as_completed(futures), total=len(futures)):
130140
future.result()
131141
finally:
132-
# Kill all python servers when done
142+
# Kill all servers started this round (any language) so ports free up for the
143+
# next round. pkill covers the Python starter; fuser frees each game port for
144+
# compiled/interpreted servers launched via run.sh.
133145
self.environment.execute(f"pkill -f 'python {self.submission}' || true")
146+
for port in player2port.values():
147+
self.environment.execute(f"fuser -k {port}/tcp 2>/dev/null || true")
134148

135149
def get_results(self, agents: list[Player], round_num: int, stats: RoundStats):
136150
scores = defaultdict(int)
@@ -163,7 +177,13 @@ def get_results(self, agents: list[Player], round_num: int, stats: RoundStats):
163177
stats.player_stats[player].score = score
164178

165179
def validate_code(self, agent: Player) -> tuple[bool, str | None]:
166-
if self.submission not in agent.environment.execute("ls")["output"]:
180+
listing = agent.environment.execute("ls")["output"]
181+
# Non-Python submissions declare how to launch their server via run.sh (any
182+
# language). We trust the launch script here; a broken one is caught at runtime
183+
# by _wait_for_ports (failed-to-start -> forfeit).
184+
if "run.sh" in listing:
185+
return True, None
186+
if self.submission not in listing:
167187
return False, f"No {self.submission} file found in the root directory"
168188
# note: no longer calling splitlines
169189
bot_content = agent.environment.execute(f"cat {self.submission}")["output"]
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Round-robin over ALL imported human BattleSnakes, used to RANK them (skill Phase 3).
2+
# Run: uv run python scripts/make_ladder.py configs/ablations/ladder/make_battlesnake.yaml
3+
# Then rank from the logs with codeclash/analysis/metrics/win_rate.py (or elo.py).
4+
#
5+
# Params: args match the main BattleSnake configs (11x11, browser off). Following the
6+
# make_robotrumble.yaml ladder precedent, tournament.rounds: 0 (dummy players never edit
7+
# code -> just play the baseline game once per pair) and sims_per_round: 250 for a stable
8+
# win-rate signal. (The main head-to-head configs use s1000; RobotRumble's ladder used 250.)
9+
#
10+
# 55 players -> 55*54/2 = 1485 pairwise tournaments. Cost is dominated by per-pair container
11+
# setup, so sims_per_round can stay generous without changing total runtime much.
12+
# NOTE: all human/* branches are pushed to CodeClash-ai/BattleSnake; Docker must be running.
13+
# Native bots (snork/esproso/bobby-witt/hettie) compile/run their real source in-container.
14+
tournament:
15+
rounds: 0
16+
game:
17+
name: BattleSnake
18+
sims_per_round: 250
19+
args:
20+
width: 11
21+
height: 11
22+
browser: false
23+
players:
24+
- agent: dummy
25+
branch_init: human/aleksiy325/snek-two
26+
- agent: dummy
27+
branch_init: human/altersaddle/untimely-neglected-wearable
28+
- agent: dummy
29+
branch_init: human/ccSnake2018/ccsnake
30+
- agent: dummy
31+
branch_init: human/ChaelCodes/cornelius
32+
- agent: dummy
33+
branch_init: human/ChaelCodes/hettie
34+
- agent: dummy
35+
branch_init: human/coreyja/amphibious-arthur
36+
- agent: dummy
37+
branch_init: human/coreyja/bombastic-bob
38+
- agent: dummy
39+
branch_init: human/coreyja/coreyja-rs
40+
- agent: dummy
41+
branch_init: human/coreyja/devious-devin
42+
- agent: dummy
43+
branch_init: human/coreyja/eremetic-eric
44+
- agent: dummy
45+
branch_init: human/coreyja/famished-frank
46+
- agent: dummy
47+
branch_init: human/coreyja/gigantic-george
48+
- agent: dummy
49+
branch_init: human/coreyja/improbable-irene
50+
- agent: dummy
51+
branch_init: human/coreyja/jump-flooding
52+
- agent: dummy
53+
branch_init: human/csauve/bookworm
54+
- agent: dummy
55+
branch_init: human/Flipez/flipez-crystal
56+
- agent: dummy
57+
branch_init: human/graeme-hill/snakebot
58+
- agent: dummy
59+
branch_init: human/hirethissnake/sneaky-snake
60+
- agent: dummy
61+
branch_init: human/jackisherwood/battlesnake-elon
62+
- agent: dummy
63+
branch_init: human/JerryKott/jerrykott-2017
64+
- agent: dummy
65+
branch_init: human/jhawthorn/snek
66+
- agent: dummy
67+
branch_init: human/joshhartmann11/battlejake
68+
- agent: dummy
69+
branch_init: human/joshhartmann11/battlejake2019
70+
- agent: dummy
71+
branch_init: human/kentmacdonald2/beames
72+
- agent: dummy
73+
branch_init: human/L4r0x/snork
74+
- agent: dummy
75+
branch_init: human/m-schier/kreuzotter
76+
- agent: dummy
77+
branch_init: human/MorganConrad/sisiutl
78+
- agent: dummy
79+
branch_init: human/MorganConrad/tantilla
80+
- agent: dummy
81+
branch_init: human/moxuz/pinky-snek
82+
- agent: dummy
83+
branch_init: human/nbw/nbw-crystal
84+
- agent: dummy
85+
branch_init: human/nbw/nbw-ruby
86+
- agent: dummy
87+
branch_init: human/Nettogrof/nessegrev-java
88+
- agent: dummy
89+
branch_init: human/Nettogrof/nessegrev-julia
90+
- agent: dummy
91+
branch_init: human/noahspriggs/tr-8r
92+
- agent: dummy
93+
branch_init: human/OliverMKing/astar-snake
94+
- agent: dummy
95+
branch_init: human/pambrose/pambrose-kotlin
96+
- agent: dummy
97+
branch_init: human/Petah/project-z
98+
- agent: dummy
99+
branch_init: human/rdbrck/bountysnake2017
100+
- agent: dummy
101+
branch_init: human/rdbrck/bountysnake2018
102+
- agent: dummy
103+
branch_init: human/rdbrck/btas
104+
- agent: dummy
105+
branch_init: human/smallsco/robosnake
106+
- agent: dummy
107+
branch_init: human/Spenca/vulture-snake
108+
- agent: dummy
109+
branch_init: human/tbgiles/feisty-snake
110+
- agent: dummy
111+
branch_init: human/Tch1b0/esproso
112+
- agent: dummy
113+
branch_init: human/TheApX/hungry
114+
- agent: dummy
115+
branch_init: human/tim-hub/awesome-snake
116+
- agent: dummy
117+
branch_init: human/tphummel/bobby-witt
118+
- agent: dummy
119+
branch_init: human/tyrelh/tyrelh-2018
120+
- agent: dummy
121+
branch_init: human/tyrelh/tyrelh-2019
122+
- agent: dummy
123+
branch_init: human/tyrelh/tyrelh-python
124+
- agent: dummy
125+
branch_init: human/woofers/woofers-java
126+
- agent: dummy
127+
branch_init: human/Xe/since
128+
- agent: dummy
129+
branch_init: human/xtagon/nagini
130+
- agent: dummy
131+
branch_init: human/zacpez/scape-goat
132+
- agent: dummy
133+
branch_init: human/zakwht/zakwht-2018
134+
prompts:
135+
game_description: |-
136+
BattleSnake ladder

0 commit comments

Comments
 (0)