feat(phase2): danger-cartography spike - per-run outcomes, objectives, focus-fire adversary

run_encounter gains an opt-in collect_runs flag returning one RunOutcome
per battle (winner, rounds, per-combatant id/side/hp/hp_max/dead) next to
the EncounterReport; default behavior is unchanged. objectives.py provides
the pure optimizer primitives: P(survives all), P(TPK), P(>=1 death),
expected hp lost, expected rounds and a worst-decile (CVaR-style) tail
metric. phase2_spike.py holds the spike-level strengthened monster policy
(focus fire on the lowest-AC enemy, ties to the wounded; party side stays
on default_policy). tests/test_phase2_danger_map.py prints the danger-map
sketch: warband and lich scenarios measured default vs focus-fire at
100 runs / seed 11, pinning the delta deterministically.
This commit is contained in:
2026-09-21 12:07:23 +02:00
parent e5c4892a57
commit de88543c5c
7 changed files with 734 additions and 27 deletions
+12 -2
View File
@@ -2321,7 +2321,17 @@ Limites de sorts (conformance D6-D9, documentées plutôt que bricolées) :
`win_rate_band` (bande 3σ bornée à [0, 1]).
- `runner.py` — `EncounterSpec`/`Side`, `build_states` (placement en zone +
désambiguïsation des ids), `run_encounter` (un `SeededRng` par run) et
agrégation en `EncounterReport` avec attrition par combattant.
agrégation en `EncounterReport` avec attrition par combattant. Option
`collect_runs=True` (Phase 2) : renvoie en plus un `RunOutcome` par combat
(vainqueur, rounds, pv/état de mort par combattant) — comportement par
défaut inchangé.
- `objectives.py` — primitives d'objectif Phase 2 (pur, sans moteur) :
P(survie totale), P(TPK), P(≥1 mort), pv perdus attendus, rounds attendus
et métrique de queue (dcile pire des pv restants) sur les `RunOutcome`.
- `phase2_spike.py` — spike de cartographie du danger : politique monstre
« focus fire » (concentration sur la cible la plus faible en CA, départage
aux pv) contre laquelle la carte de danger mesure le groupe ; la face PJ
reste sur `default_policy`.
- `cli.py` — front-end argparse `pf1e-sim`, rapport français, code de sortie 2
en cas d'erreur.
- `loaders/` — `combatant.py` (chargeur unifié PJ + monstres avec résolution
@@ -2335,7 +2345,7 @@ Limites de sorts (conformance D6-D9, documentées plutôt que bricolées) :
La gate de validation complète (tests + lint + types) :
```bash
uv run pytest -q # 1981 tests
uv run pytest -q # 2000 tests
uv run ruff check src tests
uv run basedpyright src # mode strict
```