OpenKKnD/scripts/smoke/match_smoke.py

103 lines
3.4 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Match open → seed → seep → T1 → HQ kill — startable foundation (GAMEPLAY.md v0)."""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from game.sim.catalog import FACTION_HOST, FACTION_STAFF, HZ, START_PITCH
from game.sim.match import MatchPhase, open_match, seed_v0_path, train_t1_melee
def main() -> int:
m = open_match()
staff = m.side(FACTION_STAFF)
host = m.side(FACTION_HOST)
if staff.pitch != START_PITCH or host.pitch != START_PITCH:
print(f"FAIL: start pitch {staff.pitch}/{host.pitch}")
return 1
if len(staff.units) != 4 or len(host.units) != 4:
print("FAIL: want 4 workers per side")
return 1
if not staff.hq_alive or not host.hq_alive:
print("FAIL: both HQs must be alive at open")
return 1
notes = seed_v0_path(m, FACTION_STAFF)
if any(not n.endswith(":ok") for n in notes):
print(f"FAIL: seed {notes}")
return 1
# Serial queue: Font 15 + Cot 12 + Yard 20 = 47s; start 500 - 380 = 120 left
m.tick_hz(int(47 * HZ) + 2)
if not staff.has_building("pitch_font"):
print("FAIL: pitch_font missing after seed")
return 1
if 0 not in staff.claimed_wells or not m.wells[0].claimed:
print("FAIL: well not claimed by Staff Font")
return 1
if not staff.has_producer():
print("FAIL: coven_yard missing")
return 1
if staff.pop_cap != 18:
print(f"FAIL: pop_cap want 18 (start 10 + cot 8) got {staff.pop_cap}")
return 1
pitch_before_seep = staff.pitch
m.tick_hz(int(10 * HZ)) # Staff harvest 15% on seep
if staff.pitch <= pitch_before_seep:
print(f"FAIL: seep did not raise pitch ({pitch_before_seep} -> {staff.pitch})")
return 1
# Afford one Acolyte from leftover start pitch + seep
one = train_t1_melee(m, FACTION_STAFF, n=1)
if one != ["acolyte:ok"]:
print(f"FAIL: train one {one} pitch={staff.pitch}")
return 1
m.tick_hz(int(8 * HZ) + 2)
if not any(u.spec_id == "acolyte" and u.alive for u in staff.units):
print("FAIL: acolyte not trained")
return 1
# Offline cheat for army mass (GAMEPLAY.md cheats offline-only)
m.grant_pitch(FACTION_STAFF, 500.0)
train = train_t1_melee(m, FACTION_STAFF, n=4)
if any(not t.endswith(":ok") for t in train):
print(f"FAIL: train army {train}")
return 1
m.tick_hz(int(4 * 8 * HZ) + 2)
acolytes = [u for u in staff.units if u.spec_id == "acolyte" and u.alive]
if len(acolytes) < 5:
print(f"FAIL: want >=5 acolytes got {len(acolytes)}")
return 1
m.order_attack_hq(FACTION_STAFF)
# 5 * 4 dps = 20 HP/s → 5000 HP ≈ 250s
for _ in range(HZ * 300):
if m.phase == MatchPhase.ENDED:
break
m.tick_hz(1)
if m.phase != MatchPhase.ENDED or m.winner != FACTION_STAFF:
print(f"FAIL: expected staff win got phase={m.phase} winner={m.winner}")
print(m.snapshot())
return 1
if host.hq_alive:
print("FAIL: host HQ still alive")
return 1
print(
f"PASS: open+seed+seep+T1 HQ kill t={m.sim_t:.1f}s "
f"staff_pitch={staff.pitch:.1f} ticks={m.tick_i}"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())