Execute competition phase - run Fastchess matches between agents.
(self, agents: list[Player])
| 187 | self.logger.warning(f"Match {idx} ({agent1.name} vs {agent2.name}) timed out after 5 minutes") |
| 188 | |
| 189 | def execute_round(self, agents: list[Player]): |
| 190 | """ |
| 191 | Execute competition phase - run Fastchess matches between agents. |
| 192 | """ |
| 193 | assert len(agents) >= 2, "Chess requires at least two players" |
| 194 | |
| 195 | # Recompile engines in game container |
| 196 | self.logger.info("Recompiling engines in game container...") |
| 197 | engine_paths = self._compile_engines_in_game_container(agents) |
| 198 | |
| 199 | if len(engine_paths) < 2: |
| 200 | self.logger.warning( |
| 201 | f"Only {len(engine_paths)} agent(s) compiled successfully, need at least 2. Skipping round." |
| 202 | ) |
| 203 | return |
| 204 | |
| 205 | # Build match pairings using only successfully compiled agents |
| 206 | compiled_agents = [agent for agent in agents if agent.name in engine_paths] |
| 207 | self.logger.info(f"Building match pairings for {self.game_config['sims_per_round']} simulations...") |
| 208 | pairings = self._build_match_pairings(compiled_agents) |
| 209 | |
| 210 | # Store pairings to file for retrieval in get_results() |
| 211 | pairings_file = self.log_env / "pairings.json" |
| 212 | pairings_data = [ |
| 213 | {"match_idx": idx, "agent1": agent1.name, "agent2": agent2.name} |
| 214 | for idx, (agent1, agent2) in enumerate(pairings) |
| 215 | ] |
| 216 | # Write to container's log directory |
| 217 | pairings_json = json.dumps(pairings_data, indent=2) |
| 218 | create_file_in_container( |
| 219 | container=self.environment, |
| 220 | content=pairings_json, |
| 221 | dest_path=str(pairings_file), |
| 222 | ) |
| 223 | self.logger.debug(f"Stored pairings to {pairings_file}") |
| 224 | |
| 225 | # Run matches in parallel |
| 226 | self.logger.info(f"Running {len(pairings)} matches in parallel...") |
| 227 | with ThreadPoolExecutor( |
| 228 | max_workers=min(self.game_config.get("sim_concurrency", 20), len(pairings)) |
| 229 | ) as executor: |
| 230 | futures = [ |
| 231 | executor.submit( |
| 232 | self._run_single_match, |
| 233 | agent1, |
| 234 | agent2, |
| 235 | engine_paths[agent1.name], |
| 236 | engine_paths[agent2.name], |
| 237 | idx, |
| 238 | ) |
| 239 | for idx, (agent1, agent2) in enumerate(pairings) |
| 240 | ] |
| 241 | |
| 242 | # Collect results with progress bar |
| 243 | for future in tqdm(as_completed(futures), total=len(futures), desc="Chess matches"): |
| 244 | try: |
| 245 | future.result() |
| 246 | except Exception as e: |
nothing calls this directly
no test coverage detected