feat(cli): add --transcript option
This commit is contained in:
@@ -100,6 +100,11 @@ def main(argv: list[str] | None = None) -> int:
|
||||
default=100,
|
||||
help="max rounds per battle (default: 100)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--transcript",
|
||||
action="store_true",
|
||||
help="print the turn-by-turn log of the first battle before the report",
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
if not args.side:
|
||||
parser.error("at least one --side is required")
|
||||
@@ -113,9 +118,19 @@ def main(argv: list[str] | None = None) -> int:
|
||||
return 2
|
||||
try:
|
||||
registry = load_spell_registry(SPELLS_DIR)
|
||||
report = run_encounter(spec, runs=args.runs, seed=args.seed, spell_registry=registry)
|
||||
report = run_encounter(
|
||||
spec,
|
||||
runs=args.runs,
|
||||
seed=args.seed,
|
||||
spell_registry=registry,
|
||||
capture_first_transcript=args.transcript,
|
||||
)
|
||||
except ValueError as exc:
|
||||
print(f"pf1e-sim: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
if args.transcript and report.transcript is not None:
|
||||
print("=== Déroulé du premier combat ===")
|
||||
print("\n".join(report.transcript))
|
||||
print()
|
||||
print(format_report(report, spec))
|
||||
return 0
|
||||
|
||||
@@ -57,7 +57,12 @@ class CombatantAttrition:
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EncounterReport:
|
||||
"""Aggregated results of a Monte Carlo encounter."""
|
||||
"""Aggregated results of a Monte Carlo encounter.
|
||||
|
||||
`transcript` holds the turn-by-turn log of the first battle when
|
||||
`run_encounter` is called with ``capture_first_transcript=True``; it is
|
||||
None otherwise (the default keeps the report cheap for pure Monte Carlo).
|
||||
"""
|
||||
|
||||
runs: int
|
||||
seed: int
|
||||
@@ -65,6 +70,7 @@ class EncounterReport:
|
||||
draws: int
|
||||
avg_rounds: float
|
||||
attrition: dict[str, CombatantAttrition]
|
||||
transcript: tuple[str, ...] | None = None
|
||||
|
||||
def win_rate(self, side: str) -> float:
|
||||
"""Fraction of runs won by `side` (closed form, see metrics)."""
|
||||
@@ -126,11 +132,15 @@ def run_encounter(
|
||||
*,
|
||||
policy: Policy | None = None,
|
||||
spell_registry: dict[str, SpellSpec] | None = None,
|
||||
capture_first_transcript: bool = False,
|
||||
) -> EncounterReport:
|
||||
"""Run `runs` battles (one SeededRng per run) and aggregate balance metrics.
|
||||
|
||||
The spell registry is loaded once (from ``data/spells/`` unless overridden)
|
||||
and passed into every ``CombatEngine`` so caster PCs actually cast.
|
||||
|
||||
When ``capture_first_transcript`` is True, the turn-by-turn log of the
|
||||
first battle (``seed + 0``) is stored in ``report.transcript``.
|
||||
"""
|
||||
if runs <= 0:
|
||||
msg = "runs must be positive"
|
||||
@@ -144,6 +154,7 @@ def run_encounter(
|
||||
draws = 0
|
||||
total_rounds = 0
|
||||
totals = {cid: CombatantStats() for cid in ids}
|
||||
transcript: tuple[str, ...] | None = None
|
||||
for index in range(runs):
|
||||
if index > 0:
|
||||
states = build_states(spec)
|
||||
@@ -156,6 +167,8 @@ def run_encounter(
|
||||
policy=policy,
|
||||
spell_registry=spell_registry,
|
||||
).run()
|
||||
if capture_first_transcript and index == 0:
|
||||
transcript = result.transcript
|
||||
if result.winner is None:
|
||||
draws += 1
|
||||
else:
|
||||
@@ -185,4 +198,5 @@ def run_encounter(
|
||||
draws=draws,
|
||||
avg_rounds=total_rounds / runs,
|
||||
attrition=attrition,
|
||||
transcript=transcript,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user