feat: module 10 spellgrid
SpellGrid capstone: Part I's card engine and Part II's grid engine joined at three seams (rules -> aura data, hazard victims -> one destroy Sequence, Sequence end -> write text, remove the dead, reparse). Three levels with scripted solutions, a naive-timing test via a harness-only hook that reverses the END_OF_PHASE list, CLIs in both tracks, the BATTLECRY IS DOUBLE Expansion Drop, SpellGrid cards and words in course/cards and the catalog, level schema cards/hand, and tools staging/copying of module levels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1f3359cfe2
commit
a0cf9be214
96 files changed
+10454
-8
No files matched your search
@@ -0,0 +1,66 @@
|
||||
// The SpellGrid command line, played from a script (the reference's own check; not a course test).
|
||||
use std::cell::RefCell;
|
||||
use std::io::{Cursor, Write};
|
||||
use std::rc::Rc;
|
||||
|
||||
use engine::cli::{run, Outcome};
|
||||
|
||||
/// Collects what the CLI prints.
|
||||
#[derive(Clone, Default)]
|
||||
struct Screen(Rc<RefCell<Vec<u8>>>);
|
||||
|
||||
impl Write for Screen {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.0.borrow_mut().extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn play(level: &str, input: &[&str]) -> (Outcome, String) {
|
||||
let screen = Screen::default();
|
||||
let input = Box::new(Cursor::new(input.join("\n") + "\n"));
|
||||
let outcome = run(level, input, Box::new(screen.clone()));
|
||||
let out = String::from_utf8(screen.0.borrow().clone()).unwrap();
|
||||
(outcome, out)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_wins_level_1() {
|
||||
let input = ["w", "w", "c Whirlwind", "s", "s", "a", "s", "s", "d", "d"];
|
||||
let (outcome, out) = play(include_str!("../levels/l1-double-tap.json"), &input);
|
||||
assert_eq!(outcome, Outcome::Won);
|
||||
assert!(out.contains("DEA IS DOU"));
|
||||
assert!(out.contains("You win!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_wins_level_2_with_choices_and_play() {
|
||||
let input = [
|
||||
"c Graft",
|
||||
"Ember Moth",
|
||||
"Rune Scribe",
|
||||
"bottom",
|
||||
"c Ember Moth (Assembled) 4 3",
|
||||
"a",
|
||||
"a",
|
||||
];
|
||||
let (outcome, out) = play(include_str!("../levels/l2-spliced-corpse.json"), &input);
|
||||
assert_eq!(outcome, Outcome::Won);
|
||||
assert!(out.contains("Choose the base"));
|
||||
assert!(out.contains("Hand: Ember Moth (Assembled)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_reports_errors_and_quits() {
|
||||
let input = ["c Spark", "x", "q"];
|
||||
let (outcome, out) = play(
|
||||
include_str!("../levels/l3-simultaneous-paradox.json"),
|
||||
&input,
|
||||
);
|
||||
assert_eq!(outcome, Outcome::Quit);
|
||||
assert!(out.contains("\"Spark\" is not in the hand"));
|
||||
assert!(out.contains("Commands:"));
|
||||
}
|
||||
Reference in new issue
Block a user