Skip to content

RoboCode

Java-based robot programming game where robots battle in an arena.

Overview

RoboCode is a programming game where players develop tanks in Java to battle each other in a graphical arena.

Game Rules

  • Robots are programmed in Java
  • Battle in 2D arena
  • Scan for enemies, move, and fire
  • Destroy opponents to win

Configuration Example

game:
  name: RoboCode
  rounds: 10
  sims_per_round: 5
  timeout: 300

players:
  - name: Tank1
    model: gpt-4
  - name: Tank2
    model: claude-3

Resources

Implementation

codeclash.arenas.robocode.robocode.RoboCodeArena

RoboCodeArena(config, **kwargs)

Bases: CodeArena

Source code in codeclash/arenas/robocode/robocode.py
35
36
37
38
39
40
41
42
43
def __init__(self, config, **kwargs):
    super().__init__(config, **kwargs)
    self.run_cmd_round: str = "./robocode.sh"
    for arg, val in self.game_config.get("args", self.default_args).items():
        if isinstance(val, bool):
            if val:
                self.run_cmd_round += f" -{arg}"
        else:
            self.run_cmd_round += f" -{arg} {val}"

name class-attribute instance-attribute

name: str = 'RoboCode'

description class-attribute instance-attribute

description: str = f'Robocode is a programming game where your code IS the tank. This is classic
Robocode (the `robocode.*` API compiled against robocode.jar)  NOT Robocode Tank Royale.
Your bot is a Java class that `extends robocode.Robot` (or `robocode.AdvancedRobot` for non-blocking
control): its `run()` method drives the tank in a loop (e.g. `ahead(100)`, `turnGunRight(90)`,
`fire(3)`), and it reacts to events like `onScannedRobot(ScannedRobotEvent)`, `onHitByBullet(...)`,
and `onHitWall(...)`. Move, aim the gun, sweep the radar, and fire to outlast other bots.
Your bot logic must be written in Java and located in the `robots/custom/` directory.
Keep the main bot class named `{str(RC_FILE)}`, but you can include additional Java files if you'd like.'

default_args class-attribute instance-attribute

default_args: dict = {'nodisplay': True, 'nosound': True}

submission class-attribute instance-attribute

submission: str = 'robots/custom/'

run_cmd_round instance-attribute

run_cmd_round: str = './robocode.sh'

execute_round

execute_round(agents: list[Player])
Source code in codeclash/arenas/robocode/robocode.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    def execute_round(self, agents: list[Player]):
        for agent in agents:
            # Copy the agent codebase into the game codebase and compile it
            for cmd in [
                f"mkdir -p robots/{agent.name}",
                f"cp -r /{agent.name}/robots/custom/* robots/{agent.name}/",
                f"find robots/{agent.name}/ -name '*.java' -exec sed -i 's/custom/{agent.name}/g' {{}} +",
                f'javac -cp "libs/robocode.jar" robots/{agent.name}/*.java',
            ]:
                self.environment.execute(cmd)

        # Create .battle file
        selected_robots = ",".join([f"{agent.name}.{RC_FILE.stem}*" for agent in agents])
        # Use timestamp for unique battle file name since rounds are managed by tournament
        battle_file = f"{self.game_id}-battle{int(time.time())}.battle"
        battle_content = f"""#Battle Properties
{self._get_battle_config()}
robocode.battle.selectedRobots={selected_robots}
"""
        create_file_in_container(self.environment, content=battle_content, dest_path=f"battles/{battle_file}")

        # Run battle with results output to file
        cmd = f"{self.run_cmd_round} -battle {battle_file}"
        self.logger.info(f"Running game: {cmd}")
        with ThreadPoolExecutor(self.game_config.get("sim_concurrency", 5)) as executor:
            # Submit all simulations to the thread pool
            futures = [
                executor.submit(self._run_single_simulation, agents, idx, cmd)
                for idx in range(self.game_config.get("sims_per_round", 100) // SIMS_PER_RUN)
            ]

            # Collect results as they complete
            for future in tqdm(as_completed(futures), total=len(futures)):
                future.result()

get_results

get_results(agents: list[Player], round_num: int, stats: RoundStats)
Source code in codeclash/arenas/robocode/robocode.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
def get_results(self, agents: list[Player], round_num: int, stats: RoundStats):
    scores = defaultdict(int)
    for idx in range(self.game_config.get("sims_per_round", 100) // SIMS_PER_RUN):
        with open(self.log_round(round_num) / f"results_{idx}.txt") as f:
            result_output = f.read()
        lines = result_output.strip().split("\n")

        for line in lines:
            line = line.strip()
            if not re.match(r"^\d", line):
                continue
            match = re.search(r"(\d+)\S+\:\s(\S+)\s+(\d+)", line)
            if match:
                player = match.group(2).rsplit(".", 1)[0]
                scores[player] += int(match.group(3))

    stats.winner = max(scores, key=scores.get)
    stats.scores = scores
    for player, score in scores.items():
        stats.player_stats[player].score = score

validate_code

validate_code(agent: Player) -> tuple[bool, str | None]
Source code in codeclash/arenas/robocode/robocode.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def validate_code(self, agent: Player) -> tuple[bool, str | None]:
    if "robots" not in agent.environment.execute("ls")["output"]:
        return False, "There should be a `robots/` directory"
    if "custom" not in agent.environment.execute("ls robots")["output"]:
        return False, "There should be a `robots/custom/` directory"
    if str(RC_FILE) not in agent.environment.execute("ls robots/custom")["output"]:
        return False, (
            f"There should be a `robots/custom/{RC_FILE}` file. "
            f"You can include additional files, but the primary tank logic must be in `robots/custom/{RC_FILE}`"
        )
    response = agent.environment.execute('javac -cp "libs/robocode.jar" robots/custom/*.java')
    if response["returncode"] != 0:
        return False, f"Compilation error:\n{response['output']}"
    if f"{RC_FILE.stem}.class" not in agent.environment.execute("ls robots/custom")["output"]:
        return False, f"`{RC_FILE.stem}.class` not found after compilation"
    return True, None