refactor(p2-65): 🏗️ Phase 4 C2 — delete mc-turn GameState shim + re-export (bullet 6 → 0)

Complete the consumer migration off mc-turn's state-shape pass-through:
- mc-turn/src/lib.rs: `pub mod game_state;` → `pub(crate) use mc_state::game_state;`
  (internals keep `crate::game_state::…`; external pass-through forbidden);
  delete the `pub use game_state::{GameState, …}` crate-root re-export.
- Delete the mc-turn/src/game_state.rs re-export shim file.
- Split every remaining `use mc_turn::{… state types …}` brace import (single-
  AND multi-line, incl. nested `game_state::{…}`) into
  `use mc_state::game_state::{…}` + `use mc_turn::{… logic types …}` across
  mc-turn integration tests, mc-sim (lib + 4 bins incl. solo_dominion),
  mc-player-api tests, and tests/integration. Retarget mc-turn-internal bare
  `crate::{GameState,…}` / `crate::MoveRequest` to `crate::game_state::…`.
- Sweep inline `mc_turn::MapUnit::new(…)` / `mc_turn::PlayerState {…}` call
  sites in api-gdext + mc-player-api/dispatch to `mc_state::game_state::…`;
  fix stale doc-comment refs in mc-core/mc-ai.
- Add `mc-state` path dep to tests/integration.

Gates (apricot, shared target):
- brief grep `mc_turn::game_state|mc_turn::GameState` → 0;
  completeness brace grep (single+multiline) → 0.
- cargo test --workspace --no-run exit 0.
- serde_roundtrip 6/6; full_turn_golden 3/3 (save-format byte-identical).
- mc-turn lib 234/234 (1 ignored, pre-existing five_players_overflow);
  mc-ai 268/268; mc-player-api 126/126; mc-state 12/12.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
autocommit 2026-06-04 19:47:55 -07:00
parent cea53e1ee4
commit efbfa6ae3f
35 changed files with 73 additions and 88 deletions

View file

@ -1813,6 +1813,7 @@ dependencies = [
"mc-core",
"mc-culture",
"mc-happiness",
"mc-state",
"mc-turn",
]

View file

