|
19 | 19 | logger = get_logger("ladder") |
20 | 20 |
|
21 | 21 |
|
22 | | -def _player_slug(branch_init: str, game_name: str) -> str: |
23 | | - """Turn a ``human/<...>/<bot>`` init branch into a bare, package-safe player name. |
24 | | -
|
25 | | - The name is used as an on-disk directory and — in the RoboCode arena — as a Java package name |
26 | | - and robot-selector token (``robots/<name>/``, ``sed s/custom/<name>/``, |
27 | | - ``selectedRobots=<name>.MyTank``), so it must contain no slashes. RoboCode additionally reserves |
28 | | - the ``robocode`` package namespace for its engine and its robot classloader refuses to load any |
29 | | - robot whose package starts with it, so a leading path segment equal to the game name (RoboCode |
30 | | - branches are ``human/robocode/<bot>``) is dropped rather than folded into a ``robocode_<bot>`` |
31 | | - token that would raise ClassNotFoundException at battle time. Non-RoboCode branches |
32 | | - (``human/<author>/<bot>``, ``human/<bot>``) are unaffected: their leading segment never matches |
33 | | - the game name, so the result is the same ``<author>_<bot>`` slug as before. |
| 22 | +def _player_slug(branch_init: str) -> str: |
| 23 | + """Turn a ``human/<author>/<bot>`` init branch into a bare, filesystem-safe player name: |
| 24 | + strip the ``human/`` prefix and join the rest with ``_`` (e.g. ``human/aleksiy325/snek-two`` |
| 25 | + -> ``aleksiy325_snek-two``). |
| 26 | +
|
| 27 | + RoboCode also uses this name as a Java *package* (``<name>.MyTank``), which must be a bare |
| 28 | + identifier that does not start with the engine's reserved ``robocode`` namespace. The RoboCode |
| 29 | + ladder branches are named so the plain slug already satisfies that (``human/robo_code/walls`` |
| 30 | + -> ``robo_code__walls``), so no game-specific handling is needed here. |
34 | 31 | """ |
35 | | - parts = [p for p in branch_init.split("/") if p] |
36 | | - if parts and parts[0] == "human": |
37 | | - parts = parts[1:] |
38 | | - if parts and parts[0].lower() == game_name.lower(): |
39 | | - parts = parts[1:] |
40 | | - return "_".join(parts) |
| 32 | + return branch_init.replace("human/", "").replace("/", "__") |
41 | 33 |
|
42 | 34 |
|
43 | 35 | def _resolve_ladder_rules(ladder_rules: dict, rounds: int) -> tuple[int, int]: |
@@ -115,17 +107,15 @@ def make( |
115 | 107 | players = config["players"] |
116 | 108 | num_players = len(players) |
117 | 109 |
|
118 | | - game_name = config["game"]["name"] |
119 | | - |
120 | 110 | # Build one fully independent (deep-copied) config per pair up front so concurrent runs |
121 | 111 | # never share or mutate the same player/config dicts. |
122 | 112 | jobs: list[tuple[dict, Path]] = [] |
123 | 113 | for i in range(num_players): |
124 | 114 | for j in range(i + 1, num_players): |
125 | 115 | player1 = copy.deepcopy(players[i]) |
126 | | - player1["name"] = _player_slug(player1["branch_init"], game_name) |
| 116 | + player1["name"] = _player_slug(player1["branch_init"]) |
127 | 117 | player2 = copy.deepcopy(players[j]) |
128 | | - player2["name"] = _player_slug(player2["branch_init"], game_name) |
| 118 | + player2["name"] = _player_slug(player2["branch_init"]) |
129 | 119 | pvp_config = {**copy.deepcopy(config), "players": [player1, player2]} |
130 | 120 | vs = f"PvpTournament.{player1['name']}_vs_{player2['name']}".replace("/", "_") |
131 | 121 | output_dir = LOCAL_LOG_DIR / "ladder" / config["game"]["name"] / vs |
@@ -196,7 +186,7 @@ def run( |
196 | 186 | advanced = False |
197 | 187 | for idx, opponent in enumerate(ladder): |
198 | 188 | opponent_rank = len(ladder) - idx |
199 | | - opponent["name"] = _player_slug(opponent["branch_init"], config["game"]["name"]) |
| 189 | + opponent["name"] = _player_slug(opponent["branch_init"]) |
200 | 190 | if "branch_init" in player and idx > 0: |
201 | 191 | # After first opponent, remove branch_init so that player continues from previous tournament's codebase |
202 | 192 | del player["branch_init"] |
|
0 commit comments