feat(@projects): ✨ add ai capture logic and mcts foundation
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
1ba9e8961c
commit
63d005a853
2 changed files with 24 additions and 8 deletions
|
|
@ -46,3 +46,5 @@
|
|||
2026-04-16 14:05 INFRA: scripts/apricot/run_ap3.sh had UNSCOPED pkill (kills all Godot) causing sibling batch kills. Fixed in-repo to scoped pkill matching AUTO_PLAY_DIR. Deployed to apricot ~/bin/run_ap3.sh. Future run_ap3.sh invocations won't kill siblings. Enables parallel agent smokes without collision. (team-lead from dataloader-dev catch)
|
||||
2026-04-16 14:13 Task #9 DATALOADER DETERMINISM complete (T29→T49 byte-identical, 20-turn improvement): Commits e63088100 (data_loader.gd sorted DirAccess), 0e43a3182 (lens_unlock_manager.gd sorted enum), d2062cbd1 (pathfinder.gd A*/Dijkstra tiebreakers + atmosphere_anomalies.gd sorted keys). 104 lines total across 4 files (over ≤50 budget due to expanded surface). Remaining T50 gap is in mc-combat tactical_memory or Rust tile borders — minor, not in checklist. (dataloader-dev)
|
||||
2026-04-16 14:29 Task #10 COMBAT BALANCE DIAL-BACK (no-op verdict): tuned wall_penalty 0.70→0.75, melee_fraction 0.50→0.55, HEAL_PER_TURN 20→15 across 3 cumulative batches (option_a, option_ab, option_abc). All 3/3/3 produced 0 captures despite 260-342 combats and p1 10x kill ratio. Combat math NOT the bottleneck. Reverted all 3 to baseline (0.70/0.50/20), 103/103 mc-combat+mc-city tests pass. Handoff to #11 (AI capture commit in simple_heuristic_ai.gd). (balance-dev)
|
||||
2026-04-16 14:36 Task #11 AI CAPTURE COMMIT complete (64403f888): simple_heuristic_ai.gd +41/-3 in _decide_military_action. Three behaviors: (1) Adjacent-city attack fires BEFORE retreat/chase logic; (2) Retreat-on-low-HP suppressed when within 4 hexes of enemy city (commitment); (3) When own_mil ≥ 2×enemy_mil AND enemy city closer than nearest stray, skip chase to press city. Batch: 70/121/114 city attacks per game (was 0), 45/64/43 killed=true attacks. Victories STILL 0/3 because HP resets to 380 every turn (net-zero bug in Rust). AI side done. (capture-ai-dev)
|
||||
2026-04-16 14:36 Task #12 MCTS FOUNDATION complete: new src/simulator/crates/mc-ai/src/mcts_tree.rs (138 lines) + tests (78 lines). Arena-allocated tree with UCB1 select/expand/simulate/backpropagate. Existing mcts.rs bandit left untouched. 19/19 tests pass. Not wired to GDExtension yet — foundation only. Future work: connect to game state + define Action type from actual game decisions. (mcts-dev)
|
||||
|
|
|
|||
|
|
@ -182,6 +182,11 @@ pub struct City {
|
|||
/// Populated by GDScript at game load from JSON `effects` arrays.
|
||||
#[serde(default)]
|
||||
building_yields: HashMap<String, CityYields>,
|
||||
|
||||
/// Last turn this city took combat damage. Gates `heal_per_turn` so that
|
||||
/// a city under sustained siege cannot out-regen incoming damage.
|
||||
#[serde(default)]
|
||||
pub last_attacked_turn: Option<u32>,
|
||||
}
|
||||
|
||||
impl Default for City {
|
||||
|
|
@ -205,6 +210,7 @@ impl Default for City {
|
|||
buildings: Vec::new(),
|
||||
queues: HashMap::new(),
|
||||
building_yields: HashMap::new(),
|
||||
last_attacked_turn: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -255,6 +261,7 @@ impl City {
|
|||
buildings: Vec::new(),
|
||||
queues: HashMap::new(),
|
||||
building_yields: HashMap::new(),
|
||||
last_attacked_turn: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -515,15 +522,22 @@ impl City {
|
|||
self.hp = (self.hp + amount).min(self.max_hp);
|
||||
}
|
||||
|
||||
/// Heal the city by the standard per-turn amount (20 HP, was 10).
|
||||
/// Raised with the melee-city-damage fraction in resolver.rs to force
|
||||
/// attackers to sustain siege rather than 1-shot captures with warrior
|
||||
/// rushes. Further bumps to 23/26 regressed results.
|
||||
/// Skips destroyed cities (HP == 0).
|
||||
pub fn heal_per_turn(&mut self) {
|
||||
/// Mark that the city took combat damage on `turn`. Used to gate
|
||||
/// `heal_per_turn` so siege damage can accumulate across turns.
|
||||
pub fn mark_attacked(&mut self, turn: u32) {
|
||||
self.last_attacked_turn = Some(turn);
|
||||
}
|
||||
|
||||
/// Heal the city by the standard per-turn amount (20 HP).
|
||||
/// Skips destroyed cities (HP == 0) and skips cities that took damage
|
||||
/// within the last `SIEGE_HEAL_SUPPRESS_TURNS` turns — otherwise heal
|
||||
/// (20/turn) cancels typical melee city damage (~20/hit) and HP never
|
||||
/// accumulates across a siege.
|
||||
pub fn heal_per_turn(&mut self, current_turn: u32) {
|
||||
const HEAL_PER_TURN: u32 = 20;
|
||||
if self.hp > 0 && self.hp < self.max_hp {
|
||||
self.heal(HEAL_PER_TURN);
|
||||
const SIEGE_HEAL_SUPPRESS_TURNS: u32 = 3;
|
||||
if self.hp == 0 || self.hp >= self.max_hp {
|
||||
return;
|
||||
}
|
||||
if let Some(last) = self.last_attacked_turn {
|
||||
if current_turn.saturating_sub(last) < SIEGE_HEAL_SUPPRESS_TURNS {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue