First Part II module. The cumulative reference adds a second engine (src/engine/puzzle: grid, lexer, parser) beside Part I's; nouns and properties are data in src/content/words.json, and the engine names only IS/AND/NOT/TEXT and the base rule TEXT IS PUSH. Rules are recompiled from the board on every change; rules() is a sorted set. Adds the M6 scenarios and tests (rows, columns, shared IS, AND distribution, stacked text, base rule), the HOT word as a data-only Expansion Drop, the lesson with the Part II bridge, and the Rust LevelDef shape in harness.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
119 lines
2.6 KiB
Rust
119 lines
2.6 KiB
Rust
// Module 6: Grid, lexer and parser. 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()
|
|
}
|
|
}
|
|
|
|
fn object(kind: &str, x: i32, y: i32) -> LevelObject {
|
|
LevelObject {
|
|
kind: kind.to_string(),
|
|
x,
|
|
y,
|
|
}
|
|
}
|
|
|
|
/// 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_06_horizontal_rule() {
|
|
let b = new_baba(LevelDef {
|
|
width: 5,
|
|
height: 3,
|
|
objects: vec![
|
|
object("text:BABA", 0, 0),
|
|
object("text:IS", 1, 0),
|
|
object("text:YOU", 2, 0),
|
|
object("baba", 3, 2),
|
|
object("text:YOU", 0, 2),
|
|
object("text:IS", 1, 2),
|
|
object("text:FLAG", 2, 2),
|
|
],
|
|
..Default::default()
|
|
});
|
|
|
|
assert_eq!(b.rules(), vec!["BABA IS YOU", "TEXT IS PUSH"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_06_crossword_shared_is() {
|
|
let mut b = new_baba(empty_level(3, 3));
|
|
write(
|
|
&mut b,
|
|
&[("BABA", 0, 1), ("IS", 1, 1), ("YOU", 2, 1), ("FLAG", 1, 0)],
|
|
);
|
|
|
|
assert_eq!(b.rules(), vec!["BABA IS YOU", "TEXT IS PUSH"]);
|
|
|
|
b.place("text:WIN", 1, 2);
|
|
|
|
assert_eq!(
|
|
b.rules(),
|
|
vec!["BABA IS YOU", "FLAG IS WIN", "TEXT IS PUSH"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_06_and_distributes() {
|
|
let mut b = new_baba(empty_level(7, 1));
|
|
write(
|
|
&mut b,
|
|
&[
|
|
("BABA", 0, 0),
|
|
("AND", 1, 0),
|
|
("ROCK", 2, 0),
|
|
("IS", 3, 0),
|
|
("PUSH", 4, 0),
|
|
("AND", 5, 0),
|
|
("STOP", 6, 0),
|
|
],
|
|
);
|
|
|
|
assert_eq!(
|
|
b.rules(),
|
|
vec![
|
|
"BABA IS PUSH",
|
|
"BABA IS STOP",
|
|
"ROCK IS PUSH",
|
|
"ROCK IS STOP",
|
|
"TEXT IS PUSH"
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_06_stacked_text_all_combinations() {
|
|
let mut b = new_baba(empty_level(3, 1));
|
|
write(
|
|
&mut b,
|
|
&[("BABA", 0, 0), ("ROCK", 0, 0), ("IS", 1, 0), ("YOU", 2, 0)],
|
|
);
|
|
|
|
assert_eq!(
|
|
b.rules(),
|
|
vec!["BABA IS YOU", "ROCK IS YOU", "TEXT IS PUSH"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_06_default_text_is_push() {
|
|
let b = new_baba(LevelDef {
|
|
width: 3,
|
|
height: 3,
|
|
objects: vec![object("baba", 1, 1)],
|
|
..Default::default()
|
|
});
|
|
|
|
assert_eq!(b.rules(), vec!["TEXT IS PUSH"]);
|
|
}
|