f61fc5c61f
- 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
4.1 KiB
4.1 KiB
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.* PromptPackexterne (PF1E_PROMPT_PACK_DIR) :version.txt+<name>.md+<name>.examples.jsonprompt_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 sursrc+tests