Files
systemic-game-design-course/course/05-assemble/expansion/tests/rust/test_05x.rs
T
Lucas WintherandClaude Opus 5.5 e04dc1a11f feat: module 05 assemble
Cards become JSON data in course/cards (one file per card, validated
against card.schema.json and the catalog); the M5 references load them
via registerCards and interpret effects, replacing content closures.
Adds assemble(base, donor, half), the assemble/buffHand effects with an
injected chooser, Fleshshape as the data-only Expansion Drop, and stages
course/cards in verify-references and start-module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 03:17:21 +01:00

81 lines
2.6 KiB
Rust

// Module 5 Expansion Drop: Fleshshape. Matches expansion/README.md.
use std::cell::RefCell;
use std::rc::Rc;
use engine::harness::*;
/// Names of the minion files in `cards/` with a trigger clause listening for `on`, sorted.
fn minions_with_trigger(on: &str) -> Vec<String> {
let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/cards");
let mut names = Vec::new();
for entry in std::fs::read_dir(dir).unwrap_or_else(|e| panic!("cannot read {dir}: {e}")) {
let path = entry.unwrap().path();
if path.extension().and_then(|x| x.to_str()) != Some("json") {
continue;
}
let card: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
let has_trigger = card["text"]
.as_array()
.unwrap()
.iter()
.any(|c| c["kind"] == "trigger" && c["on"] == on);
if card["type"] == "minion" && has_trigger {
names.push(card["name"].as_str().unwrap().to_string());
}
}
names.sort();
names
}
#[test]
fn test_05x_fleshshape_pool_filter() {
let mut g = new_game(GameOpts::default());
let calls: Rc<RefCell<Vec<(String, Vec<String>)>>> = Rc::default();
let record = Rc::clone(&calls);
g.set_chooser(move |kind: &str, options: &[String]| {
record
.borrow_mut()
.push((kind.to_string(), options.to_vec()));
match kind {
"base" => "Loot Hoarder",
"donor" => "Novice Engineer",
_ => "top",
}
.to_string()
});
g.clear_log();
g.cast("Fleshshape", 0, None);
let calls = calls.borrow().clone();
let kinds: Vec<&str> = calls.iter().map(|(k, _)| k.as_str()).collect();
assert_eq!(kinds, vec!["base", "donor", "half"]);
let bases = minions_with_trigger("deathrattle");
assert!(bases.contains(&"Loot Hoarder".to_string()));
assert!(!bases.contains(&"Novice Engineer".to_string()));
assert_eq!(calls[0].1, bases);
assert!(calls[1].1.contains(&"Novice Engineer".to_string()));
let hand = g.hand(0);
assert_eq!(hand.len(), 1);
let a = hand[0];
assert_eq!(g.name_of(a), "Loot Hoarder (Assembled)");
assert_eq!(g.cost(a), 2);
assert_eq!(g.attack(a), 4);
assert_eq!(g.health(a), 3);
assert_eq!(
g.log(),
vec![
GameEvent::SpellCast {
card: "Fleshshape".to_string()
},
GameEvent::StatsChanged {
id: a,
attack: 4,
health: 3
},
]
);
}