Dynamic-rule Sokoban engine: design backbone
This document is a vocabulary and execution contract for a grid puzzle engine that combines Sokoban-style pushing with Baba Is You-style rules that are physical, editable objects. It separates observations about the reference games from recommendations for this engine so that a mod author can tell which behavior is compatibility-oriented and which behavior is ours to choose.
Design thesis
The core puzzle loop is: inspect a board, form or break a rule, observe the new affordances, then make a deliberately ordered sequence of movement and pushes. The board contains both the objects affected by rules and the words that define the rules. A rule is therefore state, level geometry, and puzzle clue at the same time.
This is a direct synthesis of two proven constraints:
- Sokoban's single verb is constrained pushing. The player can walk and push but cannot pull, so a push can permanently remove future options. The creator Hiroyuki Imabayashi describes the 1981 prototype as a puzzle in which a bad box placement can make further progress impossible (creator interview).
- Baba Is You makes the rules themselves pushable blocks. Teikari's GDC slides describe the basic rule as object + verb + quality, with rules being re-detected after words move (GDC talk PDF, pp. 8–14).
Recommendation: make every turn replayable and undoable from the beginning. This preserves Sokoban's meaningful irreversible pushes while allowing players to explore a dynamic rule system without paying a restart tax.
Vocabulary inventory
The engine should distinguish surface words (what a module places and the player reads) from semantic capabilities (what the simulation executes). This avoids making the parser and renderer depend on JavaScript class names.
Tier 0: board invariants
These are the smallest shared concepts and should be stable in the public mod API.
| Term | Meaning | Required invariant |
|---|---|---|
cell |
One integer grid coordinate | A cell can hold zero or more entities and terrain layers. |
entity |
A runtime instance with identity and position | Identity survives movement; two entities may share a cell. |
kind |
Whether an entity is a physical object or pushable word |
Rule matching uses an object's noun or a word's text, never an instance ID. |
noun |
Semantic type of a physical object, such as MIAU, WALL, or ROCK |
Rules target nouns, so every object of the same noun gains the same derived properties. |
layer |
Terrain, actor, item, text, or effect ordering | Rendering order must not silently change collision order. |
direction |
Facing or movement vector | Cardinal movement is the baseline; diagonal support is an explicit extension. |
occupancy |
Entities currently in a cell | Never encode occupancy as a single sprite slot: stacked words and objects are valid. |
property |
A capability or tag attached by active rules | Properties are recomputed from the current rule set, not permanently mutated. |
goal |
A predicate over state that ends or advances a level | A goal may be an object property (WIN) or a mod-defined predicate. |
turn |
One accepted player intent plus all resulting resolution | Undo restores the complete pre-turn snapshot, including rules and effects. |
The stacked-word invariant is evidence-based: Teikari says the shipped system had to be rewritten to account for words sharing a cell and to test possible permutations (Game Developer interview, lines 68–71).
Tier 1: nouns, operators, and properties
The initial built-in lexicon should be small enough to teach in a few levels. Words are data records, not reserved identifiers; a mod can add synonyms or a new word that maps to an existing semantic capability.
Nouns (things that can be targeted):
YOU's subject kinds such as BABA and FENN; board kinds such as WALL,
ROCK, FLAG, WATER, LAVA, GRASS; and meta kinds TEXT, LEVEL,
ALL, EMPTY, WORD. ALL and EMPTY are selectors rather than ordinary
spawnable kinds and need explicit scope rules.
Core relation verbs:
IS assigns properties or transforms one kind into another; HAS emits or
creates a kind when the subject is destroyed; MAKE is an optional explicit
spawn relation. Start with IS and HAS; keep MAKE out until a concrete
level needs its semantics.
Core properties / capabilities:
YOU makes every matching entity player-controlled; PUSH allows an entity to
be pushed; PULL makes it drag an adjacent entity when it moves; STOP blocks
movement; OPEN and SHUT form a paired interaction; SINK consumes both
participants; WIN satisfies the level goal on contact; DEFEAT resets or
removes the controlled entity; HOT and MELT form a paired interaction;
FLOAT chooses a collision layer; MOVE supplies autonomous movement; SHIFT
transfers entities on contact.
Connectives and modifiers:
AND bundles adjacent nouns or properties; NOT negates a term; ON, NEAR,
FACING, and LONELY constrain a rule by spatial context. A parser must retain
the exact token sequence and source coordinates so diagnostics and editor
highlighting can explain why a rule is active.
These categories follow Teikari's documented progression: three-part rules,
then AND and ON, then parameterized conditionals such as NEAR and
FACING, then LONELY, NOT, letters, and stacked rules (GDC talk PDF, pp. 15–21).
Tier 2: derived systems
These are useful after the core loop is stable:
- Rule text: words have their own kind and can be targeted by
TEXT, transformed, destroyed, or made controllable. - Plural and wildcard selectors:
ALL IS YOUexpands to all eligible kinds; expansion must be a deterministic snapshot for the turn. - Conditional predicates: a rule can query the subject's neighbors, supporting cell, facing vector, or count. Predicates should be pure queries.
- Creation and destruction:
HAS,SINK,DEFEAT,MELT, andOPEN /are effect relationships, not movement properties.SHUT - Meta-board entities: the level map, level-select map, or UI may become simulation entities, but only in an opt-in extension tier.
Teikari's implementation uses multiple passes: find candidate straight-line sentences, filter word-type order, validate syntax, convert to rule records, then apply expansion and disabling. It accepts only left-to-right and top-to-bottom straight lines and discards lines shorter than three words (GDC talk PDF, pp. 10–14). That is a good default grammar because it is predictable to read and easy to debug.
Turn pipeline and precedence
The following order is the recommended engine contract. It makes each phase observable for debugging and deterministic for mods.
- Accept intent. Read one cardinal input (
up,down,left,right),wait,undo, orreset. Reject input while a level transition is being committed. - Snapshot. Save the entire state, including entity identities, positions, directions, text stacks, active rules, RNG seed, and pending effects.
- Resolve movement intent. Determine controlled entities from the active
YOUrules. For each requested move, compute the push chain in a stable order. A blocked chain fails atomically; a legal chain moves as one action. - Apply movement and pushes. Move entities from back to front. A push
cannot pull an entity unless
PULLindependently creates that behavior. Resolve simultaneous controlled entities with a documented ordering key (for example entity ID), then expose collision groups to mods. - Re-scan rules if relevant state changed. If any text moved, was created, destroyed, transformed, or stacked, parse the board again. The reference implementation tracks whether words changed and only rewrites rules when needed (Game Developer interview, lines 46–52).
- Compile semantic rules. Validate syntax, expand
AND/ALL, apply negation and disablement, and replace the active rule table atomically. Contradictions must be deterministic: a negative rule such asBABA IS NOT YOUsuppresses the positiveBABA IS YOUrule, as in the reference game. - Resolve interactions. Run paired effects (
OPEN/SHUT,SINK,DEFEAT,MELT) using a fixed priority list. Recommended initial order: structural destruction and transformations, then defeat, then win. A mod can register a priority but cannot create a cycle. - Run autonomous behavior. Apply
MOVE,SHIFT, and similar behaviors as a new deterministic substep, then re-run rule and interaction phases if they changed text or occupancy. Cap cascading substeps and report a cycle. - Evaluate goals. Evaluate
WINand mod goals against the settled state. A surviving object with bothYOUandWINsatisfies the goal by itself; aYOUobject and a distinctWINobject satisfy it when they share a cell. Evaluate this afterSINKandDEFEATremovals. If both win and defeat occur in a turn, use the explicit goal policy (the recommended default is defeat before win unless the level opts into “reaching win ends immediately”). This self-win behavior is supported by Hempuli's patch note forEMPTY IS YOU AND WIN AND DEFEAT(official patch notes). - Commit presentation. Emit a replay event list, sound/effect cues, and text highlighting. Rendering and particles must not mutate simulation state.
This pipeline is an engine recommendation inferred from the source's multi-pass rule detection and from Sokoban's need to reason about pushes as irreversible state changes. It is not a claim that the commercial games use this exact sequence.
Puzzle pedagogy
Use a vocabulary curriculum, not a feature dump. Teach one new noun or property in a low-pressure board; immediately give a second level where the same word combines with an older word; then make the player manipulate the word tile itself. Keep the number of initially active rules low. Teikari says levels were cut when the intended interaction could be bypassed, required too many supporting rules, or became overwhelming with many active rules (Game Developer interview, lines 59–64).
Preserve a visible escape route from difficulty spikes. Teikari grouped areas
by introduced words and used “Extra” markers to signal unusually difficult
levels, while acknowledging that sequential numbering worked against returning
later (same interview, lines 82–87).
For this engine, a mod should be able to mark levels as required, optional,
or challenge, and the campaign should unlock around a hard puzzle.
Sokoban pedagogy depends on the player learning which pushes are unsafe. A crate in a non-goal corner is a simple deadlock; more complex deadlocks arise from wall corridors, frozen crate groups, and goal ordering. Deadlock research exists because these states make Sokoban difficult even for non-optimal solving (Cazenave, “Towards deadlock free Sokoban”). Make deadlocks legible through undo, restart, and optional post-failure hints; do not silently auto-solve or remove every consequence.
For a design tool, validate levels with at least: reachability of controlled entities, a goal witness or solver result, detection of static dead squares, and a report of active rules at level start. Unique-solution or minimum-push claims are optional metadata, not a requirement for a fun level.
CommonJS mod boundary
The first playable release accepts trusted local CommonJS modules containing a manifest, levels, noun vocabulary, and an emoji palette. Its concrete shape and limits are in the modding guide. A later engine milestone should add pure hooks through a narrow, versioned contract such as:
module.exports = {
manifest: { id: 'garden-01', name: 'Garden', version: '2.0.0' },
vocabulary: { nouns: [], verbs: [], properties: [] },
levels: [{ id: 'intro', width: 9, height: 7, cells: [] }],
systems: {
onTurnStart(ctx) {},
onCollision(ctx) {},
onGoalCheck(ctx) { return false }
}
}
The host owns parsing, snapshots, input, rendering, replay, and persistence.
Mods may register words, level data, serializers, pure predicates, and named
effect handlers. They should not access DOM nodes, mutate the active rule table
in place, replace the scheduler, use wall-clock time, or depend on object
iteration order. The host passes a capability-scoped context with read-only
queries and queued commands (spawn, destroy, setDirection, emitEffect).
This boundary is a recommendation for deterministic, shareable content. It is also a practical security boundary: CommonJS is code, so untrusted mods need a worker or restricted loader with explicit resource and API limits.
Explicit non-goals and open decisions
The first engine should not promise full commercial Baba compatibility, natural language parsing, diagonal movement, continuous time, physics simulation, network multiplayer, arbitrary UI-as-level semantics, or unrestricted code execution in mods. Those features multiply ambiguity before the basic turn contract is proven.
Open decisions to settle with executable examples: whether entities can occupy
multiple layers in one cell; whether YOU entities move simultaneously or in
ID order; whether win precedes defeat; whether a failed push still consumes a
turn; how ALL, EMPTY, and TEXT select targets; whether rule scan uses
nearest valid sentence or all overlapping valid sentences; and whether effects
are allowed to trigger recursive rule scans. Each decision should become a
small fixture level and a replay test before the mod API is frozen.
The GDC talk explicitly cautions that a perfectly robust parser would require
removing some desirable complexity; Teikari kept unstable but fitting elements
and limited player access to the worst cases, while treating IF as not worth
the implementation pain at that stage (GDC talk PDF, pp. 19–21).
That is a useful product constraint: keep the grammar deliberately small and
make quirky extensions opt-in and testable.
Sources
- Hempuli, official Baba Is You patch notes, documenting the
EMPTY IS YOU AND WIN AND DEFEATwin-condition fix.