Skip to content

Commit 7d1bf76

Browse files
committed
Merge main into Singularity support
2 parents eda8648 + 5acbedb commit 7d1bf76

82 files changed

Lines changed: 5560 additions & 1190 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
# Required: GitHub token with repo access for cloning game repositories
55
GITHUB_TOKEN=your_github_token_here
66

7-
# Optional: LLM Provider API Keys (configure the ones you plan to use)
7+
# LLM provider API keys — set the ones for the models you run (see configs/models.yaml).
8+
# Models are resolved by litellm from their provider-prefixed names.
9+
ANTHROPIC_API_KEY=
810
OPENAI_API_KEY=
9-
ANTHROPIC_API_KEY=
11+
GEMINI_API_KEY=
12+
XAI_API_KEY=
13+
DASHSCOPE_API_KEY=

.github/mlc_config.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@
1818
{
1919
"pattern": "https://docs\\.codeclash\\.io"
2020
},
21+
{
22+
"pattern": "https://robotrumble\\.org/boards/2"
23+
},
24+
{
25+
"pattern": "https://robocode\\.sourceforge\\.io.*"
26+
},
27+
{
28+
"pattern": "https://huskybench\\.com/.*"
29+
},
30+
{
31+
"pattern": "https://corewar\\.co\\.uk"
32+
},
2133
{
2234
"pattern": "https?://(.*\\.)?twitter\\.com/.*"
2335
},
@@ -26,6 +38,9 @@
2638
},
2739
{
2840
"pattern": "https://www\\.contributor-covenant\\.org/version/2/1/code_of_conduct\\.html"
41+
},
42+
{
43+
"pattern": "https://join\\.slack\\.com/t/swe-bench/shared_invite/.*"
2944
}
3045
]
3146
}

.github/workflows/check-links.yaml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,29 @@ name: Check Markdown links
22

33
on:
44
workflow_dispatch:
5-
push:
65
pull_request:
76
schedule:
87
- cron: "0 0 1 * *"
98

109
jobs:
11-
markdown-link-check:
10+
markdown-link-check-pr:
11+
if: github.event_name == 'pull_request'
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@master
14+
- uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
- uses: gaurav-nelson/github-action-markdown-link-check@v1
18+
with:
19+
config-file: '.github/mlc_config.json'
20+
check-modified-files-only: 'yes'
21+
base-branch: 'main'
22+
23+
markdown-link-check-full:
24+
if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
1528
- uses: gaurav-nelson/github-action-markdown-link-check@v1
1629
with:
1730
config-file: '.github/mlc_config.json'

.github/workflows/pre-commit.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Pre-commit
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
pre-commit:
9+
runs-on: ubuntu-latest
10+
defaults:
11+
run:
12+
shell: bash -l {0}
13+
steps:
14+
- uses: actions/checkout@v5
15+
- uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.11"
18+
- name: Install uv
19+
run: curl -LsSf https://astral.sh/uv/install.sh | sh
20+
- name: Install dependencies
21+
run: uv sync --extra dev
22+
- name: Run pre-commit
23+
run: uv run pre-commit run --all-files --show-diff-on-failure

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ repos:
1515
- id: debug-statements
1616

1717
- repo: https://github.com/crate-ci/typos
18-
rev: v1
18+
rev: v1.46.0
1919
hooks:
2020
- id: typos
2121
files: \.(py|md|rst|yaml|toml)

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,23 @@ Critically, *LMs don't play the game directly*.
9696
Their code serves as their competitive proxy.
9797
The winner is the LM agent who wins the most rounds.
9898

99+
## 🧩 Available Arenas
100+
101+
CodeClash includes competitive programming games and simulation-backed arenas, including BattleSnake,
102+
Bomberland, CoreWar, CybORG, Halite, HuskyBench, RoboCode, RobotRumble, and SCML.
103+
104+
Bomberland is a Bomberman-style grid arena based on Coder One's Bomberland competition. Agents edit
105+
a Python `bomberland_agent.py` implementation and compete to maximize average score across seeded
106+
simulations through survival, damage, kills, and destructible-block control.
107+
108+
SCML is a supply-chain negotiation arena based on the ANAC Supply Chain Management League OneShot
109+
track. Agents edit a Python `scml_agent.py` implementation and compete to maximize average profit
110+
across multiple simulated supply-chain worlds.
111+
112+
CybORG is a simulated cyber-defense arena based on the CAGE Challenge 3 DroneSwarm scenario. Agents
113+
edit a Python `cyborg_agent.py` implementation and compete to maximize blue-team reward across
114+
simulated episodes.
115+
99116
## 🚀 Get Involved
100117

