From 22d0379a2c1ec6c9319147df2684ddb03320d41d Mon Sep 17 00:00:00 2001 From: Natalie Date: Thu, 25 Jun 2026 07:26:11 -0400 Subject: [PATCH] =?UTF-8?q?test(@projects/@magic-civilization):=20?= =?UTF-8?q?=E2=9B=B5=20p3-18=20P6=20=E2=80=94=20capstone:=20army=20fords?= =?UTF-8?q?=20water=20then=20attacks=20enemy=20on=20the=20far=20landmass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit teched_army_fords_water_then_attacks_enemy_on_far_landmass: end-to-end proof of the conquest payoff that motivated p3-18. Over an ocean-wall map, a player-0 army with ocean_navigation embark fords the ocean column to landmass B (process_move_requests), then strikes a player-1 unit waiting there (queued AttackRequest → process_pvp_combat): a cross-water battle resolves and the enemy takes damage. Embark turns an otherwise-unreachable rival on another landmass into an attackable one. Deterministic, cargo-verifiable; complements P6-core (the ford proof). The full headless 1v1-to-game_over demo is the only remaining (confirmatory) P6 item. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/simulator/crates/mc-turn/src/processor.rs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/simulator/crates/mc-turn/src/processor.rs b/src/simulator/crates/mc-turn/src/processor.rs index ab3a563d..72e0e076 100644 --- a/src/simulator/crates/mc-turn/src/processor.rs +++ b/src/simulator/crates/mc-turn/src/processor.rs @@ -5285,6 +5285,81 @@ mod move_request_tests { ); } + #[test] + fn teched_army_fords_water_then_attacks_enemy_on_far_landmass() { + // The conquest payoff end-to-end: landmass A (col 0) | ocean wall (col 1) + // | landmass B (cols 2-3). A teched army on A fords the ocean to B and + // strikes an enemy waiting there — embark turns an unreachable rival into + // an attackable one. + let mut grid = GridState::new(4, 3); + for r in 0..3 { + for c in 0..4 { + let idx = grid.idx(c, r); + grid.tiles[idx].biome_label_id = + if c == 1 { "ocean" } else { "plains" }.to_string(); + } + } + let mut state = GameState::default(); + state.grid = Some(grid); + state + .units_catalog + .load_json_str( + r#"[{"id":"warrior","movement":10,"domain":"land","hp":60,"attack":20,"defense":2}]"#, + ) + .expect("catalog loads"); + state.players.push(PlayerState { + player_index: 0, + embark_level: mc_core::EmbarkLevel::Ocean, + units: vec![MapUnit { + id: 1, col: 0, row: 0, unit_id: "warrior".into(), + hp: 60, max_hp: 60, attack: 20, defense: 2, ..MapUnit::default() + } + .with_moves(10)], + ..PlayerState::default() + }); + state.players.push(PlayerState { + player_index: 1, + units: vec![MapUnit { + id: 2, col: 3, row: 0, unit_id: "warrior".into(), + hp: 60, max_hp: 60, attack: 20, defense: 2, ..MapUnit::default() + } + .with_moves(0)], + ..PlayerState::default() + }); + + // 1) FORD: army (0,0) -> (2,0), crossing the ocean column to landmass B, + // landing adjacent to the enemy at (3,0). + state.pending_move_requests.push(move_req(0, (2, 0))); + process_move_requests(&mut state); + assert_eq!( + (state.players[0].units[0].col, state.players[0].units[0].row), + (2, 0), + "teched army forded the ocean to the far landmass" + ); + + // 2) ATTACK: strike the enemy now within reach across the water. + let enemy_hp_before = state.players[1].units[0].hp; + state.pending_pvp_attacks.push(crate::game_state::AttackRequest { + attacker_player: 0, + attacker_unit: 0, + defender_player: 1, + defender_unit: 0, + }); + let processor = TurnProcessor::new(500); + let mut result = TurnResult::default(); + processor.process_pvp_combat(&mut state, &mut result); + + assert!(result.pvp_battles >= 1, "a cross-water battle resolved"); + let enemy_dead = !state.players[1].units.iter().any(|u| u.id == 2); + let enemy_hurt = enemy_dead + || state.players[1].units.iter().find(|u| u.id == 2).map(|u| u.hp).unwrap_or(0) + < enemy_hp_before; + assert!( + enemy_hurt, + "the army that forded the ocean damaged the enemy on the far landmass" + ); + } + #[test] fn zero_budget_rejects() { let mut state = build_state_with_unit(7, (0, 0), 0, |_, _| "plains");