Skip to content

Commit 3bb097e

Browse files
committed
Loosen robotrumble validation constraints
1 parent 1557e6b commit 3bb097e

2 files changed

Lines changed: 35 additions & 4 deletions

File tree

codeclash/arenas/robotrumble/robotrumble.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import re
23
import shlex
34
import subprocess
45
from collections import Counter
@@ -13,6 +14,14 @@
1314
"js": ["function robot(state, unit) {"],
1415
"py": ["def robot(state, unit):", "def robot(state: State, unit: Obj)"],
1516
}
17+
# Recognizes a valid `robot(state, unit)` entry point regardless of type annotations, return
18+
# hints, or extra whitespace (e.g. `def robot(state, unit) -> Action:` or
19+
# `def robot(state: State, unit: Obj) -> Optional[Action]:`). The MAP_EXT_TO_HEADER strings
20+
# above stay as the human-readable examples shown in the validation error message.
21+
MAP_EXT_TO_SIGNATURE_RE = {
22+
"js": re.compile(r"function\s+robot\s*\(\s*state\b[^)]*,\s*unit\b[^)]*\)"),
23+
"py": re.compile(r"def\s+robot\s*\(\s*state\b[^)]*,\s*unit\b[^)]*\)"),
24+
}
1625
ROBOTRUMBLE_HIDDEN_EXEC = ".codeclash_exec"
1726

1827

@@ -155,10 +164,10 @@ def validate_code(self, agent: Player) -> tuple[bool, str | None]:
155164
return False, "There should be a `robot.js` or `robot.py` file"
156165
agent.environment.execute(f'echo "robot.{ext}" > {ROBOTRUMBLE_HIDDEN_EXEC}')
157166

158-
# Check that the robot function is defined
159-
if not any(
160-
[header in agent.environment.execute(f"cat robot.{ext}")["output"] for header in MAP_EXT_TO_HEADER[ext]]
161-
):
167+
# Check that the robot function is defined. Match on a signature regex (tolerant of type
168+
# annotations, return hints, and whitespace) rather than an exact substring, so a valid
169+
# entry point like `def robot(state, unit) -> Action:` is not spuriously rejected.
170+
if not MAP_EXT_TO_SIGNATURE_RE[ext].search(agent.environment.execute(f"cat robot.{ext}")["output"]):
162171
headers = "\n- ".join(MAP_EXT_TO_HEADER[ext])
163172
return (
164173
False,

tests/arenas/test_robotrumble.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,28 @@ def test_valid_py_submission(self, arena, mock_player_factory):
8686
assert is_valid is True
8787
assert error is None
8888

89+
def test_valid_py_submission_annotated(self, arena, mock_player_factory):
90+
"""A robot() with type annotations / a return hint must still validate.
91+
92+
Regression: the old exact-substring check rejected `def robot(state, unit) -> Action:`
93+
(matched neither whitelisted header), auto-failing otherwise-valid submissions.
94+
"""
95+
annotated = '\ndef robot(state, unit) -> "Action":\n return Action.move(Direction.East)\n'
96+
player = mock_player_factory(
97+
name="test_player",
98+
files={"robot.py": annotated},
99+
command_outputs={
100+
"test -f robot.js && echo 'exists'": {"output": "", "returncode": 1},
101+
"test -f robot.py && echo 'exists'": {"output": "exists", "returncode": 0},
102+
"cat robot.py": {"output": annotated, "returncode": 0},
103+
f'echo "robot.py" > {ROBOTRUMBLE_HIDDEN_EXEC}': {"output": "", "returncode": 0},
104+
"./rumblebot run term --raw robot.py robot.py -t 1": {"output": "Blue won", "returncode": 0},
105+
},
106+
)
107+
is_valid, error = arena.validate_code(player)
108+
assert is_valid is True
109+
assert error is None
110+
89111
def test_missing_robot_file(self, arena, mock_player_factory):
90112
"""Test that missing robot file fails validation."""
91113
player = mock_player_factory(

0 commit comments

Comments
 (0)