feat(runner): wire spell registry in CLI and Monte Carlo

This commit is contained in:
ctan
2026-08-31 13:22:16 +02:00
parent 3f65e769cf
commit a88187db20
4 changed files with 89 additions and 3 deletions
+18 -2
View File
@@ -17,12 +17,14 @@ if TYPE_CHECKING:
from pf1e_simulator.grid import Grid
from pf1e_simulator.map import MapSpec
from pf1e_simulator.models import Combatant
from pf1e_simulator.spells import SpellSpec
from pf1e_simulator.combat import CombatantState, CombatantStats, CombatEngine
from pf1e_simulator.grid import Grid
from pf1e_simulator.map import zone_cells
from pf1e_simulator.metrics import win_rate, win_rate_band, win_rate_sigma
from pf1e_simulator.rng import SeededRng
from pf1e_simulator.spells import SPELLS_DIR, load_spell_registry
@dataclass(frozen=True)
@@ -123,13 +125,20 @@ def run_encounter(
seed: int,
*,
policy: Policy | None = None,
spell_registry: dict[str, SpellSpec] | None = None,
) -> EncounterReport:
"""Run `runs` battles (one SeededRng per run) and aggregate balance metrics."""
"""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.
"""
if runs <= 0:
msg = "runs must be positive"
raise ValueError(msg)
grid = Grid.from_spec(spec.map)
states = build_states(spec)
if spell_registry is None:
spell_registry = load_spell_registry(SPELLS_DIR)
ids = [s.combatant.id for s in states]
wins = {side.name: 0 for side in spec.sides}
draws = 0
@@ -139,7 +148,14 @@ def run_encounter(
if index > 0:
states = build_states(spec)
rng = SeededRng(seed + index)
result = CombatEngine(rng, grid, states, round_cap=spec.round_cap, policy=policy).run()
result = CombatEngine(
rng,
grid,
states,
round_cap=spec.round_cap,
policy=policy,
spell_registry=spell_registry,
).run()
if result.winner is None:
draws += 1
else: