- default_policy returns (move, attack) when target out of reach: full-speed
move along the Dijkstra path (up to speed cells, 5-10-5 diagonals, stops
adjacent to nearest enemy), then attack if a weapon is usable; returns
(attack,) when already in range.
- _move logs the full path in one line (start)->(step1)->...->(end); silent
on empty path. _execute is silent on invalid attacks/moves (no spurious
wait log); run() adds a wait line only when a combatant's turn produced
no transcript line.
- Extract _take_turn helper from run() to keep cyclomatic complexity <= 10.
- Module docstring updated for activated economy.
- 4 pinned transcripts regenerated (move-then-attack shifts combat pacing):
test_default_policy_moves_toward_enemy, test_scripted_2v2_transcript
(monsters win in R2 instead of R3 — orc-2 moves + attacks same turn),
test_ranged_no_line_of_effect_moves_around_wall (archer rounds wall and
shoots in 1 turn), test_ranged_weapon_unusable_beyond_maximum_range
(archer moves 6 cells then attacks at -14 penalty).
- README: demo numbers refreshed (1v2: 49.6%->22.0%, 11.4->5.2 rounds;
3v2: 97.8%->91.4%, 6.9->6.0 rounds), rules and non-modeled sections
updated for the activated economy.
A weapon with count>1 ('2x Talons') now resolves one independent attack
roll per swing in the same attack action (label '#N'), aggregating stats
per swing. Remaining swings are lost if the target drops mid-routine.
BAB iterative attacks remain out of Phase 0 scope.
All 8 sheets from fiches_personnages/ converted to the strict Combatant
JSON schema (data/monsters convention) so the CLI can run PCs directly:
--side players data/pcs/esha.json
- id derived from the file stem; source field points at the original sheet
- oni: placeholder attacks without encoded damage ('une Pioche', 'Weapon')
left out, documented in notes (pick stats ambiguous light/heavy in the
rules KB; no identification possible for 'Weapon')
- README: PC section now documents data/pcs usage
- tests: parametrized load check + golden spots for the 8 PC files
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.