Module 9 lesson, scenarios, tests (8, both tracks), reference engines (TURN phase list, conversion, content-registered hazards, win/lost), WEAK Expansion Drop, harness.md M9 semantics, research notes (WEAK page, baba-is-auto Game::MovePlayer, X IS NOT X placement reading). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
24 lines
1.3 KiB
TypeScript
24 lines
1.3 KiB
TypeScript
// The resolution pass: from every rule the text spells to the rules in force.
|
|
//
|
|
// The parser reports what is written; this pass decides what wins, over the whole
|
|
// rule list at once, so it doesn't matter where on the board each rule is written:
|
|
//
|
|
// 1. Negation. `X IS NOT Y` cancels every `X IS Y`. The negated rule stays in
|
|
// force: it keeps overriding, and `X IS NOT X` is also the record that every X is
|
|
// to vanish (the turn removes them, Module 9).
|
|
// 2. Identity. A surviving `X IS X` cancels every conversion `X IS Y` (`Y` a noun):
|
|
// X can't become anything else. It doesn't stop other nouns becoming X. Because
|
|
// negation runs first, `X IS NOT X` cancels `X IS X` and conversions work again.
|
|
//
|
|
// Base rules are rules like any other, so `TEXT IS NOT PUSH` cancels `TEXT IS PUSH`.
|
|
|
|
import { isConversion, isIdentity, negates, type Rule } from "./parse";
|
|
|
|
/** The rules in force, in the order they were parsed. */
|
|
export function resolve(rules: readonly Rule[]): Rule[] {
|
|
const survivesNegation = rules.filter((r) => !rules.some((n) => negates(n, r)));
|
|
const locked = (subject: string) =>
|
|
survivesNegation.some((r) => !r.negated && isIdentity(r) && r.subject === subject);
|
|
return survivesNegation.filter((r) => r.negated || !isConversion(r) || isIdentity(r) || !locked(r.subject));
|
|
}
|