Skip to content

Commit 756e866

Browse files
committed
Change: Keep metadata.json more lean; put file changes elsewhere
See #54
1 parent 6c6bd0f commit 756e866

2 files changed

Lines changed: 78 additions & 19 deletions

File tree

codeclash/agents/player.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import os
23
import time
34
import uuid
@@ -39,8 +40,6 @@ def __init__(
3940
"name": self.name,
4041
"player_unique_id": self._player_unique_id,
4142
"diff": {0: ""}, # mapping round -> diff
42-
"incremental_diff": {0: ""}, # mapping round -> diff
43-
"modified_files": {0: {}}, # mapping round -> {file_path: file_content}
4443
"created_timestamp": int(time.time()),
4544
"config": self.config,
4645
"initial_commit_hash": self._get_commit_hash(),
@@ -65,14 +64,37 @@ def pre_run_hook(self, *, new_round: int) -> None:
6564
self._tag_round(0)
6665
self.game_context.round = new_round
6766

67+
def _write_changes_to_file(self, *, round: int, incremental_diff: str, modified_files: dict[str, str]) -> None:
68+
"""Write incremental changes to a JSON file in players/{name}/changes_r{round}.json"""
69+
if round == 0:
70+
return # No changes for round 0
71+
72+
player_dir = self.game_context.log_local / "players" / self.name
73+
player_dir.mkdir(parents=True, exist_ok=True)
74+
75+
changes_file = player_dir / f"changes_r{round}.json"
76+
changes_data = {
77+
"round": round,
78+
"incremental_diff": incremental_diff,
79+
"modified_files": modified_files,
80+
"timestamp": int(time.time()),
81+
}
82+
83+
changes_file.write_text(json.dumps(changes_data, indent=2))
84+
self.logger.debug(f"Wrote changes for round {round} to {changes_file}")
85+
6886
def post_run_hook(self, *, round: int) -> None:
6987
"""Should be called after we called the run method."""
7088
self._commit()
7189
raw_diff = self._get_round_diff(round)
7290
filtered_diff = filter_git_diff(raw_diff)
7391
self._metadata["diff"][round] = raw_diff
74-
self._metadata["incremental_diff"][round] = self._get_round_diff(round, incremental=True)
75-
self._metadata["modified_files"][round] = self._extract_modified_files_from_diff(filtered_diff)
92+
93+
# Write incremental changes to separate JSON file
94+
incremental_diff = self._get_round_diff(round, incremental=True)
95+
modified_files = self._extract_modified_files_from_diff(filtered_diff)
96+
self._write_changes_to_file(round=round, incremental_diff=incremental_diff, modified_files=modified_files)
97+
7698
if self.push:
7799
for cmd in [
78100
f"git push origin {self._branch_name}",

codeclash/viewer/app.py

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,30 @@ class GameMetadata:
194194
agent_info: list[AgentInfo] | None = None
195195

196196

197-
def process_round_results(round_results: dict[str, Any] | None) -> dict[str, Any] | None:
197+
def process_round_results(
198+
round_results: dict[str, Any] | None, agent_info: list[AgentInfo] | None = None
199+
) -> dict[str, Any] | None:
198200
"""Process round results to add computed fields and sort scores"""
199-
if not round_results or not round_results.get("scores"):
201+
if not round_results:
200202
return round_results
201203

202204
# Create a copy to avoid modifying original data
203205
processed = round_results.copy()
204206

207+
# Get scores, initialize empty dict if missing
208+
scores = round_results.get("scores", {}).copy()
209+
210+
# Ensure all expected players are in scores, even with 0 wins
211+
if agent_info:
212+
expected_players = {agent.name for agent in agent_info}
213+
missing_players = expected_players - set(scores.keys())
214+
if missing_players:
215+
print(f"WARNING: Players {sorted(missing_players)} not found in round results, adding with 0 wins")
216+
for player in missing_players:
217+
scores[player] = 0
218+
205219
# Sort scores alphabetically by key
206-
scores = dict(sorted(round_results["scores"].items()))
220+
scores = dict(sorted(scores.items()))
207221
processed["scores"] = scores
208222
processed["sorted_scores"] = list(scores.items())
209223

@@ -221,7 +235,9 @@ def process_round_results(round_results: dict[str, Any] | None) -> dict[str, Any
221235
processed["winner_percentage"] = None # No percentage for ties
222236

223237
# Calculate p-value for statistical significance
238+
print(f"Calculating p-value for scores: {dict(sorted(scores.items()))}")
224239
p_value = calculate_p_value(scores)
240+
print(f"P-value result: {p_value} (rounded: {round(p_value, 2)})")
225241
processed["p_value"] = round(p_value, 2)
226242
else:
227243
processed["winner_percentage"] = None
@@ -306,7 +322,9 @@ def parse_game_metadata(self) -> GameMetadata:
306322
round_results = None
307323
if results_file.exists():
308324
round_results = json.loads(results_file.read_text())
309-
round_results = process_round_results(round_results)
325+
# Get agent info for this round (we'll need to get it from metadata)
326+
agent_info = get_agent_info_from_metadata(results) if results else []
327+
round_results = process_round_results(round_results, agent_info)
310328

311329
rounds.append({"round_num": round_num, "sim_logs": sim_logs, "results": round_results})
312330

@@ -339,7 +357,7 @@ def parse_trajectory(self, player_name: str, round_num: int) -> TrajectoryInfo |
339357
info = data.get("info", {})
340358
model_stats = info.get("model_stats", {})
341359

342-
# Get diff data from player metadata if available
360+
# Get diff data from player metadata and changes file
343361
diff = None
344362
incremental_diff = None
345363
modified_files = None
@@ -349,16 +367,35 @@ def parse_trajectory(self, player_name: str, round_num: int) -> TrajectoryInfo |
349367
if player_name in self._player_metadata:
350368
player_meta = self._player_metadata[player_name]
351369
diff = player_meta.get("diff", {}).get(str(round_num), "")
352-
incremental_diff = player_meta.get("incremental_diff", {}).get(str(round_num), "")
353-
modified_files = player_meta.get("modified_files", {}).get(str(round_num), {})
354-
355-
# Filter and split diffs by files
356-
filtered_diff = filter_git_diff(diff) if diff else ""
357-
filtered_incremental_diff = filter_git_diff(incremental_diff) if incremental_diff else ""
358-
diff_by_files = split_git_diff_by_files(filtered_diff) if filtered_diff else {}
359-
incremental_diff_by_files = (
360-
split_git_diff_by_files(filtered_incremental_diff) if filtered_incremental_diff else {}
361-
)
370+
371+
# Try to read incremental changes from separate JSON file
372+
changes_file = player_dir / f"changes_r{round_num}.json"
373+
if changes_file.exists():
374+
try:
375+
changes_data = json.loads(changes_file.read_text())
376+
incremental_diff = changes_data.get("incremental_diff", "")
377+
modified_files = changes_data.get("modified_files", {})
378+
except (json.JSONDecodeError, KeyError):
379+
# Fall back to metadata if changes file is corrupted
380+
if player_name in self._player_metadata:
381+
player_meta = self._player_metadata[player_name]
382+
incremental_diff = player_meta.get("incremental_diff", {}).get(str(round_num), "")
383+
modified_files = player_meta.get("modified_files", {}).get(str(round_num), {})
384+
else:
385+
# todo: Legacy: Remove this at some point after we have migrated
386+
# Fall back to metadata if changes file doesn't exist
387+
if player_name in self._player_metadata:
388+
player_meta = self._player_metadata[player_name]
389+
incremental_diff = player_meta.get("incremental_diff", {}).get(str(round_num), "")
390+
modified_files = player_meta.get("modified_files", {}).get(str(round_num), {})
391+
392+
# Filter and split diffs by files
393+
filtered_diff = filter_git_diff(diff) if diff else ""
394+
filtered_incremental_diff = filter_git_diff(incremental_diff) if incremental_diff else ""
395+
diff_by_files = split_git_diff_by_files(filtered_diff) if filtered_diff else {}
396+
incremental_diff_by_files = (
397+
split_git_diff_by_files(filtered_incremental_diff) if filtered_incremental_diff else {}
398+
)
362399

363400
return TrajectoryInfo(
364401
player_id=player_name, # Now stores player name instead of numeric ID

0 commit comments

Comments
 (0)