82 lines
2.8 KiB
Python
82 lines
2.8 KiB
Python
"""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())
|