"""CLI: python3 -m game.sim — startable Pitchwell v0 skirmish stub. Demo path (GAMEPLAY.md Match loop Open→Seed→Break-lite): open both factions → Staff seeds Font/Cot/Yard → train Acolytes → attack Host HQ. """ from __future__ import annotations import argparse import json import sys from .catalog import FACTION_HOST, FACTION_STAFF, HZ from .match import MatchPhase, open_match, seed_v0_path, train_t1_melee def run_demo(ticks: int | None, hz: int, until_end: bool) -> int: m = open_match(hz=hz) print("OPEN", json.dumps(m.snapshot(), sort_keys=True)) notes = seed_v0_path(m, FACTION_STAFF) print("SEED", " ".join(notes)) m.tick_hz(int(50 * hz)) print("AFTER_SEED", json.dumps(m.snapshot(), sort_keys=True)) one = train_t1_melee(m, FACTION_STAFF, n=1) print("TRAIN_ONE", " ".join(one)) m.tick_hz(int(10 * hz)) m.grant_pitch(FACTION_STAFF, 500.0) train = train_t1_melee(m, FACTION_STAFF, n=4) print("TRAIN_ARMY", " ".join(train)) m.tick_hz(int(40 * hz)) ordered = m.order_attack_hq(FACTION_STAFF) print(f"ATTACK_HQ ordered={ordered}") if until_end: # Default --ticks must not cap --until-end (was HZ*5 → false early PASS). safety = ticks if ticks is not None else hz * 600 for _ in range(safety): if m.phase == MatchPhase.ENDED: break m.tick_hz(1) else: m.tick_hz(max(1, ticks if ticks is not None else hz * 5)) snap = m.snapshot() print("END", json.dumps(snap, sort_keys=True)) if m.phase == MatchPhase.ENDED and m.winner in (FACTION_STAFF, FACTION_HOST): print(f"PASS: {m.winner} wins via HQ kill") return 0 if m.phase == MatchPhase.ENDED and m.winner is None: print("PASS: draw (both HQs down)") return 0 staff = snap["sides"][FACTION_STAFF] if "pitch_font" in staff["buildings"] and "coven_yard" in staff["buildings"]: if any(u == "acolyte" for u in staff["units"]): print("PASS: startable foundation (open+seed+T1) without forced end") return 0 print("FAIL: foundation incomplete", file=sys.stderr) return 1 def main(argv: list[str] | None = None) -> int: p = argparse.ArgumentParser(description="Pitchwell headless match (game.sim)") p.add_argument("--hz", type=int, default=HZ) p.add_argument( "--ticks", type=int, default=None, help="extra ticks after attack (default HZ*5); with --until-end overrides safety cap", ) p.add_argument( "--until-end", action="store_true", help="tick until HQ victory or safety cap (default HZ*600)", ) args = p.parse_args(argv) return run_demo(args.ticks, args.hz, args.until_end) if __name__ == "__main__": raise SystemExit(main())