feat: Module 11, Conditions (NOUN ON NOUN IS ..., checked per object at use; NEAR as the Expansion Drop)

Adds course/11-conditions: lesson, scenarios, nine test_11 tests in both
tracks, the cumulative reference (Module 10's plus conditions) and the NEAR
drop. The engine learns a condition role, rules carry conditions, and
Level.hasProperty and conversion evaluate content's condition checks against
the board whenever a rule is used. Resolution pairs only rules with the same
conditions. Catalog, harness contract, research notes and the diff-tracks
generator follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-11-11 13:00:26 +01:00
1 parent 570e5d6826
commit 34c655c1a4
89 files changed
+11703 -4

No files matched your search

+8 -1
View File
@@ -84,6 +84,7 @@ export function generateCase(seed, vocab) {
const nouns = vocab.words.filter((w) => w.role === "noun").map((w) => w.name);
const objectNouns = vocab.words.filter((w) => w.role === "noun" && !w.selects).map((w) => w.name);
const properties = vocab.words.filter((w) => w.role === "property").map((w) => w.name);
const conditions = vocab.words.filter((w) => w.role === "condition").map((w) => w.name);
const placeable = vocab.cards.filter((c) => c.type === "minion" && c.noun);
const holdable = vocab.cards.filter((c) => c.type === "spell" || (c.type === "minion" && c.noun));
@@ -106,12 +107,18 @@ export function generateCase(seed, vocab) {
const cardNouns = cards.map((c) => vocab.cards.find((v) => v.name === c.card).noun);
const subjects = [...objectKinds, ...cardNouns];
// Whole sentences, in a row or a column: NOUN [AND NOUN] IS [NOT] (PROPERTY | NOUN) [AND ...].
// Whole sentences, in a row or a column: NOUN [AND NOUN] [CONDITION NOUN [AND NOUN]] IS [NOT]
// (PROPERTY | NOUN) [AND ...]. A condition only with a vocabulary that has condition words
// (Module 11 on), so the cases of earlier modules' vocabularies don't change.
const sentence = (first) => {
// The first is the player's: the first object noun IS YOU (that noun always has an object).
if (first) return [objectKinds[0], "IS", "YOU"];
const line = [chance(0.7) ? pick(subjects) : pick(nouns)];
if (chance(0.15)) line.push("AND", pick(subjects));
if (conditions.length && chance(0.4)) {
line.push(pick(conditions), pick(subjects));
if (chance(0.2)) line.push("AND", pick(nouns));
}
line.push("IS");
if (chance(0.15)) line.push("NOT");
line.push(chance(0.75) ? pick(properties) : pick(nouns));