Run a PvP tournament from a config file. [dim]• codeclash run configs/test/battlesnake_pvp_test.yaml[/dim] [dim]• codeclash run path/to/config.yaml -c -o out/ # cleanup + custom output dir[/dim]
(
config_path: Path = typer.Argument(..., help="Path to the tournament config file."),
cleanup: bool = typer.Option(False, "--cleanup", "-c", help="Clean up the game environment after running."),
output_dir: Path | None = typer.Option(None, "--output-dir", "-o", help="Output directory (default: logs/<user>)."),
suffix: str = typer.Option("", "--suffix", "-s", help="Suffix for the output folder name (no leading dot)."),
keep_containers: bool = typer.Option(
False, "--keep-containers", "-k", help="Do not remove containers after games/agent finish."
),
)
| 40 | |
| 41 | @app.command() |
| 42 | def run( |
| 43 | config_path: Path = typer.Argument(..., help="Path to the tournament config file."), |
| 44 | cleanup: bool = typer.Option(False, "--cleanup", "-c", help="Clean up the game environment after running."), |
| 45 | output_dir: Path | None = typer.Option(None, "--output-dir", "-o", help="Output directory (default: logs/<user>)."), |
| 46 | suffix: str = typer.Option("", "--suffix", "-s", help="Suffix for the output folder name (no leading dot)."), |
| 47 | keep_containers: bool = typer.Option( |
| 48 | False, "--keep-containers", "-k", help="Do not remove containers after games/agent finish." |
| 49 | ), |
| 50 | ): |
| 51 | """Run a PvP tournament from a config file. |
| 52 | |
| 53 | [dim]• codeclash run configs/test/battlesnake_pvp_test.yaml[/dim] |
| 54 | [dim]• codeclash run path/to/config.yaml -c -o out/ # cleanup + custom output dir[/dim] |
| 55 | """ |
| 56 | yaml_content = config_path.read_text() |
| 57 | preprocessed_yaml = resolve_includes(yaml_content, base_dir=CONFIG_DIR) |
| 58 | config = yaml.safe_load(preprocessed_yaml) |
| 59 | |
| 60 | def get_output_path() -> Path: |
| 61 | if is_running_in_aws_batch(): |
| 62 | # Offset timestamp by random seconds to avoid collisions |
| 63 | offset = random.randint(0, 600) |
| 64 | timestamp = time.strftime("%y%m%d%H%M%S", time.localtime(time.time() + offset)) |
| 65 | else: |
| 66 | timestamp = time.strftime("%y%m%d%H%M%S") |
| 67 | rounds = config["tournament"]["rounds"] |
| 68 | transparent = config["tournament"].get("transparent", False) |
| 69 | sims = config["game"]["sims_per_round"] |
| 70 | |
| 71 | players = [p["name"] for p in config["players"]] |
| 72 | p_num = len(players) |
| 73 | p_list = ".".join(sorted(players)) |
| 74 | suffix_part = f".{suffix}" if suffix else "" |
| 75 | folder_name = ( |
| 76 | f"PvpTournament.{config['game']['name']}.r{rounds}.s{sims}.p{p_num}.{p_list}{suffix_part}.{timestamp}" |
| 77 | ) |
| 78 | if transparent: |
| 79 | folder_name += ".transparent" |
| 80 | if is_running_in_aws_batch(): |
| 81 | _uuid = str(uuid.uuid4()) |
| 82 | folder_name += f".{_uuid}-uuid" |
| 83 | if output_dir is None: |
| 84 | if is_running_in_aws_batch(): |
| 85 | return LOCAL_LOG_DIR / "batch" / folder_name |
| 86 | else: |
| 87 | return LOCAL_LOG_DIR / getpass.getuser() / folder_name |
| 88 | else: |
| 89 | return output_dir / folder_name |
| 90 | |
| 91 | full_output_dir = get_output_path() |
| 92 | tournament = PvpTournament(config, output_dir=full_output_dir, cleanup=cleanup, keep_containers=keep_containers) |
| 93 | tournament.run() |
| 94 | |
| 95 | |
| 96 | def main() -> None: |
nothing calls this directly
no test coverage detected