Skip to content

Commit f4ea06b

Browse files
Add Gomoku arena (#88)
Co-authored-by: John Yang <byjohnyang@gmail.com>
1 parent 5d83b63 commit f4ea06b

5 files changed

Lines changed: 126 additions & 0 deletions

File tree

codeclash/arenas/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from codeclash.arenas.bridge.bridge import BridgeArena
55
from codeclash.arenas.corewar.corewar import CoreWarArena
66
from codeclash.arenas.dummy.dummy import DummyArena
7+
from codeclash.arenas.gomoku.gomoku import GomokuArena
78
from codeclash.arenas.halite.halite import HaliteArena
89
from codeclash.arenas.halite2.halite2 import Halite2Arena
910
from codeclash.arenas.halite3.halite3 import Halite3Arena
@@ -17,6 +18,7 @@
1718
BridgeArena,
1819
CoreWarArena,
1920
DummyArena,
21+
GomokuArena,
2022
HaliteArena,
2123
Halite2Arena,
2224
Halite3Arena,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM ubuntu:22.04
2+
3+
ENV DEBIAN_FRONTEND=noninteractive
4+
5+
# Install Python 3.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 \
10+
&& rm -rf /var/lib/apt/lists/*
11+
12+
# Clone Gomoku game repository
13+
RUN git clone https://github.com/CodeClash-ai/Gomoku.git /workspace \
14+
&& cd /workspace \
15+
&& git remote set-url origin https://github.com/CodeClash-ai/Gomoku.git
16+
WORKDIR /workspace
17+
18+
# No additional dependencies needed - engine uses only standard library

codeclash/arenas/gomoku/__init__.py

Whitespace-only changes.

codeclash/arenas/gomoku/gomoku.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import re
2+
3+
from codeclash.agents.player import Player
4+
from codeclash.arenas.arena import CodeArena, RoundStats
5+
from codeclash.constants import RESULT_TIE
6+
from codeclash.utils.environment import assert_zero_exit_code
7+
8+
GOMOKU_LOG = "result.log"
9+
10+
11+
class GomokuArena(CodeArena):
12+
name: str = "Gomoku"
13+
submission: str = "main.py"
14+
description: str = """Your bot (`main.py`) controls a Gomoku player on a 15x15 board.
15+
Players take turns placing stones. Win by connecting 5 stones in a row (horizontally, vertically, or diagonally).
16+
Black plays first.
17+
18+
Your bot must implement:
19+
def get_move(board: list[list[int]], color: str) -> tuple[int, int]
20+
21+
Board representation: 0=empty, 1=black, 2=white
22+
Color: "black" or "white"
23+
"""
24+
25+
def __init__(self, config, **kwargs):
26+
super().__init__(config, **kwargs)
27+
assert len(config["players"]) == 2, "Gomoku is a two-player game"
28+
29+
def execute_round(self, agents: list[Player]) -> None:
30+
args = [f"/{agent.name}/{self.submission}" for agent in agents]
31+
cmd = f"python engine.py {' '.join(args)} -r {self.game_config['sims_per_round']} > {self.log_env / GOMOKU_LOG};"
32+
self.logger.info(f"Running game: {cmd}")
33+
assert_zero_exit_code(self.environment.execute(cmd))
34+
35+
def get_results(self, agents: list[Player], round_num: int, stats: RoundStats):
36+
with open(self.log_round(round_num) / GOMOKU_LOG) as f:
37+
round_log = f.read()
38+
lines = round_log.split("FINAL_RESULTS")[-1].splitlines()
39+
40+
scores = {}
41+
for line in lines:
42+
match = re.search(r"Bot\_(\d)\_main:\s(\d+)\srounds\swon", line)
43+
if match:
44+
bot_id = match.group(1)
45+
rounds_won = int(match.group(2))
46+
scores[agents[int(bot_id) - 1].name] = rounds_won
47+
48+
# Handle draws
49+
draw_match = re.search(r"Draws:\s(\d+)", round_log)
50+
if draw_match:
51+
draws = int(draw_match.group(1))
52+
if draws > 0:
53+
scores[RESULT_TIE] = draws
54+
55+
stats.winner = max(scores, key=scores.get) if scores else "unknown"
56+
# Check for tie (equal scores)
57+
if scores:
58+
max_score = max(scores.values())
59+
winners_with_max = [k for k, v in scores.items() if v == max_score and k != RESULT_TIE]
60+
if len(winners_with_max) > 1:
61+
stats.winner = RESULT_TIE
62+
63+
stats.scores = scores
64+
for player, score in scores.items():
65+
if player != RESULT_TIE:
66+
stats.player_stats[player].score = score
67+
68+
def validate_code(self, agent: Player) -> tuple[bool, str | None]:
69+
if self.submission not in agent.environment.execute("ls")["output"]:
70+
return False, f"No {self.submission} file found in the root directory"
71+
72+
bot_content = agent.environment.execute(f"cat {self.submission}")["output"]
73+
74+
if "def get_move(" not in bot_content:
75+
return (
76+
False,
77+
f"{self.submission} must define a get_move(board, color) function. "
78+
"See the game description for the required signature."
79+
)
80+
81+
return True, None

configs/test/gomoku.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tournament:
2+
rounds: 3
3+
game:
4+
name: Gomoku
5+
sims_per_round: 10
6+
args:
7+
board_size: 15
8+
timeout: 10
9+
players:
10+
- agent: dummy
11+
name: p1
12+
- agent: dummy
13+
name: p2
14+
prompts:
15+
game_description: |
16+
You are a software developer ({{player_id}}) competing in a coding game called Gomoku.
17+
Your bot (`main.py`) controls a Gomoku player on a 15x15 board.
18+
Players take turns placing stones. Win by connecting 5 stones in a row (horizontally, vertically, or diagonally).
19+
Black plays first.
20+
21+
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}}.
22+
After you and your competitor finish editing your codebases, the game is run automatically.
23+
24+
Your task: improve the bot in `main.py`, located in {{working_dir}}.
25+
{{working_dir}} is your codebase, which contains both your bot and supporting assets.

0 commit comments

Comments
 (0)