docs(@projects/@magic-civilization): 🐺 p3-30 — decision core done; integration is a verified fork
stub → partial. Acceptance #1 (decide_wild_actions) + #4 (determinism) ✓ with
cited evidence (95a2e580b, mc-ai 301/0). Record the verified premise that the
headless GameState has no roaming wild-unit substrate (units implicit-owned,
wilds = lairs + encounters), making the in-step path a substrate-build that
duplicates encounters vs. the allowed GdWildAiController bridge. Recommend the
bridge; integration + .gd deletion stay render/decision-gated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
95a2e580bc
commit
e477784731
1 changed files with 47 additions and 11 deletions
|
|
@ -2,7 +2,7 @@
|
|||
id: p3-30
|
||||
title: Port wild-creature AI from GDScript to Rust (Rail-1 compliance)
|
||||
priority: p3
|
||||
status: stub
|
||||
status: partial
|
||||
scope: game1
|
||||
owner: warcouncil
|
||||
updated_at: 2026-06-27
|
||||
|
|
@ -49,20 +49,28 @@ GDScript decision system.
|
|||
|
||||
## Acceptance
|
||||
|
||||
- [ ] `mc-ai` (or `mc-combat`/`mc-turn` per infra call) exposes a deterministic wild-creature
|
||||
decision fn — `decide_wild_actions(state, rng) -> Vec<Action>` — covering target-select,
|
||||
step-toward, attack, and leashed roam, reusing the existing `Action`/tactical plumbing from
|
||||
p0-26 where it fits.
|
||||
- [x] `mc-ai` exposes a deterministic wild-creature decision fn —
|
||||
`mc_ai::wild::decide_wild_actions(ctx, rng) -> Vec<Action>` (wild.rs, d08099dbe) — covering
|
||||
target-select, step-toward (chase), attack (iff adjacent, per the player-tactical convention),
|
||||
leash-return, city-drift, and leashed roam, reusing the existing `Action` taxonomy + `mc_core`
|
||||
hex helpers + `XorShift64`. Combat resolution stays in `mc_combat::wilds` (no formula drift).
|
||||
- [ ] Driven from inside `mc_turn::TurnProcessor::step` as a turn phase (so the p3-29 swap needs
|
||||
no wild carve-out), OR via a `GdWildAiController` bridge if it must stay engine-driven —
|
||||
infra decides; default is in-`step`.
|
||||
infra decides; default is in-`step`. **BLOCKED ON FORK DECISION (see Notes 2026-06-27):** the
|
||||
headless `GameState` has NO roaming wild-unit substrate (units are implicit-owned per
|
||||
`PlayerState::units`; no `owner == -1`; wilds are modeled as `npc_buildings` lairs +
|
||||
`process_fauna_encounters`). "Drive in `step`" therefore requires first BUILDING a headless
|
||||
roaming-wild substrate (spawn at lairs, store, persist) that partly duplicates the encounter
|
||||
system — a real scope/architecture fork vs. the allowed `GdWildAiController` bridge path.
|
||||
- [ ] `wild_creature_ai.gd` DELETED (or reduced to a thin bridge with zero decision logic);
|
||||
`_process_wild_creatures` becomes a no-op or bridge call.
|
||||
- [ ] Determinism preserved — same seed + state → same wild actions (XorShift64, per the
|
||||
`p1-09` contract). Regression test alongside the p0-26 tactical suite.
|
||||
`_process_wild_creatures` becomes a no-op or bridge call. **(render-gated — live turn swap.)**
|
||||
- [x] Determinism preserved — same seed + state → same wild actions (XorShift64). Covered by
|
||||
`wild::tests::is_deterministic_for_same_context_and_seed` (d08099dbe).
|
||||
- [ ] Behaviour parity: headless run shows wilds still guard lairs, attack units in radius, and
|
||||
roam within leash — compared against a pre-port baseline (not just "compiles").
|
||||
- [ ] GUT green; `cargo test` green.
|
||||
roam within leash — compared against a pre-port baseline. **(needs the integration above.)**
|
||||
- [~] GUT green (no GDScript touched yet); `cargo test` green — `mc-ai` lib **301/0** incl. 12
|
||||
new `wild::tests::*` (target-select, chase, attack-iff-adjacent, leash return, leashed roam,
|
||||
city drift, passable gating, no-movement skip, determinism, `wilds.json` config parse).
|
||||
|
||||
## Notes
|
||||
|
||||
|
|
@ -72,3 +80,31 @@ decision system**, so it must close before Rail-1 can be called complete. Sequen
|
|||
p3-29's event surface + dict keystone (T1-T6) so the swap has a clean target; it can land before
|
||||
or alongside the p3-29 swap (steps 3-5). Cross-ref `mc-combat::wilds` (resolution already Rust)
|
||||
to avoid duplicating combat math.
|
||||
|
||||
## Progress 2026-06-27 — decision core landed; integration is a fork (owner call)
|
||||
|
||||
**Done (headless, fork-neutral): `mc_ai::wild`** (d08099dbe) — the pure decision logic ported +
|
||||
12 tests, `mc-ai` 301/0. This brick is identical regardless of how it's driven, so it was safe
|
||||
to build before resolving the fork.
|
||||
|
||||
**The fork (verified premise, not inferred):** `decide_wild_actions` needs roaming wild units to
|
||||
act on. The headless `GameState` has none — `PlayerState::units` are implicit-owned (no
|
||||
`owner == -1`); the headless sim models wilds as `npc_buildings` lairs (game_state.rs:575) +
|
||||
`process_fauna_encounters` (probabilistic combat), NOT autonomous roamers. So the two integration
|
||||
paths are materially different in cost/scope:
|
||||
- **A. In-`step` (the acceptance default):** first build a headless roaming-wild substrate —
|
||||
spawn `owner == -1` creatures at lairs, store + persist them on `GameState`, tick them through
|
||||
`decide_wild_actions` + a movement/attack applier. Large; partly **duplicates the existing
|
||||
encounter system** (wild combat near lairs already happens via encounters) → risks modeling the
|
||||
same thing twice / gold-plating the headless sim.
|
||||
- **B. `GdWildAiController` bridge (explicitly allowed):** the live game keeps its roaming wild
|
||||
units; a thin bridge projects them into `WildContext`, calls `decide_wild_actions`, and applies
|
||||
the returned `Action`s. Rail-1-clean (decision logic is Rust), no headless substrate, but the
|
||||
wild pass stays engine-driven (a tracked carve-out outside `step()`) and the proof is
|
||||
render-gated.
|
||||
|
||||
Recommendation: **B** — it removes the GDScript *decision logic* (the actual Rail-1 violation)
|
||||
without building a second wild model headless. "Everything inside `step()`" is the purist ideal,
|
||||
but a headless roamer substrate duplicating encounters is the kind of disabled/parallel system the
|
||||
rails warn against. Surfaced to owner; integration (A vs B) + the `.gd` deletion are the remaining
|
||||
work and are render/decision-gated.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue