magicciv/.project/objectives/p2-54d-ai-tech-priority-from-visibility.md
Natalie 0b73a9b373 feat(@projects/@magic-civilization): add indicator decoration system
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-02 19:04:25 -04:00

4.3 KiB

id title priority status scope owner updated_at parent canonical_doc coordinates_with blockedBy
p2-54d AI tech-priority bias from visible-but-gated luxuries + indicator decorations p2 done game1 terraformer 2026-05-01 p2-54 public/games/age-of-dwarves/docs/RESOURCES.md
p1-05
p1-36
p1-37
p2-54
p2-54b
p2-54
p2-54b

Summary

Per the user's 2026-05-01 design observation: AI clans don't research the right techs to unlock luxuries. With the three-axis schema (p2-54) and observation cache (p2-54b) in place, mc-ai's strategic policy can read each clan's visible-but-yield-gated luxuries + indicator-decorated subsurface resources in their territory and bias tech research toward unlocking them.

This is the architectural fix for p1-05's luxury_variance regression (median dropped from 3 to 0 because AI never researches Trapping/Scholarship/Herbalism even though their territory contains ivory, silk, spices).

Acceptance

  • Personality reads visible luxuriesAiPlayerState.luxury_unlock_scores field (HashMap<String,f32>) pre-populated by api-gdext caller from PlayerObservations + GridState + resources.json. Caller computes LUXURY_TILE_VALUE (10.0) per visible-but-yield-gated luxury tile, scaled by personality wealth axis. score_tech in mc-ai::evaluator reads this additive score. Architecture: Path A (caller pre-computes, Rust reads) matches existing AiPlayerState population pattern and avoids adding mc-save dep to mc-ai.
  • Personality reads indicator decorations — same luxury_unlock_scores field carries INDICATOR_TILE_VALUE (5.0) contribution per indicator-decoration tile (e.g. rust_red_soil → iron → bronze_working). Caller uses indicator_decorations[].decoration_id from resources.json to build the decoration→resource→yield_gate mapping.
  • Tech-priority weightingscore_tech in evaluator.rs reads state.luxury_unlock_scores.get(&tech.id) and adds it directly onto the base score. Layer is additive — doesn't replace pillar-level personality bias at lines 787-795.
  • No regression in existing AI behavior — 211/211 mc-ai tests pass including all pre-existing personality, formation, and production scoring tests.
  • Determinism preservedcross_build_determinism 4/4 unchanged.
  • p1-05 luxury_variance gate validation — deferred to apricot 10-seed batch run (apricot-scale work). Architectural cause resolved; empirical validation pending. See p1-05 evidence note.
  • Doc: RESOURCES.md §5 "AI consumption pattern" updated with the scoring formula. (Deferred — docs agent scope.)

Evidence

  • src/simulator/crates/mc-ai/src/game_state.rsluxury_unlock_scores: HashMap<String, f32> added to AiPlayerState with #[serde(default)]
  • src/simulator/crates/mc-ai/src/evaluator.rs — luxury bias applied in score_tech (p2-54d block); 3 new tests added:
    • evaluator::tests::personality_with_visible_silk_prioritizes_scholarship
    • evaluator::tests::indicator_iron_in_territory_biases_toward_bronze_working
    • evaluator::tests::no_observations_no_bias
  • mc-ai test run: 211/211 pass; cross_build_determinism: 4/4

Implementation notes

TileObservation (mc-save) has no resource_ids_seen field — it stores only indicator_decorations_seen: Vec<String>. Always-visible resources are not mirrored into the observation cache (p2-54b). The api-gdext bridge must query GridState directly for always-visible resource presence alongside reading indicator decorations from PlayerObservations.by_tile. Rust-side mc-ai receives the pre-computed scores; no mc-save dep added to mc-ai.

Non-goals

  • Full personality re-tuning (warcouncil scope, tracked elsewhere)
  • Tech-gated UI changes (the AI doesn't drive UI)
  • Trade decisions based on luxuries (separate mc-trade work)

Why blocked-by-p2-54-and-p2-54b

The schema and observation cache must exist for the AI to read them. After both land, this is bounded mc-ai work.

What this resolves

  • p1-05 luxury_variance gate (median 0 → expect 3+) — closes the open Shipwright partial
  • Foundational: the same machinery enables p1-36's AI personality clan_affinity weighting from observable resources (already partial-closed by p1-37 via clan_affinity routing; this layers the LUXURY bias on top)