test(@projects/@magic-civilization): ⛵ p3-18 P6 — capstone: army fords water then attacks enemy on the far landmass
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) <noreply@anthropic.com>
This commit is contained in:
parent
1f3f535e10
commit
22d0379a2c
1 changed files with 75 additions and 0 deletions
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue