Skip to content

Commit 8550cd7

Browse files
committed
Merge branch 'main' into john/sims_per_round
2 parents 9455682 + 2f72ff9 commit 8550cd7

5 files changed

Lines changed: 60 additions & 21 deletions

File tree

codeclash/agents/abstract.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import time
23
import uuid
34
from abc import ABC, abstractmethod
45

@@ -37,6 +38,8 @@ def __init__(
3738
"player_unique_id": self._player_unique_id,
3839
"diff": {0: ""}, # mapping round -> diff
3940
"incremental_diff": {0: ""}, # mapping round -> diff
41+
"created_timestamp": int(time.time()),
42+
"config": self.config,
4043
}
4144

4245
# --- Main methods ---

codeclash/games/abstract.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import json
21
import os
32
import subprocess
3+
import time
44
from abc import ABC, abstractmethod
55
from dataclasses import dataclass
66
from pathlib import Path
@@ -56,19 +56,26 @@ def __init__(self, config: dict, *, tournament_id: str, local_output_dir: Path):
5656
self.url_gh: str = f"git@github.com:{GH_ORG}/{self.name}.git"
5757
self.artifacts: list[Path] = []
5858
"""Artifact objects that we might want to clean up after the game."""
59-
self.game_config: dict = config["game"]
6059
self.config: dict = config
61-
self.game_id: str = tournament_id
60+
self._metadata: dict = {
61+
"name": self.name,
62+
"config": self.config["game"],
63+
"game_id": tournament_id,
64+
"created_timestamp": int(time.time()),
65+
}
6266
self.log_env: Path = (DIR_WORK / DIR_LOGS / self.game_id).resolve()
6367
self.log_local: Path = local_output_dir
6468
self.logger = get_logger(self.name, log_path=self.log_local / "game.log", emoji="🏓")
6569
self.environment: DockerEnvironment = self.get_environment()
6670
"""The running docker environment for executing the game"""
67-
self._metadata: dict = {
68-
"name": self.name,
69-
"config": self.config,
70-
"game_id": self.game_id,
71-
}
71+
72+
@property
73+
def game_config(self) -> dict:
74+
return self.config["game"]
75+
76+
@property
77+
def game_id(self) -> str:
78+
return self._metadata["game_id"]
7279

7380
@property
7481
def image_name(self) -> str:
@@ -112,7 +119,6 @@ def get_metadata(self) -> dict:
112119
return self._metadata
113120

