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

4.1 KiB
Raw Permalink Blame History

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