feat(runner): wire spell registry in CLI and Monte Carlo
This commit is contained in:
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
|
||||
from pf1e_simulator.loaders import CombatantLoadError, SheetLoadError, load_combatant
|
||||
from pf1e_simulator.map import MapValidationError, load_map
|
||||
from pf1e_simulator.runner import EncounterSpec, Side, run_encounter
|
||||
from pf1e_simulator.spells import SPELLS_DIR, load_spell_registry
|
||||
|
||||
|
||||
class CliError(Exception):
|
||||
@@ -111,7 +112,8 @@ def main(argv: list[str] | None = None) -> int:
|
||||
print(f"pf1e-sim: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
try:
|
||||
report = run_encounter(spec, runs=args.runs, seed=args.seed)
|
||||
registry = load_spell_registry(SPELLS_DIR)
|
||||
report = run_encounter(spec, runs=args.runs, seed=args.seed, spell_registry=registry)
|
||||
except ValueError as exc:
|
||||
print(f"pf1e-sim: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -25,6 +25,8 @@ from pf1e_simulator.effects import BonusType, StatModifier, StatTarget
|
||||
if TYPE_CHECKING:
|
||||
from pf1e_simulator.loaders.foundry import Json
|
||||
|
||||
SPELLS_DIR = Path(__file__).resolve().parent.parent.parent / "data" / "spells"
|
||||
|
||||
SpellRange = Literal["personal", "touch", "close", "medium", "long"]
|
||||
SaveType = Literal["fort", "ref", "will", "none"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user