diff --git a/src/simulator/crates/mc-turn/src/processor.rs b/src/simulator/crates/mc-turn/src/processor.rs index 5d167220..0fa4e7c2 100644 --- a/src/simulator/crates/mc-turn/src/processor.rs +++ b/src/simulator/crates/mc-turn/src/processor.rs @@ -35,11 +35,11 @@ use std::collections::HashMap; /// Default seek range for non-rusher profiles. Units will move toward enemies /// within this many hexes when no lair target is closer. -const DEFAULT_SEEK_RANGE: i32 = 5; +const DEFAULT_SEEK_RANGE: i32 = 40; /// Extended seek range for rusher profiles. Rushers aggressively seek out /// enemy units and cities from farther away. -const RUSHER_SEEK_RANGE: i32 = 8; +const RUSHER_SEEK_RANGE: i32 = 80; // ── Config ────────────────────────────────────────────────────────────────── @@ -811,10 +811,10 @@ impl TurnProcessor { for (ui, uc, ur, a_hp, a_atk, a_def) in &attacker_snaps { if killed.iter().any(|&(p, u)| p == pi && u == *ui) { continue; } - // Check all other players for a defender on this tile. + // Check all other players for a defender on or adjacent to this tile. for di in 0..n { if di == pi { continue; } - if let Some(def_idx) = find_enemy_at(&state.players[di].units, *uc, *ur) { + if let Some(def_idx) = find_enemy_nearby(&state.players[di].units, *uc, *ur) { if killed.iter().any(|&(p, u)| p == di && u == def_idx) { continue; } let defender = &state.players[di].units[def_idx]; @@ -1060,6 +1060,22 @@ fn find_enemy_at(units: &[MapUnit], col: i32, row: i32) -> Option { units.iter().position(|u| u.col == col && u.row == row) } +/// Find the nearest enemy unit on or adjacent to a tile (within 1 hex). +/// Same-tile matches are preferred; if none, the closest adjacent enemy is +/// returned. This implements the Civ5 "melee attack adjacent" mechanic. +fn find_enemy_nearby(units: &[MapUnit], col: i32, row: i32) -> Option { + // Prefer same-tile first. + if let Some(idx) = find_enemy_at(units, col, row) { + return Some(idx); + } + // Check adjacent tiles (distance <= 1). + units.iter() + .enumerate() + .filter(|(_, u)| hex_distance(u.col, u.row, col, row) <= 1) + .min_by_key(|(_, u)| hex_distance(u.col, u.row, col, row)) + .map(|(i, _)| i) +} + // ── Terrain helpers ───────────────────────────────────────────────────── /// Movement cost for a tile based on its biome. Mountains are impassable