| 21 | |
| 22 | |
| 23 | class SinglePlayerTraining(AbstractTournament): |
| 24 | def __init__(self, config: dict, *, output_dir: Path, cleanup: bool = False, keep_containers: bool = False): |
| 25 | super().__init__(config, name="SinglePlayerTraining", output_dir=output_dir) |
| 26 | self.cleanup_on_end = cleanup |
| 27 | self.game: CodeArena = get_arena( |
| 28 | self.config, |
| 29 | tournament_id=self.tournament_id, |
| 30 | local_output_dir=self.local_output_dir, |
| 31 | keep_containers=keep_containers, |
| 32 | ) |
| 33 | self.agent: Player = self.get_agent(self.config["player"], round=1) |
| 34 | mirror_agent_config = copy.deepcopy(self.config["player"]) |
| 35 | mirror_agent_config["name"] = "mirror" |
| 36 | self.mirror_agent: Player = self.get_agent(mirror_agent_config, round=0) |
| 37 | |
| 38 | @property |
| 39 | def rounds(self) -> int: |
| 40 | return self.config["tournament"]["rounds"] |
| 41 | |
| 42 | def get_metadata(self) -> dict: |
| 43 | return { |
| 44 | **super().get_metadata(), |
| 45 | "game": self.game.get_metadata(), |
| 46 | "agents": [self.agent.get_metadata(), self.mirror_agent.get_metadata()], |
| 47 | } |
| 48 | |
| 49 | def get_game_context(self, agent_config: dict, *, round: int) -> GameContext: |
| 50 | """Create a game context for an agent.""" |
| 51 | return GameContext( |
| 52 | id=self.game.game_id, |
| 53 | log_env=self.game.log_env, |
| 54 | log_local=self.game.log_local, |
| 55 | name=self.game.name, |
| 56 | player_id=agent_config["name"], |
| 57 | prompts=self.config["prompts"], |
| 58 | round=round, |
| 59 | rounds=self.rounds, |
| 60 | working_dir=str(DIR_WORK), |
| 61 | ) |
| 62 | |
| 63 | def get_agent(self, agent_config: dict, round: int) -> Player: |
| 64 | """Create an agent with environment and game context.""" |
| 65 | # Commit/tag messages are prefixed with `commit_label` (see Player._round_message); |
| 66 | # single-player has no rung/opponent prefix, so default it to empty (as PvP does). |
| 67 | agent_config.setdefault("commit_label", "") |
| 68 | environment = self.game.get_environment(f"{self.game.game_id}.{agent_config['name']}") |
| 69 | game_context = self.get_game_context(agent_config, round=round) |
| 70 | return get_agent(agent_config, game_context, environment) |
| 71 | |
| 72 | def _get_round_diff(self, player_name: str, round_num: int) -> str: |
| 73 | """Read diff data from changes_r{round}.json file, fallback to metadata.""" |
| 74 | if round_num == 0: |
| 75 | return "" |
| 76 | changes_file = self.game.log_local / "players" / player_name / f"changes_r{round_num}.json" |
| 77 | changes_data = json.loads(changes_file.read_text()) |
| 78 | return changes_data.get("full_diff", "") |
| 79 | |
| 80 | def get_dummy_agent(self, player_config: dict) -> Player: |