Run a single Fastchess match between two engines. Args: agent1: First agent agent2: Second agent engine1_path: Path to first engine executable in game container engine2_path: Path to second engine executable in game container
(self, agent1: Player, agent2: Player, engine1_path: str, engine2_path: str, idx: int)
| 138 | return pairings |
| 139 | |
| 140 | def _run_single_match(self, agent1: Player, agent2: Player, engine1_path: str, engine2_path: str, idx: int): |
| 141 | """ |
| 142 | Run a single Fastchess match between two engines. |
| 143 | |
| 144 | Args: |
| 145 | agent1: First agent |
| 146 | agent2: Second agent |
| 147 | engine1_path: Path to first engine executable in game container |
| 148 | engine2_path: Path to second engine executable in game container |
| 149 | idx: Simulation index for output file naming |
| 150 | """ |
| 151 | |
| 152 | output_file = self.log_env / f"match_{idx}.pgn" |
| 153 | |
| 154 | # Ensure log directory exists |
| 155 | self.environment.execute(f"mkdir -p {self.log_env}") |
| 156 | |
| 157 | cmd = ( |
| 158 | f"{self.run_cmd_base} " |
| 159 | f"-engine cmd={engine1_path} name={agent1.name} " |
| 160 | f"-engine cmd={engine2_path} name={agent2.name} " |
| 161 | f"-rounds 1 " |
| 162 | f"-pgnout file={str(output_file)}" |
| 163 | ) |
| 164 | |
| 165 | self.logger.debug(f"Running match {idx}: {agent1.name} vs {agent2.name}") |
| 166 | self.logger.debug(f"Fastchess command: {cmd}") |
| 167 | self.logger.debug(f"Output file path: {output_file}") |
| 168 | |
| 169 | try: |
| 170 | response = self.environment.execute(cmd, timeout=300) # 5 minute timeout per match |
| 171 | if response["returncode"] != 0: |
| 172 | error_output = response.get("output", "")[:1000] |
| 173 | self.logger.warning( |
| 174 | f"Match {idx} ({agent1.name} vs {agent2.name}) failed with exit code {response['returncode']}:\n{error_output}" |
| 175 | ) |
| 176 | else: |
| 177 | # Verify PGN file was created |
| 178 | check_result = self.environment.execute(f"test -f {str(output_file)} && echo 'exists'") |
| 179 | if "exists" not in check_result["output"]: |
| 180 | self.logger.warning(f"Match {idx} completed but PGN file not found at {output_file}") |
| 181 | # Debug: list files in log directory |
| 182 | ls_result = self.environment.execute(f"ls -la {self.log_env}") |
| 183 | self.logger.debug(f"Files in {self.log_env}: {ls_result.get('output', '')[:500]}") |
| 184 | else: |
| 185 | self.logger.debug(f"Match {idx} PGN file verified at {output_file}") |
| 186 | except subprocess.TimeoutExpired: |
| 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 | """ |