Skip to content

Commit 29a40cf

Browse files
committed
Add latex table print out to elo.py
1 parent addcf71 commit 29a40cf

2 files changed

Lines changed: 71 additions & 17 deletions

File tree

codeclash/analysis/metrics/elo.py

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
from tqdm import tqdm
88

9+
from codeclash.analysis.viz.utils import MODEL_TO_DISPLAY_NAME
910
from codeclash.constants import LOCAL_LOG_DIR, RESULT_TIE
11+
from codeclash.games import ARENAS
1012

1113

1214
@dataclass
@@ -221,18 +223,71 @@ def print_results(self) -> None:
221223

222224
print("\nELO per player (across all games):")
223225
calc_avg_elo = lambda total_elo, games: total_elo / games
226+
model_to_avg_elo = {mid: calc_avg_elo(weighted_elo[mid], total_games[mid]) for mid in weighted_elo}
224227
lines = sorted(
225-
[
226-
f"{pid}: {calc_avg_elo(weighted_elo[pid], total_games[pid]):.1f} (Rounds: {total_games[pid]})"
227-
for pid in weighted_elo
228-
if total_games[pid] > 0
229-
],
228+
[f"{pid}: {model_to_avg_elo[pid]:.1f} (Rounds: {total_games[pid]})" for pid in model_to_avg_elo.keys()],
230229
key=lambda x: float(x.split(":")[1].split("(")[0]),
231230
reverse=True,
232231
)
233232
for i, line in enumerate(lines, 1):
234233
print(f"{i}. {line}")
235234

235+
# Print latex formatted table for results, formatted as
236+
# Model & ELO & & & \\
237+
# & BattleCode & BattleSnake & ... \\
238+
# \midrule
239+
# Claude 4 Sonnet & ... & & & \\
240+
# ...
241+
print("\nLaTeX formatted table:")
242+
arenas = [x.name for x in ARENAS]
243+
lines = []
244+
arenas_small = [f"\\scriptsize{{{arena}}}" for arena in arenas]
245+
line = "& " + " & ".join(arenas_small) + " & All" + r" \\"
246+
line = line.replace("HuskyBench", "Poker")
247+
lines.append(line)
248+
lines.append(r"\midrule")
249+
per_model_lines = {}
250+
arena_rankings = {}
251+
252+
# Create a mapping of model -> arena -> elo and also arena -> list of (model, elo) for ranking
253+
for profile in self._player_profiles.values():
254+
if profile.model not in per_model_lines:
255+
per_model_lines[profile.model] = {}
256+
if profile.arena not in arena_rankings:
257+
arena_rankings[profile.arena] = []
258+
per_model_lines[profile.model][profile.arena] = round(profile.rating, 1)
259+
arena_rankings[profile.arena].append((profile.model, profile.rating))
260+
261+
# Sort each arena ranking by ELO descending
262+
for arena in arena_rankings:
263+
arena_rankings[arena].sort(key=lambda x: x[1], reverse=True)
264+
for rank, (model, _) in enumerate(arena_rankings[arena], 1):
265+
per_model_lines[model][arena] = (
266+
f"{per_model_lines[model][arena]}" + f"\\textsuperscript{{\\textcolor{{gray}}{{{rank}}}}}"
267+
)
268+
if rank == 1:
269+
# Make it bold
270+
per_model_lines[model][arena] = f"\\textbf{{{per_model_lines[model][arena]}}}"
271+
272+
# Now print the table
273+
overall_ranks = sorted(model_to_avg_elo.items(), key=lambda x: x[1], reverse=True)
274+
model_rank = {model: rank + 1 for rank, (model, _) in enumerate(overall_ranks)}
275+
for model in sorted(per_model_lines.keys()):
276+
m = model.split("/", 1)[-1]
277+
rank = f"\\textsuperscript{{\\textcolor{{gray}}{{{model_rank[model]}}}}}"
278+
overall_elo = f"{model_to_avg_elo[model]:.1f}{rank}"
279+
if model_rank[model] == 1:
280+
overall_elo = f"\\textbf{{{overall_elo}}}"
281+
line = (
282+
f"\\small{{{MODEL_TO_DISPLAY_NAME[m]}}} & "
283+
+ " & ".join(str(per_model_lines[model].get(arena, "-")) for arena in arenas)
284+
+ " & "
285+
+ overall_elo
286+
+ " \\\\"
287+
)
288+
lines.append(line)
289+
print("\n".join(lines))
290+
236291

237292
if __name__ == "__main__":
238293
parser = argparse.ArgumentParser(description="Calculate weighted ELO ratings with configurable weighting functions")

codeclash/games/__init__.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,20 @@
77
from codeclash.games.robocode.robocode import RoboCodeGame
88
from codeclash.games.robotrumble.robotrumble import RobotRumbleGame
99

10+
ARENAS = [
11+
BattleCodeGame,
12+
BattleSnakeGame,
13+
CoreWarGame,
14+
DummyGame,
15+
HuskyBenchGame,
16+
RoboCodeGame,
17+
RobotRumbleGame,
18+
]
19+
1020

1121
# might consider postponing imports to avoid loading things we don't need
1222
def get_game(config: dict, **kwargs) -> CodeGame:
13-
game = {
14-
x.name: x
15-
for x in [
16-
BattleCodeGame,
17-
BattleSnakeGame,
18-
CoreWarGame,
19-
DummyGame,
20-
HuskyBenchGame,
21-
RoboCodeGame,
22-
RobotRumbleGame,
23-
]
24-
}.get(config["game"]["name"])
23+
game = {x.name: x for x in ARENAS}.get(config["game"]["name"])
2524
if game is None:
2625
raise ValueError(f"Unknown game: {config['game']['name']}")
2726
return game(config, **kwargs)

0 commit comments

Comments
 (0)