Campaign tests: every mission runs a simulated minute undecided; missions 1 and 2 are won by a scripted player

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 11:37:32 +01:00
1 parent 6c85a62f1b
commit b8bf2b8503
1 file changed
+83
+83
View File
@@ -421,6 +421,89 @@ mod tests {
}
}
#[test]
fn missions_run_and_are_not_decided_at_once() {
for (i, d) in missions().iter().enumerate() {
let mut g = start_mission(i, None);
let m = g.mission.clone().unwrap();
assert!(!m.goals.iter().all(|&goal| g.goal_done(goal)), "{}: goals done at the start", d.key);
let t = std::time::Instant::now();
for _ in 0..(60 * 20) {
g.update(0.05);
}
println!("{}: 60 s simulated in {} ms, state {}, core {:.0}", d.key, t.elapsed().as_millis(), g.state, g.core_hp);
assert_eq!(g.state, 0, "{}: decided without the player", d.key);
}
}
// A simple scripted player: digs a corridor to the shard, bridges rivers on the way and
// sends a few strong creatures along with the flag.
fn play_to_shard(i: usize) -> (u8, f32) {
let mut g = start_mission(i, None);
let (cx, cy) = g.map.core;
let shard = g.map.t.iter().position(|t| t.kind == Kind::Shard).unwrap();
let path = g
.map
.search(idx(cx + 1, cy - 2), |j| {
let t = &g.map.t[j];
match t.kind {
Kind::Rock | Kind::Core | Kind::Tower => None,
Kind::Water | Kind::Lava => Some(3),
k if t.solid() => Some(if t.reinforced { 6 } else { 2 }),
_ => Some(1),
}
}, |j| j == shard, false)
.expect("a way to the shard");
for &j in &path {
if g.map.t[j].diggable() {
g.map.t[j].marked = true;
}
}
for k in [EK::Ogre, EK::Ogre, EK::Mage, EK::Ogre] {
g.spawn(k, false, cx as f32 + 4.5, cy as f32 + 1.5, 4);
}
g.gold = 3000.0;
// lair and mushroom garden at the sides of the start area
g.build_rect(1, cx - 3, cy - 3, cx - 2, cy + 5);
g.build_rect(2, cx + 4, cy - 3, cx + 5, cy + 5);
g.gold = 2500.0; // as if the player had mined some gold
let mut t = 0.0;
while t < 1500.0 && g.state == 0 {
for _ in 0..20 {
g.update(0.05);
}
t += 1.0;
// fallen imps are replaced
if t as i32 % 10 == 0 && g.worker_count() < 4 {
g.summon(cx as f32 + 1.5, cy as f32 - 1.5);
}
// bridges: every few seconds try to bridge the water on the path
if t as i32 % 5 == 0 {
for &j in &path {
if g.map.t[j].liquid() {
let (x, y) = xy(j);
g.build_bridges(x, y, x, y);
}
}
// the creatures follow the digging: flag at the furthest open tile of the path
if let Some(&j) = path.iter().rev().find(|&&j| g.map.t[j].walkable()) {
let (x, y) = xy(j);
g.set_flag(x, y);
}
}
}
(g.state, t)
}
#[test]
fn first_missions_can_be_won() {
for i in [0, 1] {
let (state, t) = play_to_shard(i);
println!("mission {}: state {} after {} s", i + 1, state, t);
assert_eq!(state, 1, "mission {} not won", i + 1);
}
}
#[test]
fn carry_over_takes_the_strongest() {
let mut g = start_mission(0, None);