docs(objectives): 📝 p3-17 exploration objective + p3-16 blocked_by p3-17
- p3-16: war-declaration marked implemented + tested + proven (turns 17/18);
status held partial with blocked_by: [p3-17] (personality differentiation
still gated on AI exploration).
- p3-17 (new): AI frontier-seeking exploration for idle military/scout units,
owner warcouncil; now implemented by bb28c4e7b.
- regen objectives dashboard.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bb28c4e7b1
commit
45ffb44bcf
5 changed files with 227 additions and 77 deletions
|
|
@ -521,5 +521,6 @@
|
|||
| [p3-13d](p3-13d-anomalous-events.md) | ✅ done | P3 | Anomalous events — aurora, fog_bank, thermal_anomaly | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-14](p3-14-game-start-script.md) | ✅ done | P3 | Declarative game-start script + runner — data-driven, moddable opening sequence | [shipwright](../team-leads/shipwright.md) | 🟢 |
|
||||
| [p3-15](p3-15-hotseat-multiplayer.md) | ✅ done | P3 | Local hotseat multiplayer — multiple humans alternating on one device | [shipwright](../team-leads/shipwright.md) | 🟢 |
|
||||
| [p3-16](p3-16-ai-proactive-war-declaration.md) | 🟡 partial | P3 | AI proactive war-declaration via the courier system | [warcouncil](../team-leads/warcouncil.md) | 🟢 |
|
||||
| [p3-16](p3-16-ai-proactive-war-declaration.md) | 🟡 partial | P3 | AI proactive war-declaration via the courier system | [warcouncil](../team-leads/warcouncil.md) | 🔒 p3-17 |
|
||||
| [p3-17](p3-17.md) | 🟡 partial | P3 | AI exploration / frontier-seeking for idle military + scout units | [warcouncil](../team-leads/warcouncil.md) | 🟢 |
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
| **P0** | 0 | 0 | 0 | 0 | 0 | 44 | 44 |
|
||||
| **P1** | 0 | 0 | 0 | 0 | 1 | 88 | 89 |
|
||||
| **P2** | 0 | 0 | 0 | 0 | 1 | 132 | 133 |
|
||||
| **P3 (oos)** | 0 | 1 | 0 | 0 | 29 | 26 | 56 |
|
||||
| **total** | **0** | **1** | **0** | **0** | **31** | **290** | **322** |
|
||||
| **P3 (oos)** | 0 | 2 | 0 | 0 | 29 | 26 | 57 |
|
||||
| **total** | **0** | **2** | **0** | **0** | **31** | **290** | **323** |
|
||||
|
||||
</td><td valign='top' style='padding-left:2em'>
|
||||
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
| Team Lead | Remaining |
|
||||
|---|---|
|
||||
| [warcouncil](../team-leads/warcouncil.md) | 1 |
|
||||
| [warcouncil](../team-leads/warcouncil.md) | 2 |
|
||||
|
||||
</td></tr></table>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"generated_at": "2026-06-24T00:06:25Z",
|
||||
"generated_at": "2026-06-24T00:56:30Z",
|
||||
"totals": {
|
||||
"done": 290,
|
||||
"in_progress": 0,
|
||||
"partial": 1,
|
||||
"partial": 2,
|
||||
"stub": 0,
|
||||
"missing": 0,
|
||||
"oos": 31,
|
||||
"total": 322
|
||||
"total": 323
|
||||
},
|
||||
"objectives": [
|
||||
{
|
||||
|
|
@ -3575,9 +3575,22 @@
|
|||
"status": "partial",
|
||||
"scope": "game1",
|
||||
"owner": "warcouncil",
|
||||
"updated_at": "2026-06-23",
|
||||
"updated_at": "2026-06-24",
|
||||
"blocked_by": [
|
||||
"p3-17"
|
||||
],
|
||||
"summary": "The canonical courier-diplomacy model (`COMMUNICATIONS.md` §\"War declaration\nsemantics\") assumes a player **dispatches a war-dec envelope** to enter war: the\n**sender** enters `War` immediately on dispatch and its units can attack the same\nturn; the **recipient** flips to `War` on delivery/interception; **defenders\nalways retaliate when struck** regardless of formal `RelationState`. Pairs start\nat **peace** (`mc-player-api/src/projection.rs::project_tactical_relations` defaults\nunset pairs to 0 = peace; `mc-player-api/src/comms_dispatch.rs` flips the shared\n`RelationState` cell to `War` only on delivery).\n\n**The war-declaration decision is now SHIPPED.** A new per-turn diplomacy step\n`decide_diplomacy` (`src/simulator/crates/mc-ai/src/tactical/diplomacy.rs:30`)\nevaluates each *discovered* rival against the clan's `aggression` axis and the\nown-vs-perceived military balance, and emits `Action::DeclareWar { target }`\n(`diplomacy.rs:69`) when conditions warrant. That action is routed through\n`mc_player_api::comms_dispatch::dispatch_war_declaration`\n(`mc-player-api/src/comms_dispatch.rs:59`) — the same envelope path the human\nwar-dec uses — so the **sender** enters `War` on dispatch and the **recipient**\nflips on delivery, exactly per `COMMUNICATIONS.md` §\"War declaration semantics\".\n\nIt is **unit-tested and proven to fire**:\n- 5 `mc-ai` diplomacy cases (`diplomacy.rs:140–185`) cover discovered-weaker\n (declares), undiscovered (holds), already-at-war (holds), cautious-at-parity\n vs warmonger-strikes (axis differentiation), and no-army (holds).\n- The dispatch round-trip is covered by\n `ai_declare_war_maps_to_player_declare_war` (`mc-player-api/src/dispatch.rs:3068`):\n the AI's `DeclareWar` maps to the same `RelationState` mutation the human path\n produces.\n- **Live proof:** in hotseat self-play (seed 42) war-decs dispatch on first\n contact at turns 17/18.\n\n**Why this is still `partial` — the war-dec does not yet transform AI-vs-AI\ngames.** The decision-to-declare half is shipped, but full aggressive AI play is\nblocked on a **separate upstream gap: the AI does not scout/explore** (tracked by\n**p3-17**, AI exploration / frontier-seeking). Two consequences:\n1. War-decs fire only on a fleeting enemy-**unit** sighting, which is rare because\n idle military units never push toward unexplored territory.\n2. Even once at war, `collect_enemy_city_positions` (`tactical/movement.rs:402`)\n returns only **visible** enemy cities — which the AIs almost never see, because\n idle military units fall to `score_patrol_for_military` (`movement.rs:285`,\n scout-sweep of already-known cities / garrison next to friendly cities) with\n **no frontier-seeking**. So the army declares war but cannot find the enemy\n capital to march on.\n\nThe downstream at-war combat path (`is_at_war` gate, `locked_target` maneuver,\n`aggression`-tuned posture per `mc-ai/src/tactical/thresholds.rs`) already works\nonce a pair is at war and an enemy city is known — but discovery starves it.\n**p3-17 must land before personality `aggression` materially differentiates\nAI-vs-AI war outcomes.**"
|
||||
},
|
||||
{
|
||||
"id": "p3-17",
|
||||
"title": "AI exploration / frontier-seeking for idle military + scout units",
|
||||
"priority": "p3",
|
||||
"status": "partial",
|
||||
"scope": "game1",
|
||||
"owner": "warcouncil",
|
||||
"updated_at": "2026-06-24",
|
||||
"blocked_by": [],
|
||||
"summary": "The canonical courier-diplomacy model (`COMMUNICATIONS.md` §\"War declaration\nsemantics\") assumes a player **dispatches a war-dec envelope** to enter war: the\n**sender** enters `War` immediately on dispatch and its units can attack the same\nturn; the **recipient** flips to `War` on delivery/interception; **defenders\nalways retaliate when struck** regardless of formal `RelationState`. Pairs start\nat **peace** (`mc-player-api/src/projection.rs::project_tactical_relations` defaults\nunset pairs to 0 = peace; `mc-player-api/src/comms_dispatch.rs` flips the shared\n`RelationState` cell to `War` only on delivery).\n\n**The gap:** the AI has **no proactive war-declaration logic**. `mc-ai`'s tactical\nentry point `decide_tactical_actions` (`src/simulator/crates/mc-ai/src/tactical/mod.rs`)\nruns movement → combat → settle → production → citizens with **no diplomacy step**,\nand there is **zero** `DeclareWar` / war-dec action anywhere in `mc-ai` (no\nstrategic action variant, no caller of `comms_dispatch::dispatch_war_declaration`\n— the only non-test caller is the human dispatch path at\n`mc-player-api/src/dispatch.rs:1605`). `mc-ai/src/diplomacy.rs` covers only the\np3-01 courier open-borders / shared-map offer/accept heuristics — not war.\n\nConsequence (observed in a 200-turn hotseat self-play): AIs never initiate war →\n`collect_enemy_city_positions` (`tactical/movement.rs`) is always empty because it\nis gated through `is_at_war` (movement.rs:366) → no `locked_target` is ever set →\nmilitary units never maneuver (48 successful moves across 200 turns). Because war\nnever comes in AI-vs-AI play, the five clan personalities\n(`public/games/age-of-dwarves/data/ai_personalities.json`, `aggression` 1–10,\nwarmonger = 9) do **not** manifest distinct aggression: per\n`mc-ai/src/tactical/thresholds.rs`, `aggression` only tunes combat posture **once\nalready at war**, so a difference that only fires after war-declaration never\nfires at all.\n\nThis objective adds the missing strategic decision step so the AI dispatches a\nwar-dec envelope through the courier system when conditions warrant, entering the\nexisting at-war combat path and making personality `aggression` actually drive\nbehavior."
|
||||
"summary": "The AI never scouts or explores the map, which starves both target-acquisition\nand the war-declaration discovery gate. This objective adds a frontier-seeking\nmove for idle military / scout units so first contact and enemy-city discovery\nhappen reliably in AI-vs-AI play."
|
||||
}
|
||||
],
|
||||
"blocked": [
|
||||
|
|
@ -3822,12 +3835,18 @@
|
|||
"blockedBy": [
|
||||
"p0-36"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "p3-16",
|
||||
"blockedBy": [
|
||||
"p3-17"
|
||||
]
|
||||
}
|
||||
],
|
||||
"remaining_by_lead": [
|
||||
{
|
||||
"owner": "warcouncil",
|
||||
"remaining": 1
|
||||
"remaining": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ priority: p3
|
|||
status: partial
|
||||
scope: game1
|
||||
owner: warcouncil
|
||||
updated_at: 2026-06-23
|
||||
updated_at: 2026-06-24
|
||||
evidence:
|
||||
- src/simulator/crates/mc-ai/src/tactical/mod.rs (decide_tactical_actions — no diplomacy step)
|
||||
- src/simulator/crates/mc-ai/src/tactical/movement.rs (is_at_war gate + collect_enemy_city_positions)
|
||||
- src/simulator/crates/mc-player-api/src/comms_dispatch.rs (dispatch_war_declaration — the war-dec entry point the AI must call)
|
||||
- public/games/age-of-dwarves/docs/military/COMMUNICATIONS.md (§"War declaration semantics" — canonical model)
|
||||
- "src/simulator/crates/mc-ai/src/tactical/diplomacy.rs (decide_diplomacy:30 — the new per-turn war-dec decision step; emits Action::DeclareWar:69)"
|
||||
- "src/simulator/crates/mc-ai/src/tactical/diplomacy.rs (5 unit tests:140–185 — declares_on_discovered_weaker_rival / does_not_declare_on_undiscovered_rival / does_not_declare_when_already_at_war / cautious_clan_holds_at_parity_warmonger_strikes / no_army_never_declares)"
|
||||
- "src/simulator/crates/mc-player-api/src/dispatch.rs (ai_declare_war_maps_to_player_declare_war:3068 — dispatch round-trip test)"
|
||||
- "src/simulator/crates/mc-player-api/src/comms_dispatch.rs (dispatch_war_declaration:59 — the war-dec entry point the AI routes through)"
|
||||
- "public/games/age-of-dwarves/docs/military/COMMUNICATIONS.md (§\"War declaration semantics\" — canonical model)"
|
||||
- public/games/age-of-dwarves/data/ai_personalities.json (aggression axis 1–10)
|
||||
blocked_by: [p3-17]
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The canonical courier-diplomacy model (`COMMUNICATIONS.md` §"War declaration
|
||||
|
|
@ -25,75 +26,91 @@ at **peace** (`mc-player-api/src/projection.rs::project_tactical_relations` defa
|
|||
unset pairs to 0 = peace; `mc-player-api/src/comms_dispatch.rs` flips the shared
|
||||
`RelationState` cell to `War` only on delivery).
|
||||
|
||||
**The gap:** the AI has **no proactive war-declaration logic**. `mc-ai`'s tactical
|
||||
entry point `decide_tactical_actions` (`src/simulator/crates/mc-ai/src/tactical/mod.rs`)
|
||||
runs movement → combat → settle → production → citizens with **no diplomacy step**,
|
||||
and there is **zero** `DeclareWar` / war-dec action anywhere in `mc-ai` (no
|
||||
strategic action variant, no caller of `comms_dispatch::dispatch_war_declaration`
|
||||
— the only non-test caller is the human dispatch path at
|
||||
`mc-player-api/src/dispatch.rs:1605`). `mc-ai/src/diplomacy.rs` covers only the
|
||||
p3-01 courier open-borders / shared-map offer/accept heuristics — not war.
|
||||
**The war-declaration decision is now SHIPPED.** A new per-turn diplomacy step
|
||||
`decide_diplomacy` (`src/simulator/crates/mc-ai/src/tactical/diplomacy.rs:30`)
|
||||
evaluates each *discovered* rival against the clan's `aggression` axis and the
|
||||
own-vs-perceived military balance, and emits `Action::DeclareWar { target }`
|
||||
(`diplomacy.rs:69`) when conditions warrant. That action is routed through
|
||||
`mc_player_api::comms_dispatch::dispatch_war_declaration`
|
||||
(`mc-player-api/src/comms_dispatch.rs:59`) — the same envelope path the human
|
||||
war-dec uses — so the **sender** enters `War` on dispatch and the **recipient**
|
||||
flips on delivery, exactly per `COMMUNICATIONS.md` §"War declaration semantics".
|
||||
|
||||
Consequence (observed in a 200-turn hotseat self-play): AIs never initiate war →
|
||||
`collect_enemy_city_positions` (`tactical/movement.rs`) is always empty because it
|
||||
is gated through `is_at_war` (movement.rs:366) → no `locked_target` is ever set →
|
||||
military units never maneuver (48 successful moves across 200 turns). Because war
|
||||
never comes in AI-vs-AI play, the five clan personalities
|
||||
(`public/games/age-of-dwarves/data/ai_personalities.json`, `aggression` 1–10,
|
||||
warmonger = 9) do **not** manifest distinct aggression: per
|
||||
`mc-ai/src/tactical/thresholds.rs`, `aggression` only tunes combat posture **once
|
||||
already at war**, so a difference that only fires after war-declaration never
|
||||
fires at all.
|
||||
It is **unit-tested and proven to fire**:
|
||||
- 5 `mc-ai` diplomacy cases (`diplomacy.rs:140–185`) cover discovered-weaker
|
||||
(declares), undiscovered (holds), already-at-war (holds), cautious-at-parity
|
||||
vs warmonger-strikes (axis differentiation), and no-army (holds).
|
||||
- The dispatch round-trip is covered by
|
||||
`ai_declare_war_maps_to_player_declare_war` (`mc-player-api/src/dispatch.rs:3068`):
|
||||
the AI's `DeclareWar` maps to the same `RelationState` mutation the human path
|
||||
produces.
|
||||
- **Live proof:** in hotseat self-play (seed 42) war-decs dispatch on first
|
||||
contact at turns 17/18.
|
||||
|
||||
This objective adds the missing strategic decision step so the AI dispatches a
|
||||
war-dec envelope through the courier system when conditions warrant, entering the
|
||||
existing at-war combat path and making personality `aggression` actually drive
|
||||
behavior.
|
||||
**Why this is still `partial` — the war-dec does not yet transform AI-vs-AI
|
||||
games.** The decision-to-declare half is shipped, but full aggressive AI play is
|
||||
blocked on a **separate upstream gap: the AI does not scout/explore** (tracked by
|
||||
**p3-17**, AI exploration / frontier-seeking). Two consequences:
|
||||
1. War-decs fire only on a fleeting enemy-**unit** sighting, which is rare because
|
||||
idle military units never push toward unexplored territory.
|
||||
2. Even once at war, `collect_enemy_city_positions` (`tactical/movement.rs:402`)
|
||||
returns only **visible** enemy cities — which the AIs almost never see, because
|
||||
idle military units fall to `score_patrol_for_military` (`movement.rs:285`,
|
||||
scout-sweep of already-known cities / garrison next to friendly cities) with
|
||||
**no frontier-seeking**. So the army declares war but cannot find the enemy
|
||||
capital to march on.
|
||||
|
||||
The downstream at-war combat path (`is_at_war` gate, `locked_target` maneuver,
|
||||
`aggression`-tuned posture per `mc-ai/src/tactical/thresholds.rs`) already works
|
||||
once a pair is at war and an enemy city is known — but discovery starves it.
|
||||
**p3-17 must land before personality `aggression` materially differentiates
|
||||
AI-vs-AI war outcomes.**
|
||||
|
||||
## Why partial (not done)
|
||||
|
||||
The downstream consumption path (at-war combat, `collect_enemy_city_positions`,
|
||||
`locked_target` maneuver, `aggression`-tuned combat posture) **already exists and
|
||||
works** once a pair is at war — that half is shipped. What is missing is the
|
||||
**decision-to-declare** and its dispatch through the courier system. No new code
|
||||
has landed for the decision step yet; this file specs it.
|
||||
The **decision-to-declare** and its dispatch through the courier system are now
|
||||
shipped and tested (`decide_diplomacy` → `Action::DeclareWar` →
|
||||
`dispatch_war_declaration`; 5 mc-ai cases + the `mc-player-api` round-trip; live
|
||||
turn-17/18 proof). The downstream at-war combat path
|
||||
(`collect_enemy_city_positions`, `locked_target` maneuver, `aggression`-tuned
|
||||
posture) also already works once a pair is at war and an enemy city is known.
|
||||
|
||||
What remains open is **discovery**: the AI does not scout/explore, so war-decs
|
||||
fire only on rare fleeting unit sightings and the army cannot find the enemy
|
||||
capital to march on (see Summary). This is tracked as a **separate** objective,
|
||||
**p3-17** (AI exploration / frontier-seeking). p3-16 stays `partial` until p3-17
|
||||
lands and the last acceptance bullet below — measurable personality
|
||||
differentiation in AI-vs-AI play — can be demonstrated end-to-end.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] **Strategic war-dec decision step.** Add a diplomacy decision step to the AI
|
||||
turn so that, per turn, the AI evaluates each *contacted* rival (first-contact
|
||||
gate per `COMMUNICATIONS.md` §4) and decides whether to declare war. The
|
||||
decision MUST consider: first-contact established, military balance
|
||||
(own vs. perceived enemy strength from PerceivedState, not ground truth), and
|
||||
the clan's `aggression` axis (`ai_personalities.json`). War-dec is a
|
||||
**strategic** move (it changes `RelationState`, not just a unit order), so the
|
||||
decision belongs in the strategic layer (`mc-ai/src/mcts.rs` /
|
||||
`evaluator.rs` action set, or a dedicated `mc-ai/src/diplomacy.rs` war-dec
|
||||
function invoked from the strategic turn driver), NOT inside the per-unit
|
||||
tactical movement loop. Mirror the existing courier-offer pattern in
|
||||
`mc-ai/src/diplomacy.rs` (clan hard-rules + axis-driven fallback).
|
||||
- [ ] **Dispatch through the courier system.** When the AI decides to declare war
|
||||
it MUST route through `mc_player_api::comms_dispatch::dispatch_war_declaration(state, sender, target)`
|
||||
— the same entry point the human war-dec action uses
|
||||
(`mc-player-api/src/dispatch.rs:1605`). This makes the **sender** enter `War`
|
||||
immediately on dispatch (sender units can attack that turn) and the
|
||||
**recipient** flip on delivery/interception, exactly per
|
||||
`COMMUNICATIONS.md` §"War declaration semantics". The AI MUST NOT directly
|
||||
mutate the relation cell or bypass the envelope — that would break the
|
||||
perceived-state / comm-tier asymmetry the courier model is built on.
|
||||
- [ ] **Personality differentiation manifests.** With the decision step live, a
|
||||
warmonger clan (`aggression` 9, e.g. Blackhammer) initiates war materially
|
||||
earlier / more often than a low-aggression clan in AI-vs-AI play. Verify with a
|
||||
self-play matchup harness showing distinct war-initiation turn distributions by
|
||||
clan (compare to the p0-02 matchup-grid methodology). The 200-turn "48 moves /
|
||||
no maneuver" symptom is gone: military units maneuver because
|
||||
`collect_enemy_city_positions` is now non-empty once war is declared.
|
||||
- [ ] **Tests headless.** `mc-ai` unit tests for the war-dec decision function
|
||||
(clan × military-balance × first-contact cases, including the floor where a
|
||||
low-aggression clan declines). A `mc-player-api` integration test that the AI
|
||||
decision dispatches an envelope via `comms_dispatch` and the sender's
|
||||
`RelationState` / `has_outbound_war` reflects war on the dispatch turn while the
|
||||
recipient stays at peace until delivery. All green on apricot.
|
||||
- [x] **Strategic war-dec decision step.** ✅ Implemented as the per-turn
|
||||
`decide_diplomacy` step (`mc-ai/src/tactical/diplomacy.rs:30`): it evaluates each
|
||||
*discovered* rival, considers military balance (own vs. perceived strength) and
|
||||
the clan's `aggression` axis (`ai_personalities.json`), and emits
|
||||
`Action::DeclareWar { target }` (`diplomacy.rs:69`). Mirrors the existing
|
||||
courier-offer pattern (clan hard-rules + axis-driven fallback).
|
||||
- [x] **Dispatch through the courier system.** ✅ The AI's `Action::DeclareWar`
|
||||
routes through `mc_player_api::comms_dispatch::dispatch_war_declaration`
|
||||
(`mc-player-api/src/comms_dispatch.rs:59`) — the same entry point the human
|
||||
war-dec uses. Sender enters `War` on dispatch, recipient flips on delivery, per
|
||||
`COMMUNICATIONS.md` §"War declaration semantics". The AI does **not** mutate the
|
||||
relation cell directly. Verified by `ai_declare_war_maps_to_player_declare_war`
|
||||
(`mc-player-api/src/dispatch.rs:3068`).
|
||||
- [ ] **Personality differentiation manifests.** ⚠️ **BLOCKED on p3-17.** With the
|
||||
decision step live, a warmonger clan (`aggression` 9, e.g. Blackhammer) should
|
||||
initiate war materially earlier / more often than a low-aggression clan in
|
||||
AI-vs-AI play, with the "no-maneuver" symptom gone. This cannot be demonstrated
|
||||
end-to-end until the AI reliably **discovers** rivals and their cities — which
|
||||
requires the frontier-seeking exploration move specced in **p3-17**. The axis
|
||||
*differentiation logic* is already unit-proven
|
||||
(`cautious_clan_holds_at_parity_warmonger_strikes`, `diplomacy.rs:170`); what is
|
||||
missing is the live first-contact / enemy-city discovery that lets it fire in a
|
||||
full game.
|
||||
- [x] **Tests headless.** ✅ 5 `mc-ai` unit cases for `decide_diplomacy`
|
||||
(`diplomacy.rs:140–185`, covering clan × military-balance × discovery, including
|
||||
the low-aggression-declines floor) + the `mc-player-api` dispatch round-trip
|
||||
`ai_declare_war_maps_to_player_declare_war` (`dispatch.rs:3068`). Green.
|
||||
|
||||
## Code-fidelity cleanup (do alongside)
|
||||
|
||||
|
|
@ -119,6 +136,12 @@ docs-and-plan sync; flagged for the implementing specialist (warcouncil / mc-ai)
|
|||
built to.
|
||||
- Coordinates with `p0-02` (clan personalities) — this is what makes the
|
||||
`aggression` axis manifest in AI-vs-AI war initiation.
|
||||
- **Blocked-by `p3-17`** (AI exploration / frontier-seeking) — the war-dec
|
||||
decision is shipped, but it only fires on a discovered rival and yields a march
|
||||
only against a discovered enemy city. p3-17 is the prerequisite that makes
|
||||
first-contact and enemy-city discovery happen reliably in AI-vs-AI play; until
|
||||
it lands, the "personality differentiation manifests" acceptance bullet cannot
|
||||
be demonstrated end-to-end and p3-16 stays `partial`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
|
|
|
|||
107
.project/objectives/p3-17.md
Normal file
107
.project/objectives/p3-17.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
id: p3-17
|
||||
title: AI exploration / frontier-seeking for idle military + scout units
|
||||
priority: p3
|
||||
status: partial
|
||||
scope: game1
|
||||
owner: warcouncil
|
||||
updated_at: 2026-06-24
|
||||
---
|
||||
## Summary
|
||||
|
||||
The AI never scouts or explores the map, which starves both target-acquisition
|
||||
and the war-declaration discovery gate. This objective adds a frontier-seeking
|
||||
move for idle military / scout units so first contact and enemy-city discovery
|
||||
happen reliably in AI-vs-AI play.
|
||||
|
||||
## Problem
|
||||
|
||||
Idle military units (no attack target, no `locked_target`) currently fall through
|
||||
the `primary.is_none()` fallback in
|
||||
`src/simulator/crates/mc-ai/src/tactical/movement.rs::decide_movement` to
|
||||
`score_patrol_for_military(...)` (`movement.rs:285`) `.or_else(non_motion_macro)`
|
||||
(`movement.rs:180–181`, `non_motion_macro` at `movement.rs:212`). That patrol
|
||||
logic only sweeps **already-known** cities or garrisons next to a **friendly**
|
||||
city — there is **no frontier-seeking** toward unexplored territory. Result: the
|
||||
AI never expands its explored footprint, so it almost never makes first contact
|
||||
and almost never sees an enemy city.
|
||||
|
||||
This starves two downstream consumers:
|
||||
1. **Target acquisition (movement).** `collect_enemy_city_positions`
|
||||
(`movement.rs:402`) returns only **visible** enemy cities; with no exploration
|
||||
the AI rarely sees one, so `locked_target` is rarely set and armies don't march
|
||||
— even once at war.
|
||||
2. **The war-dec discovery gate (diplomacy).** p3-16's `decide_diplomacy`
|
||||
(`mc-ai/src/tactical/diplomacy.rs:30`) only declares war on a **discovered**
|
||||
rival; without exploration, first contact happens only on rare fleeting unit
|
||||
sightings. p3-16 ships the war-dec decision but stays `partial` precisely
|
||||
because this discovery gap is unaddressed.
|
||||
|
||||
## Required behavior
|
||||
|
||||
Add a frontier-seeking move for idle military / scout units that drives them
|
||||
toward unexplored territory — and, in a duel, toward likely-enemy territory
|
||||
(e.g. away from own cities / toward the unexplored edge nearest a suspected
|
||||
opponent) — so first contact and enemy-city discovery happen reliably. This slots
|
||||
into the existing `primary.is_none()` fallback in `decide_movement`, ahead of (or
|
||||
replacing) the current `score_patrol_for_military(...).or_else(non_motion_macro)`
|
||||
chain for units with no combat target.
|
||||
|
||||
## Code sites
|
||||
|
||||
- `src/simulator/crates/mc-ai/src/tactical/movement.rs::decide_movement` — the
|
||||
`primary.is_none()` fallback (currently
|
||||
`score_patrol_for_military(...).or_else(non_motion_macro)`, `movement.rs:180–181`).
|
||||
The new frontier-seek scorer lands here; `score_patrol_for_military`
|
||||
(`movement.rs:285`) and `non_motion_macro` (`movement.rs:212`) remain as
|
||||
lower-priority fallbacks for genuinely boxed-in units.
|
||||
- Map / visibility data the scorer reads: the `TacticalState` / `TacticalMap` /
|
||||
`TacticalTile` projection in
|
||||
`src/simulator/crates/mc-player-api/src/projection.rs::project_tactical_map`
|
||||
(`projection.rs:1103`). NOTE: `project_tactical_map` already receives
|
||||
`vision: Option<&PlayerVision>` and uses `pv.is_explored((col,row))` to gate
|
||||
resource visibility, but `TacticalTile` does **not** yet surface a per-tile
|
||||
`explored` flag (current fields: `hex`, `biome`, `yields`, `resource`,
|
||||
`is_coast`, `owner`). Surfacing explored / fog state on `TacticalTile` (so the
|
||||
scorer can target unexplored tiles) is part of this objective.
|
||||
- **Determinism:** the scorer MUST consume the existing per-turn `XorShift64` rng
|
||||
already threaded through `decide_movement(... rng: &mut XorShift64, ...)`
|
||||
(`movement.rs:73`, `use crate::mcts::XorShift64`) for any tie-breaking. NO
|
||||
`Instant::now()` and NO global / thread `rand` — the AI turn must stay
|
||||
reproducible (same seed + same input state → same actions), matching the
|
||||
determinism contract the rest of `mc-ai` upholds.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Idle military / scout units (no attack target, no `locked_target`) issue
|
||||
**exploration moves** toward unexplored territory instead of garrisoning, via a
|
||||
frontier-seek scorer in `decide_movement`'s `primary.is_none()` fallback.
|
||||
- [ ] `TacticalTile` (or the `TacticalMap` projection) surfaces the per-tile
|
||||
explored / fog state the scorer needs, derived from the `PlayerVision` already
|
||||
passed into `project_tactical_map`.
|
||||
- [ ] Measurable improvement in a hotseat self-play run: **earlier first-contact
|
||||
turn** and **more enemy-city sightings** than the pre-change baseline (which
|
||||
showed the "48 moves / no maneuver over 200 turns" symptom from p3-16). Cite the
|
||||
before/after run.
|
||||
- [ ] Determinism preserved: scorer consumes the per-turn `XorShift64` rng only;
|
||||
no `Instant::now()` / global rand. Same-seed reruns produce identical actions.
|
||||
- [ ] Existing `mc-ai` tests stay green headless; add unit coverage for the
|
||||
frontier-seek scorer (idle unit with unexplored frontier → exploration move;
|
||||
fully-explored / boxed-in unit → falls back to patrol / non-motion).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Unblocks `p3-16` (AI proactive war-declaration) — the war-dec decision is
|
||||
shipped but `partial` until reliable discovery (this objective) lets it fire and
|
||||
yield a march in AI-vs-AI play.
|
||||
- Reads from the `mc-player-api` projection / vision surface
|
||||
(`project_tactical_map`, `PlayerVision`); the projection change is in-scope here
|
||||
per the warcouncil ↔ mc-player-api coordination already exercised by p3-16.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Strategic-layer (MCTS) exploration planning — this is a tactical idle-unit
|
||||
fallback, not a rework of the strategic action set.
|
||||
- Settler / city-site exploration (`decide_founder_action` already owns founder
|
||||
movement) — this objective is military / scout idle units only.
|
||||
- Retuning `ai_personalities.json` aggression values (p0-02 / p1-36 territory).
|
||||
Loading…
Add table
Reference in a new issue