Movement planned in full, then committed: a Planner builds a plan (moving and destroyed ids) by running content's movement responses (OPEN/SHUT, PUSH, STOP) for every step, rolling back failed branches. Edge of level blocks; YOU objects move furthest-ahead first; rules are reparsed once after movement. Harness: move/move_dir(Dir), posOf, kindsAt. Expansion Drop: PULL as one word plus one content response. Co-Authored-By: Claude Opus 5.5 <[email protected]>
140 lines
3.9 KiB
Rust
140 lines
3.9 KiB
Rust
// Module 8: Push chains. Each test matches a heading in scenarios.md.
|
|
use engine::harness::*;
|
|
|
|
/// An empty level `width` wide and `height` high.
|
|
fn empty_level(width: i32, height: i32) -> LevelDef {
|
|
LevelDef {
|
|
width,
|
|
height,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// Place one text object per word: `(word, x, y)`.
|
|
fn write(b: &mut Baba, words: &[(&str, i32, i32)]) {
|
|
for &(word, x, y) in words {
|
|
b.place(&format!("text:{word}"), x, y);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_08_chain_moves_together() {
|
|
let mut b = new_baba(empty_level(6, 4));
|
|
write(&mut b, &[("BABA", 0, 0), ("IS", 1, 0), ("YOU", 2, 0)]);
|
|
write(&mut b, &[("ROCK", 0, 1), ("IS", 1, 1), ("PUSH", 2, 1)]);
|
|
let baba = b.place("baba", 0, 3);
|
|
let rocks = [
|
|
b.place("rock", 1, 3),
|
|
b.place("rock", 2, 3),
|
|
b.place("rock", 3, 3),
|
|
];
|
|
|
|
b.move_dir(Dir::Right);
|
|
|
|
assert_eq!(b.pos_of(baba), Some((1, 3)));
|
|
assert_eq!(b.pos_of(rocks[0]), Some((2, 3)));
|
|
assert_eq!(b.pos_of(rocks[1]), Some((3, 3)));
|
|
assert_eq!(b.pos_of(rocks[2]), Some((4, 3)));
|
|
assert_eq!(b.kinds_at(0, 3), Vec::<String>::new());
|
|
assert_eq!(b.kinds_at(4, 3), vec!["rock"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_08_chain_into_stop_nothing_moves() {
|
|
let mut b = new_baba(empty_level(6, 5));
|
|
write(&mut b, &[("BABA", 0, 0), ("IS", 1, 0), ("YOU", 2, 0)]);
|
|
write(&mut b, &[("ROCK", 0, 1), ("IS", 1, 1), ("PUSH", 2, 1)]);
|
|
write(&mut b, &[("WALL", 0, 2), ("IS", 1, 2), ("STOP", 2, 2)]);
|
|
let baba = b.place("baba", 0, 4);
|
|
let rocks = [
|
|
b.place("rock", 1, 4),
|
|
b.place("rock", 2, 4),
|
|
b.place("rock", 3, 4),
|
|
];
|
|
let wall = b.place("wall", 4, 4);
|
|
|
|
b.move_dir(Dir::Right);
|
|
|
|
assert_eq!(b.pos_of(baba), Some((0, 4)));
|
|
assert_eq!(b.pos_of(rocks[0]), Some((1, 4)));
|
|
assert_eq!(b.pos_of(rocks[1]), Some((2, 4)));
|
|
assert_eq!(b.pos_of(rocks[2]), Some((3, 4)));
|
|
assert_eq!(b.pos_of(wall), Some((4, 4)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_08_open_shut_during_push() {
|
|
let mut b = new_baba(empty_level(5, 5));
|
|
write(&mut b, &[("BABA", 0, 0), ("IS", 1, 0), ("YOU", 2, 0)]);
|
|
write(
|
|
&mut b,
|
|
&[
|
|
("KEY", 0, 1),
|
|
("IS", 1, 1),
|
|
("PUSH", 2, 1),
|
|
("AND", 3, 1),
|
|
("OPEN", 4, 1),
|
|
],
|
|
);
|
|
write(
|
|
&mut b,
|
|
&[
|
|
("DOOR", 0, 2),
|
|
("IS", 1, 2),
|
|
("STOP", 2, 2),
|
|
("AND", 3, 2),
|
|
("SHUT", 4, 2),
|
|
],
|
|
);
|
|
let baba = b.place("baba", 0, 4);
|
|
let key = b.place("key", 1, 4);
|
|
let door = b.place("door", 2, 4);
|
|
|
|
b.move_dir(Dir::Right);
|
|
|
|
assert_eq!(b.pos_of(key), None);
|
|
assert_eq!(b.pos_of(door), None);
|
|
// Inferred, not stated by a source: Baba lands on the key's former tile.
|
|
assert_eq!(b.pos_of(baba), Some((1, 4)));
|
|
assert_eq!(b.kinds_at(2, 4), Vec::<String>::new());
|
|
assert_eq!(b.kinds_at(1, 4), vec!["baba"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_08_text_pushed() {
|
|
let mut b = new_baba(empty_level(4, 5));
|
|
write(&mut b, &[("BABA", 0, 0), ("IS", 1, 0), ("YOU", 2, 0)]);
|
|
let baba = b.place("baba", 0, 2);
|
|
let rock_text = b.place("text:ROCK", 1, 2);
|
|
write(&mut b, &[("IS", 2, 3), ("PUSH", 2, 4)]);
|
|
|
|
assert_eq!(b.rules(), vec!["BABA IS YOU", "TEXT IS PUSH"]);
|
|
|
|
b.move_dir(Dir::Right);
|
|
|
|
assert_eq!(b.pos_of(baba), Some((1, 2)));
|
|
assert_eq!(b.pos_of(rock_text), Some((2, 2)));
|
|
assert_eq!(
|
|
b.rules(),
|
|
vec!["BABA IS YOU", "ROCK IS PUSH", "TEXT IS PUSH"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_08_level_edge_is_stop() {
|
|
let mut b = new_baba(empty_level(3, 3));
|
|
write(&mut b, &[("BABA", 0, 0), ("IS", 1, 0), ("YOU", 2, 0)]);
|
|
let baba = b.place("baba", 1, 2);
|
|
let flag_text = b.place("text:FLAG", 2, 2);
|
|
|
|
b.move_dir(Dir::Right);
|
|
|
|
assert_eq!(b.pos_of(baba), Some((1, 2)));
|
|
assert_eq!(b.pos_of(flag_text), Some((2, 2)));
|
|
|
|
b.move_dir(Dir::Down);
|
|
|
|
assert_eq!(b.pos_of(baba), Some((1, 2)));
|
|
assert_eq!(b.pos_of(flag_text), Some((2, 2)));
|
|
}
|