Files
ctan f61fc5c61f docs: reorganize documentation structure
- Clean README.md with installation, usage, CLI, examples
- Move data formats to docs/data-formats/ (map.md, monster.md, pc-sheets.md, index.md)
- Move architecture to docs/architecture/ (index.md, technical-architecture.md, phase-2-llm-architecture.md)
- Add contributing guidelines in docs/contributing.md
- Clean README.md: install, usage, CLI, data-formats/rules/architecture links, contributing link
2026-09-26 20:12:05 +02:00

64 lines
4.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architecture technique
## Modules principaux
| Module | Responsabilité |
|--------|----------------|
| `combat.py` | `CombatEngine` : initiative, actions, résolution attaques, états de vie, incantation, invocation, manœuvres, transcript, politique par défaut, `Observation`/`LLMPolicy` |
| `dice.py` | Parseur notation dés (`2d4+1`) + lancer |
| `rng.py` | `SeededRng` (flux reproductible par graine), `ScriptedRng` (tests) |
| `models.py` | Schémas Pydantic v2 : `Combatant`, `AbilityScores`, `ACProfile`, `AttackSpec`, `DamageComponent`, `Saves`, `DamageReduction` |
| `map.py` | `MapSpec`/`TerrainType`, chargement YAML, validation (dimensions, chars, zones, déploiement, markers) |
| `grid.py` | Grille 5-10-5 : `distance`, `step_cost`, `diagonal_allowed`, Dijkstra `reachable`, `airborne_view` |
| `los.py` | Ligne d'effet, couvert par règle des coins : gate dans `weapon_for`, bonus dans `resolve_attack` |
| `areas.py` | Géométrie gabarits zone : `cone_cells` (quart cercle 90°), `line_cells` (Bresenham 5ft), bursts via `_area_creatures` |
| `combat.py` | `CombatEngine` déterministe : initiative, actions, résolution attaques, tailles/allonges, high ground, actions immédiates opt-in, états de vie, incantation, invocation, manœuvres, capacités de classe, transcripts, politique par défaut, `Observation`/`LLMPolicy` |
| `effects.py` | `StatModifier` + `resolve_modifiers` (empilement PF1e : dodge/racial/trait/sans-type s'additionnent ; moral/sacré/profane/enhancement gardent le max) |
| `conditions.py` | 29 conditions PF1e : `Condition` (modificateurs + flags comportementaux + `miss_chance`) |
| `abilities.py` | Dons : `AbilitySpec` + fabriques (Weapon Focus, Toughness, Power Attack actif −X/+2X, X=BAB//4+1) |
| `spells.py` | `SpellSpec` (niveau, école, portée, save, DC, `requires_ranged_touch`, `harmless`, `cast_time`, `components`) + effets (`DamageEffect`, `ConditionEffect`, `HealEffect`, `BuffEffect`, `AreaEffect`, `OneShotEffect`) + scalage (`scaling` dés, `scaling_flat` bonus plat) |
| `metrics.py` | `win_rate`, `win_rate_sigma`, `win_rate_band` (bande 3σ bornée [0,1]) |
| `runner.py` | `EncounterSpec`/`Side`, `build_states` (placement + désambiguïsation ids), `run_encounter` (1 `SeededRng`/run), `EncounterReport` + attrition |
| `objectives.py` | Primitives Phase 2 : P(survie), P(TPK), P(≥1 mort), PV perdus, rounds, dcile PV restants |
| `phase2_spike.py` | Politique "focus fire" + cartographie danger |
| `cli.py` | `argparse` `pf1e-sim`, rapport FR, codes sortie, flags `--llm-report`/`--output` |
| `loaders/combatant.py` | Chargeur unifié PJ + monstres : résolution dons → `AbilitySpec`, sorts → `Combatant.spells` |
| `loaders/foundry.py` | Migration Foundry one-shot (incomplet — ne pas régénérer `data/pcs/*.json`) |
| `report.py` | LLM report writer P5.3 : `ReportContext`, `LLMReportPolicy`, `ReportBudget`, `format_llm_report` |
## Flux de données principal
```
CLI (args) → _build_spec() → EncounterSpec
↓
load_spell_registry() → spell_registry
↓
run_encounter(spec, runs, seed, spell_registry, ...)
↓
Pour chaque run: SeededRng(seed+i) → CombatEngine → CombatResult
↓
Agrégation → EncounterReport (wins, draws, avg_rounds, attrition)
↓
format_report() → sortie FR
```
## Frontière LLM (Phase 2/3)
```
CombatEngine(llm_policy=LLMPolicy) → avant chaque appel politique:
Observation snapshot (to_dict() → JSON isolé)
LLMPolicy(observation) → tuple[Action, ...]
```
- LLM ne reçoit **que** le snapshot, jamais `engine.*`
- `PromptPack` externe (`PF1E_PROMPT_PACK_DIR`) : `version.txt` + `<name>.md` + `<name>.examples.json`
- `prompt_version = version:name:hash12` (clé cache composite D6)
- Cache `ResponseCache` (clé = modèle|params|prompt_version|schema|prompt_hash)
- Budget `CostBudget` + `CircuitBreaker` + retry/backoff
## Tests & qualité
- Transcripts épinglés (combats 1v1/2v2 scriptés via `ScriptedRng`)
- Assertions statistiques forme fermée (matchup symétrique dans bande 3σ)
- Gate : `uv run pytest -q && uv run ruff check src tests && uv run basedpyright src`
- Ruff `select = ["ALL"]`, ligne 100 ; basedpyright strict sur `src` + `tests`