4.6 KiB
4.6 KiB
| id | title | priority | status | scope | updated_at | parent | related | assigned_by | ||
|---|---|---|---|---|---|---|---|---|---|---|
| p2-43a-followup-gdscript-delegation | Shared infra — wire GdAiController into auto_play.gd so Rail-1 bridges can be one-liners | p3 | done | game1 | 2026-05-15 | p0-26 |
|
shipwright |
Summary
Rail-1 ports keep landing the Rust bridge (GdAiController::pick_*) but
cannot collapse the GDScript call site to a one-liner because
auto_play.gd does not currently instantiate a GdAiController — same
constraint that holds _pick_research inline today. This is a shared
infrastructure gap, not the responsibility of any individual port
objective.
Per CLAUDE.md Rail-1 and p0-26-ai-tactical-rust-port.md, the resolution
is a single wiring change: auto_play.gd (and any other AI driver
scenes) grows a GdAiController member via the same path the tactical
bridge will use. Once that lands, every accumulated [ ] one-liner delegation bullet across the p2-43* family flips green in a single
sweep.
Acceptance
auto_play.gdholds aGdAiControllerinstance (or equivalent autoload-style accessor) reachable from each_pick_*site. →GameState.get_ai_controller()+reset_ai_controllers()+_ai_map_initializedfield added tosrc/game/engine/src/autoloads/game_state.gd. Lazy-instantiates on first call, returns the sharedGdAiControllerRefCounted, cleared on game-reset. This is also the accessorai_turn_bridge.gd(lines 214, 297, 306, 315, 324, 342) already calls — the autoload addition fixes a latent runtime-null gap as well as enabling the bullet below._pick_culture_traditioncollapses to a single bridge call (closes p2-43a's remaining delegation bullet). →src/game/engine/scenes/tests/auto_play.gd::_pick_culture_traditionnow builds a{id, cost}candidate list fromCultureWeb.get_available_traditions/get_tradition_dataand hands axes + candidates toGameState.get_ai_controller().pick_culture_tradition(...)in one call. All inline scoring (mercantile blend, cost normalisation, argmax) removed — Rustmc_ai::tactical::culture_pickis now the single source of truth for tradition selection._pick_researchcollapses to a single bridge call. →GdAiController::pick_research(available_json, personality_axes_json) -> GStringadded tosrc/simulator/api-gdext/src/ai.rsmirroring thepick_culture_traditionpattern: parses anAiTechCandidate[]JSON + raw 1..=10 axes dict, builds a minimalAiPlayerStatecarrying the axes, derivesStrategicWeights::from_race_axes, and delegates tomc_ai::evaluator::ScoringEvaluator::pick_tech. Parse failures / empty lists return""and log viagodot_error!— the bridge never silently substitutes a default.auto_play.gd::_pick_researchnow filters out already-researched + unmet-prereq techs (cheap GDScript pass that needs liveplayer.has_techaccess), builds the candidate list, and hands axes + candidates across the bridge in oneGameState.get_ai_controller().pick_research(...)call. All inline scoring (pillar multipliers, prereq-mult Pass 2, tier-3+ mercantile penalty, tier-4 unit unlocks bonus) removed — Rustmc_ai::evaluator::pick_techis now the single source of truth. Verifiedcargo check -p magic-civ-physics-gdextexit 0.- Any other mirrored
_pick_*AI helpers inauto_play.gduse the controller, no inline scoring shadows remain. → Only_pick_researchand_pick_culture_traditionexist inauto_play.gd(verified bygrep -n "^func _pick_"). Both now delegate toGdAiController— no inline scoring shadows remain. - GUT smoke covers controller instantiation under
--headless. → Newsrc/game/engine/tests/unit/ai/test_ai_controller_accessor.gdasserts non-null return, idempotency, and reset semantics. Existingtest_gdextension_contract.gd::EXPECTED_CLASSES["GdAiController"]already covers method-presence. Staletest_gd_ai_controller_absentsentinel removed — it contradicted the EXPECTED_CLASSES entry.
K/N = 5/5 — all delegation bullets ticked.
Out of scope
- Re-porting any individual
pick_*function — those Rust modules already exist and have unit tests; this objective only wires them. - Adding the
GdAiController::pick_researchRust#[func]— landed inline with this objective rather than deferred to p0-26, since the scorer (mc_ai::evaluator::pick_tech) already existed and the bridge was a thin shim mirroringpick_culture_tradition.