a21e234b41
Adds the S6 layer on top of the deterministic combat engine: - metrics.py: closed-form binomial statistics (win rate, standard error, 3-sigma confidence band) with clamped degenerate proportions. - runner.py: EncounterSpec/Side definitions, per-run SeededRng streams, global duplicate-id disambiguation, attrition averages per combatant. - cli.py: pf1e-sim entry point producing a French balance report with win rates, 3-sigma bands, draws, rounds, and attrition. - Movement fix: greedy straight-line heuristic could oscillate at walls; _step_toward now follows the true shortest path via an unbounded Dijkstra cost field from the target (Grid.reachable budget=None). - Regression tests: melee unit routes around a wall and engages; grid unbounded-budget coverage.
60 lines
1.6 KiB
Python
60 lines
1.6 KiB
Python
"""Tests for the pf1e-sim command-line interface."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
from typing import TYPE_CHECKING
|
||
|
||
if TYPE_CHECKING:
|
||
import pytest
|
||
|
||
from pf1e_simulator.cli import main
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
MAP = ROOT / "data" / "maps" / "sample_arena.yaml"
|
||
GOBLIN = ROOT / "data" / "monsters" / "goblin.json"
|
||
ORC = ROOT / "data" / "monsters" / "orc.json"
|
||
|
||
|
||
def test_cli_prints_balance_report(capsys: pytest.CaptureFixture[str]) -> None:
|
||
rc = main(
|
||
[
|
||
"--map",
|
||
str(MAP),
|
||
"--side",
|
||
"players",
|
||
str(GOBLIN),
|
||
str(GOBLIN),
|
||
str(GOBLIN),
|
||
"--side",
|
||
"monsters",
|
||
str(ORC),
|
||
str(ORC),
|
||
"--runs",
|
||
"50",
|
||
"--seed",
|
||
"1",
|
||
]
|
||
)
|
||
out = capsys.readouterr().out
|
||
assert rc == 0
|
||
assert "Rapport d'équilibrage" in out
|
||
assert "Victoires players" in out
|
||
assert "Victoires monsters" in out
|
||
assert "bande 3σ" in out
|
||
assert "Nuls" in out
|
||
assert "Rounds moyens" in out
|
||
assert "Attrition moyenne" in out
|
||
|
||
|
||
def test_cli_rejects_side_not_in_deployment(capsys: pytest.CaptureFixture[str]) -> None:
|
||
rc = main(["--map", str(MAP), "--side", "bogus", str(GOBLIN), "--runs", "10"])
|
||
assert rc == 2
|
||
assert "deployment" in capsys.readouterr().err
|
||
|
||
|
||
def test_cli_rejects_missing_deployment_side(capsys: pytest.CaptureFixture[str]) -> None:
|
||
rc = main(["--map", str(MAP), "--side", "players", str(GOBLIN), "--runs", "10"])
|
||
assert rc == 2
|
||
assert "monsters" in capsys.readouterr().err
|