114121
def end(self, cleanup: bool = False):
115-
(self.log_local / "metadata.json").write_text(json.dumps(self.get_metadata()))
116122
if cleanup:
117123
for artifact in self.artifacts:
118124
if artifact.exists():
@@ -127,6 +133,8 @@ def get_environment(self, branch_name: str | None = None) -> DockerEnvironment:
127133
cwd=str(DIR_WORK),
128134
env={"GITHUB_TOKEN": os.getenv("GITHUB_TOKEN", "")},
129135
)
136+
# Logger setting will likely not take effect for initial container creation logs
137+
environment.logger = get_logger("environment", emoji="🪴")
130138
# Ensure all future branches occur against branch
131139
branch_name = self.game_id if branch_name is None else branch_name
132140
for cmd in [

codeclash/tournaments/abstract.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ def __init__(self, config: dict, *, name: str, **kwargs):
1717
self._metadata: dict = {
1818
"name": self.name,
1919
"tournament_id": self.tournament_id,
20+
"config": self.config,
21+
"created_timestamp": int(time.time()),
2022
}
2123
self.logger = get_logger(self.name, log_path=self.local_output_dir / "tournament.log", emoji="🏆")
2224

codeclash/tournaments/pvp.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
PvP training mode where multiple agents compete against each other.
33
"""
44

5+
import json
6+
57
from codeclash.agents import get_agent
68
from codeclash.agents.abstract import Player
79
from codeclash.agents.utils import GameContext
810
from codeclash.constants import DIR_WORK
911
from codeclash.games import get_game
10-
from codeclash.games.abstract import CodeGame, RoundStats
12+
from codeclash.games.abstract import CodeGame
1113
from codeclash.tournaments.abstract import AbstractTournament
1214
from codeclash.utils.environment import copy_to_container
1315
from codeclash.utils.log import get_logger
@@ -27,12 +29,24 @@ def __init__(self, config: dict, *, cleanup: bool = False, push_agent: bool = Fa
2729
for agent_conf in self.config["players"]:
2830
self.agents.append(self.get_agent(agent_conf, self.config["prompts"]))
2931
self.logger = get_logger(self.game.name)
30-
self.scoreboard: list[RoundStats] = []
32+
33+
@property
34+
def scoreboard(self) -> list[tuple[int, str]]:
35+
return self._metadata.setdefault("scoreboard", [])
3136

3237
@property
3338
def rounds(self) -> int:
3439
return self.config["tournament"]["rounds"]
3540

41+
def get_metadata(self) -> dict:
42+
# will be saved in end()
43+
return {
44+
**super().get_metadata(),
45+
"scoreboard": self.scoreboard,
46+
"game": self.game.get_metadata(),
47+
"agents": [agent.get_metadata() for agent in self.agents],
48+
}
49+
3650
def get_agent(self, agent_config: dict, prompts: dict) -> Player:
3751
"""Create an agent with environment and game context."""
3852
environment = self.game.get_environment(f"{self.game.game_id}.{agent_config['name']}")
@@ -57,7 +71,7 @@ def run(self) -> None:
5771
for round_num in range(1, self.rounds + 1):
5872
self.run_training_round(round_num)
5973
finally:
60-
self.cleanup()
74+
self.end()
6175

6276
def run_training_round(self, round_num: int) -> None:
6377
"""Execute a single training round."""
@@ -96,8 +110,9 @@ def run_agent(self, agent: Player, round_num: int) -> None:
96110
agent.run()
97111
agent.post_run_hook(round=round_num)
98112

99-
def cleanup(self) -> None:
100-
"""Clean up game resources and push agents if requested."""
113+
def end(self) -> None:
114+
"""Save output files, clean up game resources and push agents if requested."""
115+
(self.local_output_dir / "metadata.json").write_text(json.dumps(self.game.get_metadata()))
101116
self.game.end(self.cleanup_on_end)
102117
if self.push_agent:
103118
for agent in self.agents:

codeclash/tournaments/single_player.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
from codeclash.agents.utils import GameContext
1111
from codeclash.constants import DIR_WORK
1212
from codeclash.games import get_game
13-
from codeclash.games.abstract import CodeGame, RoundStats
13+
from codeclash.games.abstract import CodeGame
1414
from codeclash.tournaments.abstract import AbstractTournament
1515
from codeclash.tournaments.utils.git_utils import filter_git_diff
1616
from codeclash.utils.environment import copy_to_container
1717

1818

1919
class SinglePlayerTraining(AbstractTournament):
20-
def __init__(self, config: dict, cleanup: bool = False):
20+
def __init__(self, config: dict, *, cleanup: bool = False):
2121
super().__init__(config, name="SinglePlayerTraining")
2222
self.cleanup_on_end = cleanup
2323
self.game: CodeGame = get_game(
@@ -29,12 +29,23 @@ def __init__(self, config: dict, cleanup: bool = False):
2929
mirror_agent_config = copy.deepcopy(self.config["player"])
3030
mirror_agent_config["name"] = "mirror"
3131
self.mirror_agent: Player = self.get_agent(mirror_agent_config, round=0)
32-
self.scoreboard: list[RoundStats] = []
32+
33+
@property
34+
def scoreboard(self) -> list[tuple[int, str]]:
35+
return self._metadata.setdefault("scoreboard", [])
3336

3437
@property
3538
def rounds(self) -> int:
3639
return self.config["tournament"]["rounds"]
3740

41+
def get_metadata(self) -> dict:
42+
return {
43+
**super().get_metadata(),
44+
"scoreboard": self.scoreboard,
45+
"game": self.game.get_metadata(),
46+
"agents": [self.agent.get_metadata(), self.mirror_agent.get_metadata()],
47+
}
48+
3849
def get_game_context(self, agent_config: dict, *, round: int) -> GameContext:
3950
"""Create a game context for an agent."""
4051
return GameContext(
@@ -71,7 +82,7 @@ def run(self):
7182
if self.config["tournament"]["evaluate_matrix"]:
7283
self.evaluate()
7384
finally:
74-
self.cleanup()
85+
self.end()
7586

7687
def run_training_round(self, round_num: int) -> None:
7788
"""Execute a single training round, i.e., run the game, then run the agent."""
@@ -113,11 +124,11 @@ def set_mirror_state_to_round(self, round_num: int):
113124
full_diff = filter_git_diff(full_diff)
114125
self.mirror_agent.reset_and_apply_patch(full_diff)
115126

116-
def cleanup(self):
127+
def end(self):
117128
"""Clean up game resources."""
118129
self.game.end(self.cleanup_on_end)
119130

120-
def evaluate(self, n_repetitions: int = 3):
131+
def evaluate(self, n_repetitions: int = 3) -> None:
121132
"""Evaluate the agent's performance by
122133
calculating the matrix of every round against each other.
123134
"""
@@ -144,4 +155,4 @@ def evaluate(self, n_repetitions: int = 3):
144155
self.logger.info(f"Round {p1_round} vs {p2_round} repetition {i_repetition} winner: {winner}")
145156
matrix[p1_round][p2_round].append(winner)
146157
self.logger.info(f"Evaluation matrix: {matrix}")
147-
return matrix
158+
self._metadata.setdefault("evaluation", {})["matrix"] = matrix

0 commit comments

Comments
 (0)