101118
- Check out our [docs](https://docs.codeclash.ai/) for more details on running different arenas, configuring tournaments, etc.

codeclash/agents/minisweagent.py

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33
import traceback
44
from collections.abc import Callable
55

6-
from minisweagent import Model
76
from minisweagent.agents.default import AgentConfig, DefaultAgent
8-
from minisweagent.models import get_model
9-
from minisweagent.models.test_models import DeterministicModel
10-
from minisweagent.run.utils.save import save_traj
7+
from minisweagent.models import Model, get_model
118

129
from codeclash import REPO_DIR
1310
from codeclash.agents.player import Player
@@ -21,10 +18,8 @@
2118

2219

2320
class ClashAgent(DefaultAgent):
24-
"""
25-
Slightly modified version of `DefaultAgent` from mini-SWE-agent
26-
(https://github.com/SWE-agent/mini-swe-agent)
27-
"""
21+
"""`DefaultAgent` from mini-SWE-agent (https://github.com/SWE-agent/mini-swe-agent)
22+
with per-player debug logging."""
2823

2924
def __init__(
3025
self,
@@ -38,9 +33,11 @@ def __init__(
3833
super().__init__(model, env, config_class=config_class, **kwargs)
3934
self.logger = logger
4035

41-
def add_message(self, role: str, content: str, **kwargs):
42-
super().add_message(role, content, **kwargs)
43-
self.logger.debug(f"[{role}] {content}", extra={"highlighter": None})
36+
def add_messages(self, *messages: dict) -> list[dict]:
37+
result = super().add_messages(*messages)
38+
for m in messages:
39+
self.logger.debug(f"[{m.get('role')}] {m.get('content')}", extra={"highlighter": None})
40+
return result
4441

4542

4643
class MiniSWEAgent(Player):
@@ -50,26 +47,21 @@ def __init__(self, config: dict, environment: ContainerEnvironment, game_context
5047
super().__init__(config, environment=environment, game_context=game_context)
5148

5249
def run(self):
53-
# temporary workaround around https://github.com/SWE-agent/mini-swe-agent/issues/477
54-
if "DeterministicModel" not in self.config["config"]["model"].get("model_class", ""):
55-
model = get_model(config=self.config["config"]["model"])
56-
else:
57-
model = DeterministicModel(outputs=self.config["config"]["model"]["outputs"])
50+
model = get_model(config=self.config["config"]["model"])
5851
self.agent = ClashAgent(
5952
model=model,
6053
env=self.environment,
6154
logger=self.logger,
6255
**self.config["config"]["agent"],
6356
)
6457
exit_status = None
65-
result = None
6658
exc_message = None
6759
try:
68-
exit_status, result = self.agent.run(task="", **self.game_context.to_template_vars())
60+
result = self.agent.run(task="", **self.game_context.to_template_vars())
61+
exit_status = result.get("exit_status", "")
6962
except Exception as e:
7063
exit_status = str(e)
7164
exc_message = traceback.format_exc()
72-
result = exc_message
7365
self.logger.critical(exc_message)
7466
finally:
7567
traj_path = (
@@ -78,22 +70,16 @@ def run(self):
7870
/ self.name
7971
/ f"{self.name}_r{self.game_context.round}.traj.json"
8072
)
81-
save_traj(
82-
self.agent, # type: ignore
83-
traj_path,
84-
exit_status=exit_status,
85-
result=result,
86-
print_fct=self.logger.debug,
87-
)
73+
self.agent.save(traj_path)
8874
copy_to_container(
8975
self.environment,
9076
traj_path,
9177
self.game_context.log_env / "edits" / traj_path.name,
9278
)
9379
self._metadata["agent_stats"][self.game_context.round] = {
9480
"exit_status": exit_status,
95-
"cost": self.agent.model.cost,
96-
"api_calls": self.agent.model.n_calls,
81+
"cost": self.agent.cost,
82+
"api_calls": self.agent.n_calls,
9783
}
9884
if exit_status.lower().strip() not in ["", "submitted", "limitsexceeded"] and exc_message is not None:
9985
raise RuntimeError(f"Agent {self.name} failed with exit status: {exit_status} and exception: {exc_message}")

codeclash/agents/player.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def __init__(
3434
log_path=self.game_context.log_local / "players" / self.name / "player.log",
3535
emoji="👤",
3636
)
37+
self._branch_name = config.get("branch", f"{self.game_context.id}.{self.name}")
3738
self._metadata = {
3839
"name": self.name,
3940
"player_unique_id": self._player_unique_id,
@@ -45,10 +46,6 @@ def __init__(
4546
"agent_stats": {}, # mapping round -> agent stats
4647
}
4748

48-
if branch := config.get("branch_init"):
49-
self.logger.info(f"Checking out branch {branch}")
50-
assert_zero_exit_code(self.environment.execute(f"git checkout {branch}"), logger=self.logger)
51-
5249
if self.push:
5350
self.logger.info("Will push agent gameplay as branch to remote repository after each round")
5451
token = os.getenv("GITHUB_TOKEN")
@@ -60,6 +57,33 @@ def __init__(
6057
]:
6158
assert_zero_exit_code(self.environment.execute(cmd), logger=self.logger)
6259

60+
# Handle branch initialization
61+
if branch_init := config.get("branch_init"):
62+
# Fetch from remote first (handles branches pushed in previous tournaments)
63+
# Then checkout - git will create tracking branch if needed
64+
assert_zero_exit_code(
65+
self.environment.execute(f"git fetch origin && git checkout {branch_init}"),
66+
logger=self.logger,
67+
)
68+
self.logger.info(f"Checked out initial branch {branch_init}")
69+
70+
if self._branch_name != branch_init:
71+
self.logger.info(f"Switching to branch {self._branch_name} for pushing changes")
72+
# First fetch to see if the branch exists on remote
73+
assert_zero_exit_code(
74+
self.environment.execute("git fetch origin"),
75+
logger=self.logger,
76+
)
77+
# Try to checkout the branch - git will track remote if it exists there
78+
checkout_result = self.environment.execute(f"git checkout {self._branch_name}")
79+
if checkout_result.get("returncode", 0) != 0:
80+
# Branch doesn't exist locally or remotely, create it
81+
self.logger.info(f"Branch {self._branch_name} doesn't exist, creating it")
82+
assert_zero_exit_code(
83+
self.environment.execute(f"git checkout -b {self._branch_name}"),
84+
logger=self.logger,
85+
)
86+
6387
# --- Main methods ---
6488

6589
def pre_run_hook(self, *, new_round: int) -> None:
@@ -103,7 +127,7 @@ def post_run_hook(self, *, round: int) -> None:
103127

104128
if self.push:
105129
for cmd in [
106-
f"git push origin {self._branch_name}",
130+
f"git push -u origin {self._branch_name}",
107131
"git push origin --tags",
108132
]:
109133
assert_zero_exit_code(self.environment.execute(cmd), logger=self.logger)
@@ -154,11 +178,6 @@ def _tag_round(self, round: int) -> None:
154178
)
155179
self._metadata["round_tags"][round] = tag
156180

157-
@property
158-
def _branch_name(self) -> str:
159-
"""Get the branch name for the agent's codebase."""
160-
return f"{self.game_context.id}.{self.name}"
161-
162181
def _get_round_tag_name(self, round: int) -> str:
163182
"""Get git tag name for the version of the codebase at the given round."""
164183
return f"{self._player_unique_id}-round-{round}"

codeclash/analysis/llm_as_judge/hallucination.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ system_prompt: |
7171
by reasoning)
7272
- "My bot is working perfectly" (this is just a slightly overconfident statement, but not a concrete claim that can be corroborated or disproven)
7373
This violates 2 (the statement of fact is not concrete)
74-
- Agent using an incorrect linenumber when referring to a code snippet (as long as the agent recovers later on and this doens't cause an edit to
74+
- Agent using an incorrect linenumber when referring to a code snippet (as long as the agent recovers later on and this doesn't cause an edit to
7575
fail without being able to recover). This violates 6 (the incident is not relevant to the overall trajectory and objective of the agent)
7676
- Anything related to failed edits as long as the failure is spotted and corrected later on.
7777

codeclash/arenas/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
from codeclash.arenas.battlecode24.battlecode24 import BattleCode24Arena
44
from codeclash.arenas.battlecode25.battlecode25 import BattleCode25Arena
55
from codeclash.arenas.battlesnake.battlesnake import BattleSnakeArena
6+
from codeclash.arenas.bomberland.bomberland import BomberlandArena
67
from codeclash.arenas.bridge.bridge import BridgeArena
78
from codeclash.arenas.chess.chess import ChessArena
89
from codeclash.arenas.corewar.corewar import CoreWarArena
10+
from codeclash.arenas.cyborg.cyborg import CybORGArena
911
from codeclash.arenas.dummy.dummy import DummyArena
1012
from codeclash.arenas.figgie.figgie import FiggieArena
1113
from codeclash.arenas.gomoku.gomoku import GomokuArena
@@ -15,15 +17,18 @@
1517
from codeclash.arenas.huskybench.huskybench import HuskyBenchArena
1618
from codeclash.arenas.robocode.robocode import RoboCodeArena
1719
from codeclash.arenas.robotrumble.robotrumble import RobotRumbleArena
20+
from codeclash.arenas.scml.scml import SCMLOneShotArena
1821

1922
ARENAS = [
2023
BattleCode23Arena,
2124
BattleCode24Arena,
2225
BattleCode25Arena,
2326
BattleSnakeArena,
27+
BomberlandArena,
2428
BridgeArena,
2529
ChessArena,
2630
CoreWarArena,
31+
CybORGArena,
2732
DummyArena,
2833
FiggieArena,
2934
GomokuArena,
@@ -33,6 +38,7 @@
3338
HuskyBenchArena,
3439
RoboCodeArena,
3540
RobotRumbleArena,
41+
SCMLOneShotArena,
3642
]
3743

3844

0 commit comments

Comments
 (0)