feat(@projects/@magic-civilization): ✨ add iron-ore density strategy objective
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
88080e597e
commit
c81744b01e
2 changed files with 67 additions and 7 deletions
59
.project/objectives/p0-40-iron-ore-resource-density.md
Normal file
59
.project/objectives/p0-40-iron-ore-resource-density.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
id: p0-40
|
||||
title: Iron-ore strategic resource density — unblock tier 3-6 unit chain
|
||||
priority: p0
|
||||
status: stub
|
||||
scope: game1
|
||||
owner: shipwright
|
||||
updated_at: 2026-04-18
|
||||
evidence:
|
||||
- public/games/age-of-dwarves/data/resources.json
|
||||
- public/games/age-of-dwarves/data/units/cavalry.json
|
||||
- public/games/age-of-dwarves/data/units/ironwarden.json
|
||||
- .local/iter/apricot-20260418_194533/
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Warcouncil filed 2026-04-18 after p0-39 (AI tier-progression) unlocked tier-2 units. Post-p0-39 smoke batch (`.local/iter/apricot-20260418_194533/`) shows pikemen (tier 2, tech=bronze_working, no resource) building reliably (107 in seed 2, 83 in seed 3), but no tier 3+ unit (cavalry, ironwarden, forge_titan, mithril_vanguard) ever gets built.
|
||||
|
||||
Root cause is NOT tactical AI: the p0-39 `_best_melee_for_player` helper correctly checks `requires_resource` and filters cavalry (and thus downstream tier 4+ units that also gate on iron_ore) when the player owns no iron_ore tile. Empirically, 10/10 seeds in the smoke batch have player 0 with zero iron_ore ownership at T300.
|
||||
|
||||
Iron ore density in current map gen is too low for tier 3+ unit emergence. Fix is either (a) bias map gen toward iron_ore resource placement OR (b) drop the `requires_resource` gate on tier 3 units that previously used it as a "forbidden chokepoint" balance lever.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- ✗ **Iron-ore frequency audit** against `public/games/age-of-dwarves/data/resources.json` — confirm current `frequency` / `placement` rules and document expected tiles-per-player at `duel` (2p) / `small` (4p) map sizes.
|
||||
- ✗ **Map-gen tune** — raise iron_ore availability to target: **median player owns ≥1 iron_ore tile by turn 50** on the `duel` map size. Implementation in `tools/` map-gen scripts OR `mc-map` resource placement.
|
||||
- ✗ **Smoke batch** (10 seeds T300 smoke, post-tune) shows `median_peak_unit_tier ≥ 3` across seeds. Each player has reached cavalry or higher before game end in majority of seeds.
|
||||
- ✗ **No regression** on p0-39 gates: pikemen still dominant in early-game builds when bronze_working lands but iron_working doesn't.
|
||||
- ✗ **p0-01 re-test** after p0-40 lands — measure whether tier_peak median rises past 4.0 (current post-p0-37+p0-39 baseline).
|
||||
|
||||
## Why P0
|
||||
|
||||
Without iron_ore availability, tier 3-6 units are inaccessible by design. Every p0-01 state-at-end gate (`peak_unit_tier ≥ 6`, `tier_peak_gap ≤ 2`, `wonder_count ≥ 1 per player in ≥5/10 games`) is structurally gated by this. p0-22 ultimate_stress (5-clan huge map) likewise — clans can't diverge on tier 3+ if nobody can build them. p0-02 era-divergence (production vs expansion clan pairs) similarly gated.
|
||||
|
||||
## Fix direction (non-prescriptive — shipwright picks)
|
||||
|
||||
1. **Resource frequency bump**: raise iron_ore placement weight in `resources.json` or map-gen so expected player ownership hits ≥1 tile by T50.
|
||||
2. **Clustered placement**: prefer spawning iron_ore near starting positions to guarantee at least one per player, regardless of map size.
|
||||
3. **Gate relaxation**: drop `requires_resource: iron_ore` from cavalry/ironwarden (tier 3+ still tech-gated). Loses some strategic-resource flavor but unblocks the ladder immediately.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Re-adding tier-3+ units to `ai_personalities.json` clan preferences — existing p0-37 axis-driven selection handles this once the units are buildable.
|
||||
- `horses` / other strategic resources — scope is iron_ore for the tier-3-6 chain only. Other resources can be audited if similar patterns emerge.
|
||||
|
||||
## Depends on
|
||||
|
||||
- None (this is the blocker for others).
|
||||
|
||||
## Blocks
|
||||
|
||||
- **p0-01** MCTS wiring — `peak_unit_tier ≥ 6 in ≥ 7/10 games` gate.
|
||||
- **p0-22** Ultimate AI stress test — matchup-grid + huge-map median-turn gates need post-tier-1 armies.
|
||||
- **p0-02** era-divergence gate under reframed p0-01 framework.
|
||||
|
||||
## Related
|
||||
|
||||
- **p0-39** AI tier-progression (done) — lifted tier-1 ceiling to tier-2; this objective lifts to tier-3+.
|
||||
|
|
@ -75,10 +75,11 @@ pub fn derived_defense(axes: &BTreeMap<String, i32>) -> i32 {
|
|||
/// to capital-assault commitment. Aggressive clans commit earlier; cautious
|
||||
/// clans wait for real superiority.
|
||||
///
|
||||
/// Range: `axis=1` → 1.60 (very cautious), `axis=5` → 1.25 (baseline,
|
||||
/// matches historical hardcoded value), `axis=10` → 1.10 (rush-happy).
|
||||
/// Range: `axis=1` → 1.80 (very cautious), `axis=5` → 1.50 (post-p0-37+39
|
||||
/// tempo baseline), `axis=10` → 1.15 (rush-happy). Baseline raised 2026-04-18
|
||||
/// from 1.25 so games reach T250+ — tier-3+ tech chains need the runway.
|
||||
pub fn dominance_factor(axes: &BTreeMap<String, i32>) -> f32 {
|
||||
lerp_axis(axis(axes, "aggression"), 1.60, 1.25, 1.10)
|
||||
lerp_axis(axis(axes, "aggression"), 1.80, 1.50, 1.15)
|
||||
}
|
||||
|
||||
/// Hex radius within which a unit bypasses stray-unit chasing to march on
|
||||
|
|
@ -190,7 +191,7 @@ mod tests {
|
|||
#[test]
|
||||
fn dominance_factor_baseline_matches_historical() {
|
||||
let a = axes(&[("aggression", 5)]);
|
||||
assert!((dominance_factor(&a) - 1.25).abs() < 1e-6);
|
||||
assert!((dominance_factor(&a) - 1.50).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -241,7 +242,7 @@ mod tests {
|
|||
#[test]
|
||||
fn empty_axes_match_historical_baseline() {
|
||||
let empty: BTreeMap<String, i32> = BTreeMap::new();
|
||||
assert!((dominance_factor(&empty) - 1.25).abs() < 1e-6);
|
||||
assert!((dominance_factor(&empty) - 1.50).abs() < 1e-6);
|
||||
assert_eq!(capital_approach_hex(&empty), 16);
|
||||
assert!((retreat_hp_fraction(&empty) - 0.40).abs() < 1e-6);
|
||||
assert_eq!(defensive_chase_range(&empty), 12);
|
||||
|
|
@ -303,8 +304,8 @@ mod tests {
|
|||
let insane_high = axes(&[("aggression", 999)]);
|
||||
let df_low = dominance_factor(&insane_low);
|
||||
let df_high = dominance_factor(&insane_high);
|
||||
assert!(df_low >= 1.09 && df_low <= 1.61);
|
||||
assert!(df_high >= 1.09 && df_high <= 1.61);
|
||||
assert!(df_low >= 1.14 && df_low <= 1.81);
|
||||
assert!(df_high >= 1.14 && df_high <= 1.81);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue