Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions codeclash/agents/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __init__(
log_path=self.game_context.log_local / "players" / self.name / "player.log",
emoji="👤",
)
self._branch_name = config.get("branch", f"{self.game_context.id}.{self.name}")
self._metadata = {
"name": self.name,
"player_unique_id": self._player_unique_id,
Expand All @@ -46,10 +47,6 @@ def __init__(
"agent_stats": {}, # mapping round -> agent stats
}

if branch := config.get("branch_init"):
self.logger.info(f"Checking out branch {branch}")
assert_zero_exit_code(self.environment.execute(f"git checkout {branch}"), logger=self.logger)

if self.push:
self.logger.info("Will push agent gameplay as branch to remote repository after each round")
token = os.getenv("GITHUB_TOKEN")
Expand All @@ -61,6 +58,33 @@ def __init__(
]:
assert_zero_exit_code(self.environment.execute(cmd), logger=self.logger)

# Handle branch initialization
if branch_init := config.get("branch_init"):
# Fetch from remote first (handles branches pushed in previous tournaments)
# Then checkout - git will create tracking branch if needed
assert_zero_exit_code(
self.environment.execute(f"git fetch origin && git checkout {branch_init}"),
logger=self.logger,
)
self.logger.info(f"Checked out initial branch {branch_init}")

if self._branch_name != branch_init:
self.logger.info(f"Switching to branch {self._branch_name} for pushing changes")
# First fetch to see if the branch exists on remote
assert_zero_exit_code(
self.environment.execute("git fetch origin"),
logger=self.logger,
)
# Try to checkout the branch - git will track remote if it exists there
checkout_result = self.environment.execute(f"git checkout {self._branch_name}")
if checkout_result.get("returncode", 0) != 0:
# Branch doesn't exist locally or remotely, create it
self.logger.info(f"Branch {self._branch_name} doesn't exist, creating it")
assert_zero_exit_code(
self.environment.execute(f"git checkout -b {self._branch_name}"),
logger=self.logger,
)

# --- Main methods ---

def pre_run_hook(self, *, new_round: int) -> None:
Expand Down Expand Up @@ -104,7 +128,7 @@ def post_run_hook(self, *, round: int) -> None:

if self.push:
for cmd in [
f"git push origin {self._branch_name}",
f"git push -u origin {self._branch_name}",
"git push origin --tags",
]:
assert_zero_exit_code(self.environment.execute(cmd), logger=self.logger)
Expand Down Expand Up @@ -155,11 +179,6 @@ def _tag_round(self, round: int) -> None:
)
self._metadata["round_tags"][round] = tag

@property
def _branch_name(self) -> str:
"""Get the branch name for the agent's codebase."""
return f"{self.game_context.id}.{self.name}"

def _get_round_tag_name(self, round: int) -> str:
"""Get git tag name for the version of the codebase at the given round."""
return f"{self._player_unique_id}-round-{round}"
Expand Down
12 changes: 7 additions & 5 deletions codeclash/arenas/robotrumble/robotrumble.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

DEFAULT_SIMS = 100
MAP_EXT_TO_HEADER = {
"js": "function robot(state, unit) {",
"py": "def robot(state, unit):",
"js": ["function robot(state, unit) {"],
"py": ["def robot(state, unit):", "def robot(state: State, unit: Obj)"],
}
ROBOTRUMBLE_HIDDEN_EXEC = ".codeclash_exec"

Expand Down Expand Up @@ -156,11 +156,13 @@ def validate_code(self, agent: Player) -> tuple[bool, str | None]:
agent.environment.execute(f'echo "robot.{ext}" > {ROBOTRUMBLE_HIDDEN_EXEC}')

# Check that the robot function is defined
header = MAP_EXT_TO_HEADER[ext]
if header not in agent.environment.execute(f"cat robot.{ext}")["output"]:
if not any(
[header in agent.environment.execute(f"cat robot.{ext}")["output"] for header in MAP_EXT_TO_HEADER[ext]]
):
headers = "\n- ".join(MAP_EXT_TO_HEADER[ext])
return (
False,
f"robot.{ext} does not contain the required robot function. It should be defined as '{header}'.",
f"robot.{ext} does not contain the required robot function. It should be defined as one of: '{headers}'.",
)
test_run_cmd = f"{self.run_cmd_round} robot.{ext} robot.{ext} -t 1"
try:
Expand Down
Loading