Skip to content

Commit 75363e7

Browse files
committed
Add Trueskill metric
1 parent 3a17996 commit 75363e7

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import json
2+
from collections import defaultdict
3+
from pathlib import Path
4+
5+
import trueskill as ts
6+
from tqdm.auto import tqdm
7+
8+
from codeclash.analysis.viz.utils import MODEL_TO_DISPLAY_NAME
9+
from codeclash.constants import LOCAL_LOG_DIR
10+
11+
# TrueSkill environment setup
12+
env = ts.TrueSkill(
13+
mu=25.0,
14+
sigma=25.0 / 3.0, # ~8.33
15+
beta=25.0 / 6.0, # ~4.17 (performance variance)
16+
tau=0.7, # skill drift per round (↑ for faster adaptation)
17+
draw_probability=0.0,
18+
)
19+
ts.setup(env)
20+
ratings = defaultdict(env.Rating)
21+
22+
# Find all game log folders with 3+ players
23+
game_log_folders = [x.parent for x in Path(LOCAL_LOG_DIR).rglob("metadata.json")]
24+
three_plus_players = []
25+
for game_log_folder in tqdm(game_log_folders):
26+
arena = game_log_folder.name.split(".")[1]
27+
num_players = int(game_log_folder.name.split(".")[4].strip("p"))
28+
if num_players >= 3:
29+
three_plus_players.append(game_log_folder)
30+
31+
print(f"Found {len(three_plus_players)} tournaments with 3+ players.")
32+
33+
34+
def scores_to_ranks(scores_dict):
35+
"""Higher score => better rank (0 is best). Ties share the same rank."""
36+
# Sort by (-score, name) for deterministic ordering
37+
ordered = sorted(scores_dict.items(), key=lambda kv: (-kv[1], kv[0]))
38+
ranks = {}
39+
prev_score, prev_rank = None, None
40+
for idx, (name, score) in enumerate(ordered):
41+
if score == prev_score:
42+
ranks[name] = prev_rank
43+
else:
44+
ranks[name] = idx
45+
prev_rank = idx
46+
prev_score = score
47+
return ordered, ranks # ordered is list[(name, score)] in finishing order
48+
49+
50+
def update_ratings_for_round(scores_dict):
51+
ordered, ranks = scores_to_ranks(scores_dict)
52+
53+
# Convert to the Rating objects, preserving team order
54+
team_ratings = [[ratings[name]] for name, _ in ordered]
55+
56+
# ranks vector matches the order of `teams`
57+
ranks_vec = [ranks[name] for name, _ in ordered]
58+
59+
# Update using TrueSkill (one multi-player game)
60+
new_team_ratings = ts.rate(team_ratings, ranks=ranks_vec)
61+
62+
# Write back
63+
for (name, _), new_rating in zip(ordered, new_team_ratings):
64+
ratings[name] = new_rating[0]
65+
66+
67+
def conservative_score(r):
68+
# Leaderboard-safe score (lower-bound skill)
69+
return r.mu - 3 * r.sigma
70+
71+
72+
for game_log_folder in tqdm(three_plus_players):
73+
with open(game_log_folder / "metadata.json") as f:
74+
metadata = json.load(f)
75+
p2m = {
76+
x["name"]: x["config"]["model"]["model_name"].strip("@").split("/")[-1] for x in metadata["config"]["players"]
77+
}
78+
for round_num in range(len(metadata["round_stats"])):
79+
if round_num == 0:
80+
continue
81+
scores = metadata["round_stats"][str(round_num)]["scores"]
82+
update_ratings_for_round(scores)
83+
84+
leaderboard = sorted(ratings.items(), key=lambda kv: conservative_score(kv[1]), reverse=True)
85+
86+
for name, r in leaderboard:
87+
print(f"{name:30s} mu={r.mu:6.2f} sigma={r.sigma:5.2f} conservative={conservative_score(r):6.2f}")
88+
89+
# Print out a latex style table
90+
# print("\nLatex Table:")
91+
# print("\\begin{tabular}{lccc}")
92+
# print("\\textbf{Model} & $\\mu$ & $\\sigma$ & \\textbf{Conservative} \\\\ \\hline")
93+
# for name, r in leaderboard:
94+
# display_name = MODEL_TO_DISPLAY_NAME.get(name, name)
95+
# print(f"{display_name:30s} & {r.mu:6.2f} & {r.sigma:5.2f} & {conservative_score(r):6.2f} \\\\")
96+
# print("\\end{tabular}")
97+
98+
print("\nLatex Table:")
99+
print("\\begin{tabular}{l|c}")
100+
print("\\textbf{Model} & $\\mu$ \\\\ \\midrule")
101+
for name, r in leaderboard:
102+
display_name = MODEL_TO_DISPLAY_NAME.get(name, name)
103+
print(f"{display_name:30s} & {r.mu:6.2f} $\\pm$ {r.sigma:5.2f} \\\\")
104+
print("\\end{tabular}")

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ dependencies = [
3030
"markupsafe",
3131
"scipy",
3232
"unidiff",
33+
"trueskill",
3334
]
3435

3536
[project.optional-dependencies]

0 commit comments

Comments
 (0)