@ -3592,11 +3592,11 @@ impl GdGameState {
// (hp/attack/defense — these are warrior-flavored fixtures, not
// catalog-driven yet) are applied after construction; promoting
// those to the catalog is tracked separately.
let mut units: Vec<mc_turn::MapUnit> = (0..3)
let mut units: Vec<mc_state::game_state::MapUnit> = (0..3)
.map(|i| {
let unit_u32 = self.inner.next_unit_id;
self.inner.next_unit_id = self.inner.next_unit_id.saturating_add(1);
let mut u = mc_turn::MapUnit::new(
let mut u = mc_state::game_state::MapUnit::new(
"dwarf_warrior",
city_col + i,
city_row,
@ -3620,7 +3620,7 @@ impl GdGameState {
// default-constructed and stuck at `base_moves = 0`).
let founder_id = self.inner.next_unit_id;
self.inner.next_unit_id = self.inner.next_unit_id.saturating_add(1);
let mut founder = mc_turn::MapUnit::new(
let mut founder = mc_state::game_state::MapUnit::new(
"dwarf_founder",
city_col,
city_row,
@ -3633,7 +3633,7 @@ impl GdGameState {
founder.auto_join = false;
units.push(founder);
self.inner.players.push(mc_turn::PlayerState {
self.inner.players.push(mc_state::game_state::PlayerState {
player_index: pi,
gold: 60,
cities: vec![mc_city::CityState::starter()],
@ -4096,7 +4096,7 @@ impl GdGameState {
}
}
self.inner.players.push(mc_turn::PlayerState {
self.inner.players.push(mc_state::game_state::PlayerState {
player_index: pi,
gold: 0,
cities: Vec::new(),
@ -4170,7 +4170,7 @@ impl GdGameState {
);
continue;
};
ingested.push(mc_turn::MapUnit {
ingested.push(mc_state::game_state::MapUnit {
col: col as i32,
row: row as i32,
hp: dict.get("hp").and_then(|v| v.try_to::<i64>().ok()).unwrap_or(60) as i32,
@ -4417,7 +4417,7 @@ impl GdGameState {
target_col: i64,
target_row: i64,
) {
use mc_turn::PillageRequest;
use mc_state::game_state::PillageRequest;
self.inner.pending_pillage_requests.push(PillageRequest {
player_index: player_index as u8,
unit_index: unit_index as usize,
@ -4483,7 +4483,7 @@ impl GdGameState {
target_row: i64,
indirect_fire: bool,
) {
use mc_turn::BombardRequest;
use mc_state::game_state::BombardRequest;
self.inner.pending_bombard_requests.push(BombardRequest {
attacker_player: attacker_player as u8,
attacker_unit: attacker_unit as usize,
@ -4504,7 +4504,7 @@ impl GdGameState {
target_col: i64,
target_row: i64,
) {
use mc_turn::VolleyRequest;
use mc_state::game_state::VolleyRequest;
self.inner.pending_volley_requests.push(VolleyRequest {
attacker_player: attacker_player as u8,
attacker_unit: attacker_unit as usize,
@ -4524,7 +4524,7 @@ impl GdGameState {
target_col: i64,
target_row: i64,
) {
use mc_turn::ChargeRequest;
use mc_state::game_state::ChargeRequest;
self.inner.pending_charge_requests.push(ChargeRequest {
attacker_player: attacker_player as u8,
attacker_unit: attacker_unit as usize,

View file

@ -8,7 +8,7 @@
//!
//! # Where it lives (Rail 1 — Rust source of truth)
//!
//! A [`TacticalMemory`] is carried on `mc_turn::PlayerState` (one per player
//! A [`TacticalMemory`] is carried on `mc_state::game_state::PlayerState` (one per player
//! slot). `mc_player_api::dispatch::drive_ai_slot` borrows it `&mut` and
//! threads it into [`super::decide_tactical_actions`] →
//! [`super::movement::decide_movement`], which mutates it in place. There is
@ -35,7 +35,7 @@
//! invisible at the game-decision scale.
// p2-65 Phase 0c — `TacticalMemory` is pure data carried on
// `mc_turn::PlayerState`, so it relocated to `mc-core` (which the future
// `mc_state::game_state::PlayerState`, so it relocated to `mc-core` (which the future
// `mc-state` crate can depend on without pulling `mc-ai`). Re-exported here so
// every `mc_ai::tactical::TacticalMemory` reference keeps compiling unchanged;
// the impl + unit tests now live in `mc_core::tactical_types`.

View file

@ -182,7 +182,7 @@ fn default_promotion_weight() -> f32 {
/// }
/// ```
// p2-65 Phase 0c — `BuildingPriors` relocated to `mc-core` (pure data carried on
// `mc_turn::PlayerState`); re-exported so `crate::tactical::state::BuildingPriors`
// `mc_state::game_state::PlayerState`); re-exported so `crate::tactical::state::BuildingPriors`
// keeps resolving.
pub use mc_core::tactical_types::BuildingPriors;

View file

@ -2,7 +2,7 @@
//!
//! Per Rail 3 (GDScript is presentation only) and the p2-72a Wall-2 decision,
//! UI-only player fields — display name, race id, gender preset, banner colour,
//! human-vs-AI flag — must not pollute `mc_turn::PlayerState` (simulation state).
//! human-vs-AI flag — must not pollute `mc_state::game_state::PlayerState` (simulation state).
//! Instead, they live in this side-table struct that the save envelope carries
//! alongside `GameState`.
//!

View file

@ -21,7 +21,7 @@ use crate::BuildingId;
/// Per-player cross-turn tactical intent (p1-29h). The persistence channel that
/// makes the AI's war *decisive* (capture → elimination) instead of indecisive
/// (capture → disperse → opponent refounds). Carried on
/// `mc_turn::PlayerState::tactical_memory` (`#[serde(skip)]` there); the
/// `mc_state::game_state::PlayerState::tactical_memory` (`#[serde(skip)]` there); the
/// commitment hysteresis + army-wide target-lock that `mc-ai`'s
/// `decide_movement` mutates each turn.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View file

@ -1448,7 +1448,7 @@ fn apply_move(
state
.pending_move_requests
.push(mc_turn::MoveRequest {
.push(mc_state::game_state::MoveRequest {
player_idx,
unit_idx,
target_col: to[0],

View file

@ -27,7 +27,7 @@ use std::collections::BTreeMap;
use mc_player_api::action::PlayerAction;
use mc_player_api::apply_action;
use mc_turn::{GameState, MapUnit, PlayerState};
use mc_state::game_state::{GameState, MapUnit, PlayerState};
use mc_ai::evaluator::ScoringWeights;
use mc_trade::relation::{Relation, RelationState};

View file

@ -20,7 +20,7 @@
use mc_player_api::action::PlayerAction;
use mc_player_api::apply_action;
use mc_player_api::wire::Event;
use mc_turn::{GameState, MapUnit, PlayerState};
use mc_state::game_state::{GameState, MapUnit, PlayerState};
#[test]
fn queued_attack_emits_unit_destroyed_event() {

View file

@ -25,7 +25,8 @@ use mc_ecology::EcologyEngine;
use mc_flora::FloraEngine;
#[cfg(feature = "gpu")]
use mc_compute::AcceleratedClimate;
use mc_turn::{CityEcology, GameState, MapUnit, PlayerState, TurnProcessor, VictoryConfig, VictoryType};
use mc_state::game_state::{CityEcology, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor, VictoryConfig, VictoryType};
use serde::Deserialize;
use std::collections::HashMap;
use std::path::Path;

View file

@ -23,7 +23,8 @@ use mc_ecology::EcologyEngine;
use mc_flora::FloraEngine;
#[cfg(feature = "gpu")]
use mc_compute::AcceleratedClimate;
use mc_turn::{CityEcology, FaunaCombatEvent, GameState, MapUnit, PlayerState, TurnProcessor};
use mc_state::game_state::{CityEcology, GameState, MapUnit, PlayerState};
use mc_turn::{FaunaCombatEvent, TurnProcessor};
use std::collections::HashMap;
const EVOLUTION_TICKS: u32 = 50_000;

View file

@ -11,7 +11,8 @@ use mc_ecology::EcologyEngine;
use mc_flora::FloraEngine;
#[cfg(feature = "gpu")]
use mc_compute::AcceleratedClimate;
use mc_turn::{CityEcology, GameState, MapUnit, PlayerState, TurnProcessor};
use mc_state::game_state::{CityEcology, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
use std::collections::HashMap;
const MAP_SIZE: i32 = 48;

View file

@ -19,7 +19,8 @@ use mc_core::grid::GridState;
use mc_ecology::evolution::{run_evolution, EventConfig, WorldAgeConfig};
use mc_ecology::EcologyEngine;
use mc_flora::FloraEngine;
use mc_turn::{CityEcology, GameState, MapUnit, PlayerState, TurnProcessor, VictoryConfig, VictoryType};
use mc_state::game_state::{CityEcology, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor, VictoryConfig, VictoryType};
use serde::Deserialize;
use std::collections::HashMap;
use std::path::Path;

View file

@ -2,7 +2,8 @@ pub mod event_dispatch;
use mc_balance::{outcome::GameOutcome, runner::SimRunner, strategy::StrategyConfig};
use mc_city::CityState;
use mc_turn::{GameState, PlayerState, TurnProcessor};
use mc_state::game_state::{GameState, PlayerState};
use mc_turn::{TurnProcessor};
/// Real game runner backed by mc-turn. Implements the SimRunner trait
/// so balance-tool and optimizer can run actual simulated games.

View file

@ -1,17 +0,0 @@
//! Re-export shim (p2-65 Phase 3b).
//!
//! The canonical full-simulation state struct `GameState` and its pending-queue
//! value types now live in [`mc_state::game_state`]. This module re-exports the
//! whole thing so every `crate::game_state::…` / `mc_turn::game_state::…` import
//! path inside `mc-turn` (and the `mc_turn::lib.rs` re-export of `GameState`,
//! `PlayerState`, `MapUnit`, the action-request structs, …) keeps resolving
//! unchanged for one migration cycle.
//!
//! The turn-step *logic* that mutates `GameState` stays in `mc-turn`
//! (`processor.rs`, `action_handlers/`, victory/capture/ransom resolvers). The
//! `PendingCaptureEvents::drain_into` event-translation method — which embeds
//! `mc_replay::TurnEvent` + `mc_turn::combat_event::TurnResult` (both mc-turn
//! shapes, not movable to the data crate without a cycle) — is re-homed as the
//! `crate::capture_drain::DrainCaptureEvents` extension trait.
pub use mc_state::game_state::*;

View file

@ -29,7 +29,11 @@ pub mod chronicle;
pub mod end_conditions;
pub mod formation_move;
pub mod patrol;
pub mod game_state;
// p2-65 Phase 4: GameState + pending-queue value types live in `mc-state`.
// Crate-private re-export so mc-turn internals keep using `crate::game_state::…`;
// `pub(crate)` deliberately forbids any external pass-through of these shapes
// through mc-turn (consumers import them from `mc_state` directly).
pub(crate) use mc_state::game_state;
pub mod combat_event;
pub mod processor;
pub mod prologue;
@ -60,7 +64,6 @@ pub use quality::{apply_quality, band_name, resolve_deltas, UnitQualityChain};
pub use lair_siege::{
tick_one_lair, LairSiegeConfig, LairSiegeEvent, SiegeTuningData, TickResult,
};
pub use game_state::{AttackRequest, BombardRequest, BuildingRallyPoint, ChargeRequest, CityEcology, EscortRequest, GameState, MapUnit, MoveRequest, PillageRequest, PlayerState, TechState, VolleyRequest};
pub use mc_core::improvement::{RawImprovementJson, TileImprovement, TileImprovementSpec};
pub use capture::{resolve_posture, CapturePosture, PromptUnresolved};
pub use ransom::{RansomOffer, RansomQueue, RANSOM_OFFER_DURATION_TURNS};

View file

@ -4633,7 +4633,7 @@ pub enum MoveOutcome {
},
}
fn process_one_move(state: &mut GameState, req: &crate::MoveRequest) -> MoveOutcome {
fn process_one_move(state: &mut GameState, req: &crate::game_state::MoveRequest) -> MoveOutcome {
use mc_pathfinding::{find_path, UnitDomain};
// Bounds + lookup.
@ -4814,7 +4814,7 @@ fn process_one_move(state: &mut GameState, req: &crate::MoveRequest) -> MoveOutc
mod move_request_tests {
use super::*;
use crate::game_state::MoveRequest;
use crate::{GameState, MapUnit, PlayerState};
use crate::game_state::{GameState, MapUnit, PlayerState};
use mc_core::grid::GridState;
fn build_state_with_unit(
@ -8096,7 +8096,7 @@ mod tests {
// Move the escort (unit_idx 1) one tile to (6, 4). No grid -> teleport,
// cost = 1.
state.pending_move_requests.push(crate::MoveRequest {
state.pending_move_requests.push(crate::game_state::MoveRequest {
player_idx: 0, unit_idx: 1, target_col: 6, target_row: 4,
});
let outcomes = process_move_requests(&mut state);
@ -8137,7 +8137,7 @@ mod tests {
..Default::default()
};
state.escort_links.insert(1, 2);
state.pending_move_requests.push(crate::MoveRequest {
state.pending_move_requests.push(crate::game_state::MoveRequest {
player_idx: 0, unit_idx: 1, target_col: 6, target_row: 4,
});
let outcomes = process_move_requests(&mut state);

View file

@ -13,7 +13,8 @@ use mc_core::{
ids::SpeciesId,
};
use mc_replay::TurnEvent;
use mc_turn::{GameState, MapUnit, PlayerState, TurnProcessor};
use mc_state::game_state::{GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
/// Load the real encounter_rates.json from the workspace.
fn load_rates() -> EncounterRates {

View file

@ -19,9 +19,8 @@
use mc_core::units::ActionPoints;
use mc_replay::TurnEvent;
use mc_turn::{
capture::CapturePosture, AttackRequest, GameState, MapUnit, PlayerState, TurnProcessor,
};
use mc_state::game_state::{AttackRequest, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor, capture::CapturePosture};
use mc_units::{CombatStats, UnitStats as CatalogUnitStats, UnitsCatalog};
const CARAVAN_ID: u32 = 900;

View file

@ -40,15 +40,11 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_replay::TurnEvent;
use mc_turn::{
capture_drain::DrainCaptureEvents,
combat_event::{
use mc_state::game_state::{GameState, MapUnit, PendingCaptureEvents, PlayerState};
use mc_turn::{TurnProcessor, capture_drain::DrainCaptureEvents, combat_event::{
CivilianDestroyedEvent, TurnResult, UnitCapturedEvent,
UnitRansomAcceptedEvent, UnitRansomExpiredEvent, UnitRansomOfferedEvent,
},
game_state::{GameState, MapUnit, PendingCaptureEvents, PlayerState},
TurnProcessor,
};
}};
use std::collections::BTreeMap;
fn minimal_player(index: u8, anchor: (i32, i32)) -> PlayerState {

View file

@ -24,9 +24,8 @@
use mc_core::units::ActionPoints;
use mc_replay::TurnEvent;
use mc_turn::{
capture::CapturePosture, AttackRequest, GameState, MapUnit, PlayerState, TurnProcessor,
};
use mc_state::game_state::{AttackRequest, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor, capture::CapturePosture};
use mc_units::{CombatStats, UnitStats as CatalogUnitStats, UnitsCatalog};
const ENGINEER_ID: u32 = 800;

View file

@ -40,9 +40,8 @@
//! correct outcome.
use mc_replay::TurnEvent;
use mc_turn::{
capture::CapturePosture, AttackRequest, GameState, MapUnit, PlayerState, TurnProcessor,
};
use mc_state::game_state::{AttackRequest, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor, capture::CapturePosture};
use mc_units::{CombatStats, UnitStats as CatalogUnitStats, UnitsCatalog};
fn build_capturable_catalog() -> UnitsCatalog {

View file

@ -22,9 +22,8 @@ use mc_replay::{
ClanId, GameHistory, GameId, MapDescriptor, PackId, PackVersion, TurnEvent,
TurnEventCollector,
};
use mc_turn::{
AttackRequest, GameState, MapUnit, PlayerState, TurnProcessor,
};
use mc_state::game_state::{AttackRequest, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
use std::collections::BTreeMap;
fn balanced_player(index: u8, anchor_x: i32) -> PlayerState {

View file

@ -6,7 +6,8 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_turn::{GameState, LairCombatConfig, PlayerState, TurnProcessor};
use mc_state::game_state::{GameState, PlayerState};
use mc_turn::{LairCombatConfig, TurnProcessor};
use std::collections::BTreeMap;
fn balanced_player(index: u8) -> PlayerState {

View file

@ -8,10 +8,8 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_replay::{ClanId, TurnEvent};
use mc_turn::{
end_conditions::{evaluate_conditions, GameOver, GameOverReason},
GameState, LairCombatConfig, PlayerState, TurnProcessor, VictoryConfig, VictoryType,
};
use mc_state::game_state::{GameState, PlayerState};
use mc_turn::{LairCombatConfig, TurnProcessor, VictoryConfig, VictoryType, end_conditions::{evaluate_conditions, GameOver, GameOverReason}};
use std::collections::{BTreeMap, BTreeSet};
// ── Helpers ───────────────────────────────────────────────────────────────────

View file

@ -16,7 +16,8 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_core::lair::{SiegePressure, SiegeState};
use mc_turn::{GameState, MapUnit, PlayerState, TurnProcessor};
use mc_state::game_state::{GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
use std::collections::BTreeMap;
/// A one-player fixture with a single unit at `unit_pos`. Production is left

View file

@ -18,9 +18,8 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_replay::{ClanId, TurnEvent};
use mc_turn::{
GameState, MapUnit, PlayerState, TurnProcessor, VictoryConfig,
};
use mc_state::game_state::{GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor, VictoryConfig};
use std::collections::BTreeMap;
fn player_with_city_at(index: u8, pos: (i32, i32)) -> PlayerState {

View file

@ -29,7 +29,8 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::{CityState, Queueable, QualityTier};
use mc_turn::{GameState, MapUnit, PlayerState, TurnProcessor};
use mc_state::game_state::{GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
use mc_units::{CombatStats, UnitStats as CatalogUnitStats};
use std::collections::BTreeMap;

View file

@ -21,7 +21,8 @@
//! unit_id = 200, .. }` is in `result.events_emitted`.
use mc_replay::{ClanId, TurnEvent};
use mc_turn::{AttackRequest, GameState, MapUnit, PlayerState, TurnProcessor};
use mc_state::game_state::{AttackRequest, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
#[test]
fn queued_pvp_kill_emits_unit_killed_event() {

View file

@ -31,10 +31,8 @@ static ENV_GUARD: Mutex<()> = Mutex::new(());
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_replay::TurnEvent;
use mc_turn::{
game_state::{GameState, MapUnit, PlayerState},
TurnProcessor,
};
use mc_state::game_state::{GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
fn data_dir() -> PathBuf {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

View file

@ -22,7 +22,7 @@ use mc_trade::relation::{Relation, RelationState};
use mc_trade::{
CourierRoute, DiplomaticAgreement, OpenBordersAgreement, SharedMapAgreement, TradeLedger,
};
use mc_turn::{GameState, MapUnit, PlayerState, TechState};
use mc_state::game_state::{GameState, MapUnit, PlayerState, TechState};
use std::collections::{BTreeMap, BTreeSet};
// ── Fixture builders ────────────────────────────────────────────────────────

View file

@ -22,7 +22,8 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_replay::TurnEvent;
use mc_turn::{GameState, PlayerState, TurnProcessor};
use mc_state::game_state::{GameState, PlayerState};
use mc_turn::{TurnProcessor};
use std::collections::BTreeMap;
/// Two-player fixture rigged so bench `try_spawn_unit` fires every turn

View file

@ -16,10 +16,8 @@
use mc_ai::evaluator::ScoringWeights;
use mc_city::CityState;
use mc_replay::ClanId;
use mc_turn::{
end_conditions::{evaluate_conditions, load_conditions, GameOverReason},
GameState, PlayerState, TechState, VictoryConfig, VictoryType,
};
use mc_state::game_state::{GameState, PlayerState, TechState};
use mc_turn::{VictoryConfig, VictoryType, end_conditions::{evaluate_conditions, load_conditions, GameOverReason}};
use std::collections::BTreeMap;
// Path relative to this crate's manifest (CARGO_MANIFEST_DIR resolves to

View file

@ -13,6 +13,7 @@ mc-city = { path = "../../crates/mc-city" }
mc-happiness = { path = "../../crates/mc-happiness" }
mc-combat = { path = "../../crates/mc-combat" }
mc-culture = { path = "../../crates/mc-culture" }
mc-state = { path = "../../crates/mc-state" }
mc-turn = { path = "../../crates/mc-turn" }
[lints]

View file

@ -6,7 +6,8 @@
//! - resolve_single_pvp_attack enqueues via the same code route as process_pvp_combat
use mc_city::CityState;
use mc_turn::{AttackRequest, GameState, MapUnit, PlayerState, TurnProcessor};
use mc_state::game_state::{AttackRequest, GameState, MapUnit, PlayerState};
use mc_turn::{TurnProcessor};
use std::collections::BTreeMap;
fn make_warrior(col: i32, row: i32) -> MapUnit {