diff --git a/src/game/engine/scenes/tests/auto_play.gd b/src/game/engine/scenes/tests/auto_play.gd index 22f355e9..281691c6 100644 --- a/src/game/engine/scenes/tests/auto_play.gd +++ b/src/game/engine/scenes/tests/auto_play.gd @@ -829,7 +829,7 @@ func _apply_difficulty_starting_bonuses() -> void: return var unit_manager: Node = get_node_or_null("/root/UnitManager") for p: Variant in GameState.players: - if p is PlayerScript and not p.is_human: + if p is Player and not p.is_human: if gold_bonus > 0: p.gold += gold_bonus if extra_units > 0 and unit_manager != null: diff --git a/src/simulator/api-gdext/src/ai.rs b/src/simulator/api-gdext/src/ai.rs index ea57649d..062f98dc 100644 --- a/src/simulator/api-gdext/src/ai.rs +++ b/src/simulator/api-gdext/src/ai.rs @@ -143,8 +143,9 @@ impl GdMcTreeController { }; let processor = TurnProcessor::new(300); - let snapshot = McSnapshot::from_game_state(&state, &processor); + let mut snapshot = McSnapshot::from_game_state(&state, &processor); let pi = player_index.max(0) as usize; + snapshot.active_player = pi as u8; let base_seed = seed as u64; let depth = self.rollout_depth; @@ -239,8 +240,9 @@ impl GdMcTreeController { }; let processor = TurnProcessor::new(300); - let snapshot = McSnapshot::from_game_state(&state, &processor); + let mut snapshot = McSnapshot::from_game_state(&state, &processor); let pi = player_index.max(0) as usize; + snapshot.active_player = pi as u8; let base_seed = seed as u64; let depth = self.rollout_depth; @@ -526,6 +528,7 @@ mod tests { ], config: LairCombatConfig::default(), victory_city_count: 30, + active_player: 0, } } diff --git a/src/simulator/crates/mc-turn/src/snapshot.rs b/src/simulator/crates/mc-turn/src/snapshot.rs index 5d326a14..c5ac8d05 100644 --- a/src/simulator/crates/mc-turn/src/snapshot.rs +++ b/src/simulator/crates/mc-turn/src/snapshot.rs @@ -191,6 +191,27 @@ impl TreeState for McSnapshot { fn is_terminal(&self) -> bool { self.is_terminal() } + + /// Personality-weighted action prior for PUCT selection (p0-38). + /// + /// Maps `McAction` variants to the `active_player`'s `ScoringWeights`: + /// - `SpawnUnit` → `military_base` (aggressive clans prefer early armies) + /// - `FoundCity` → `expansion` (expansionist clans prefer new cities) + /// - `Idle` → 1.0 baseline (everyone considers doing nothing) + /// + /// Returns unnormalised weights — `Tree::best_puct_child` normalises via + /// the PUCT formula rather than requiring pre-normalised priors. + fn action_prior(&self, action: &McAction) -> f32 { + let Some(p) = self.players.get(self.active_player as usize) else { + return 1.0; + }; + let w = &p.scoring_weights; + match action { + McAction::SpawnUnit => w.military_base.max(0.1), + McAction::FoundCity => w.expansion_base.max(0.1), + McAction::Idle => 1.0, + } + } } // ── Tests ────────────────────────────────────────────────────────────────────