Files
systemic-game-design-course/course/09-turn-pipeline/reference/ts/src/engine/puzzle/resolve.ts
T
Lucas WintherandClaude Opus 5.5 1f3359cfe2 feat: module 09 turn pipeline
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>
2026-10-29 04:28:17 +01:00

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));
}