Gridponder DSL v0.5 — System Catalog
The ten built-in engine systems, their execution order, and the per-system configuration reference.
1. System Architecture
Execution Phases
Each system declares which phase(s) it participates in. During a turn, phases execute in this fixed order:
| # | Phase | Purpose |
|---|---|---|
| 1 | input_validation |
Engine validates action legality. No system runs here — this is engine-internal. |
| 2 | action_resolution |
Primary action executes (avatar moves, tiles slide, overlay shifts, etc.). |
| 3 | movement_resolution |
Secondary movement triggered by the primary action (pushing, teleporting). |
| 4 | interaction_resolution |
Reserved for future systems. In v0.5, item/environment interactions are handled by rules in phase 5. |
| 5 | cascade_resolution |
Chain effects: rules evaluate, emitters fire, gravity settles. Repeats up to maxCascadeDepth. |
| 6 | npc_resolution |
Autonomous NPC behavior executes. |
| 7 | goal_evaluation |
Win and lose conditions are checked. |
Events
Systems emit events as they modify state. Events accumulate during phases 2–4 and are consumed by rules during phase 5. See 05_rules.md for the full event catalog.
Interaction Protocol
Systems interact through:
- Shared state — all systems read/write the board, avatar, and variables. Phase ordering determines visibility.
- Events — systems emit events; rules (and some systems) react to them.
- Phase ordering — a phase-3 system always sees state changes from phase 2.
Systems never call each other directly.
Config Override
Levels may override specific config fields per system via systemOverrides. Overrides are shallow-merged onto the game-level config.
2. System Catalog
2.1 avatar_navigation
Purpose: Move the avatar one step per move action. Enforce boundaries and solid collisions.
Phase: action_resolution
Events emitted: avatar_entered, avatar_exited, move_blocked
Config:
| Field | Type | Default | Description |
|---|---|---|---|
directions |
array of strings | ["up","down","left","right"] |
Allowed movement directions. |
solidHandling |
string | "block" |
What happens when moving into a solid cell: "block" (reject move) or "delegate" (let later systems handle, e.g. push or consume). |
moveAction |
string | "move" |
Which action id triggers navigation. |
Behavior:
- Compute target position from direction.
- Check bounds — reject if out of grid.
- Check ground layer — reject if
void. - Check
solidtag on objects layer:"block": reject move."delegate": mark the move as pending. Emitmove_blockedwith the target position and blocker kind. Later phases (push) or rules (resolve_moveeffect) may complete or reject the pending move.
- If not blocked, move avatar to target. Emit
avatar_exitedfor old position,avatar_enteredfor new position. - Update
avatar.facingto the movement direction.
2.2 push_objects
Purpose: Allow the avatar to push configured objects into adjacent empty cells.
Phase: movement_resolution
Events emitted: object_pushed, object_placed, avatar_entered, avatar_exited
Config:
| Field | Type | Default | Description |
|---|---|---|---|
pushableTags |
array of strings | ["pushable"] |
Tags identifying pushable entities. |
validTargetTags |
array of strings | ["walkable"] |
Tags the destination ground must have. Also allows null (empty objects layer) cells. |
chainPush |
boolean | false |
Whether pushing into another pushable triggers a chain push. |
toolInteractions |
array | [] |
List of item-based destruction interactions. Each entry: { "item": "<kind>", "targetTag": "<tag>", "consumeItem": false, "animation": "<name>" }. When the avatar holds the specified item and moves into an entity with the specified tag, the entity is destroyed and the avatar enters the vacated cell. consumeItem (default false) controls whether the item is removed from inventory. animation (optional) names an animation defined on the target entity kind to play before removal. Applies before pushable logic — works on any solid entity, not just pushable ones. |
Behavior:
- When avatar movement targets a cell with an entity in the objects layer:
a. Check
toolInteractionsin order. If any interaction matches (avatar holds the required item, entity has the required tag), destroy entity, optionally consume item, play animation if configured, move avatar. Skip remaining push logic. b. If entity is not pushable, movement fails. c. Compute push destination (one cell further in movement direction). c. Check push destination: must be in bounds, ground must have avalidTargetTagstag, objects layer must be empty (or have matching tag ifchainPush). d. If valid: move pushed object, then move avatar into vacated cell. e. If invalid: movement fails, avatar stays. - Emit
object_pushed,object_placedfor pushed object; standard avatar events.
2.3 portals
Purpose: Teleport avatar (or objects) between paired portal entities.
Phase: movement_resolution
Events emitted: avatar_entered, avatar_exited
Config:
| Field | Type | Default | Description |
|---|---|---|---|
teleportTags |
array of strings | ["teleport"] |
Tags identifying portal entities. |
matchKey |
string | "channel" |
Entity parameter used to match portal pairs. |
endMovement |
boolean | true |
Whether teleport ends the move (avatar lands on exit portal). |
teleportObjects |
boolean | false |
Whether pushed objects can also teleport through portals. |
Behavior:
- When avatar enters a cell with a
teleportTagsentity: a. ReadmatchKeyparameter (e.g.,channel: "blue"). b. Find the paired portal with the same value. c. Move avatar to paired portal position. d. IfendMovement: turn continues from portal position. Iffalse: avatar continues moving in original direction. - If
teleportObjectsand an object is pushed onto a portal: teleport object similarly.
2.4 slide_merge
Purpose: Slide all mergeable tiles in the swipe direction; merge matching tiles.
Phase: action_resolution
Events emitted: tiles_slid, tiles_merged, cell_cleared
Config:
| Field | Type | Default | Description |
|---|---|---|---|
mergeableTags |
array of strings | ["mergeable"] |
Tags identifying slideable/mergeable entities. |
mergeAction |
string | "move" |
Which action id triggers sliding. |
mergePredicate |
string | "equal_value" |
When two tiles merge: "equal_value" (same value param). |
mergeResult |
string | "sum" |
Result of merge: "sum" or "double". |
mergeLimit |
integer | 1 |
Max merges per tile per action. |
blockerTags |
array of strings | ["solid"] |
Tags that stop sliding. |
wrapAround |
boolean | false |
Whether tiles wrap around the board. |
Behavior:
- On action, determine slide direction from action params.
- Process rows/columns in slide direction order.
- Each mergeable tile slides until hitting a boundary, blocker, void, or another tile.
- If tile meets another tile with matching
mergePredicate: merge. New tile hasmergeResultvalue. - Each tile can merge at most
mergeLimittimes per action. - Emit events for each slide and merge.
2.5 queued_emitters
Purpose: Release one item per turn from each multi-cell emitter whose exit cell is empty.
Phase: npc_resolution (runs once per turn, after all slides and cascades have settled)
Events emitted: item_released
Config:
| Field | Type | Default | Description |
|---|---|---|---|
emitterKind |
string | "pipe" |
Multi-cell object kind that acts as an emitter. |
Behavior — unidirectional pipe (no exit2Position):
- Check whether the exit cell (
exitPosition) and the spawn cell (one step inexitDirection) are both empty. - If both empty and the queue has remaining items: spawn the next item at the spawn cell, increment
currentIndex, emititem_released. - Only one item is released per emitter per turn.
Behavior — bidirectional pipe (exit2Position present):
A bidirectional pipe has two open exits. Numbers occupy physical slots (cells) within the pipe. Initially, queue[0] is placed in cell 0 (the exit-1 cell), queue[1] in cell 1, and so on. The runtime state is stored in pipeSlots — an array of length pipe_length (one entry per cell, each int | null).
Each turn the pipe runs two phases:
Emit phase: For each exit, if an item occupies the exit cell and the exit's spawn cell is clear, emit it (place it on the board, set the slot to
null). Both exits can emit simultaneously.Move phase: Each remaining item moves one step toward its nearest exit:
- Distance to exit 1 = cell index; distance to exit 2 =
(pipe_length - 1) - cell index. - Closer to exit 1 → shift one cell toward exit 1 (index − 1).
- Closer to exit 2 → shift one cell toward exit 2 (index + 1).
- Equidistant (midpoint of an odd-length pipe):
- If only exit 1 is clear → move toward exit 1.
- If only exit 2 is clear → move toward exit 2.
- If both clear or both blocked → stuck: no movement this turn.
- An item arriving at an exit cell does not emit on the same turn — it exits on the next turn's emit phase.
- Distance to exit 1 = cell index; distance to exit 2 =
Even vs. odd pipe length: In an even-length pipe every cell has a strictly nearer exit, so the stuck condition never arises. It is exclusive to odd-length pipes.
2.6 overlay_cursor
Purpose: Maintain a movable overlay region that region_transform operates on.
Phase: action_resolution
Events emitted: overlay_moved
Config:
| Field | Type | Default | Description |
|---|---|---|---|
size |
[w, h] |
[2, 2] |
Overlay dimensions. |
moveAction |
string | "move" |
Action id that moves the overlay. |
anchorToAvatar |
boolean | false |
If true, the overlay follows the avatar position. Avatar position = top-left (for 2x2) or center (for 3x3). |
boundsConstrained |
boolean | true |
Whether the overlay must stay fully within the board. |
Behavior:
- On
moveAction, shift overlay position in the action's direction. - If
boundsConstrained, clamp to board boundaries. - If
anchorToAvatar, overlay tracks avatar position automatically. - Update
state.overlay.position. - Emit
overlay_moved.
2.8 region_transform
Purpose: Apply spatial transformations (rotate, flip, diagonal swap) to cell contents within the overlay region.
Phase: action_resolution
Events emitted: region_rotated, region_flipped, cells_swapped
Config:
| Field | Type | Default | Description |
|---|---|---|---|
overlaySystemId |
string | — | Id of the overlay_cursor system providing the region. |
affectedLayers |
array of strings | ["objects"] |
Which layers are transformed. |
operations |
object | {} |
Map of operation name → operation config. |
Each operation:
| Field | Type | Description |
|---|---|---|
type |
string | "rotate", "flip", or "diagonal_swap". |
action |
string | Action id that triggers this operation. |
Example config:
{
"overlaySystemId": "overlay",
"affectedLayers": ["objects"],
"operations": {
"rotate": { "type": "rotate", "action": "rotate" },
"flip": { "type": "flip", "action": "flip" },
"swap": { "type": "diagonal_swap", "action": "diagonal_swap" }
}
}
Operation: rotate
Rotates all cell contents within the overlay.
2×2 clockwise rotation:
[0,0] → [1,0]
[1,0] → [1,1]
[1,1] → [0,1]
[0,1] → [0,0]
3×3 clockwise (standard matrix rotation): [x,y] → [size-1-y, x]
Action params: { "rotation": "clockwise" } or { "rotation": "counterclockwise" }.
Operation: flip
Mirrors cell contents along an axis.
Vertical flip: [x, y] → [x, size-1-y]
Horizontal flip: [x, y] → [size-1-x, y]
Action params: { "axis": "vertical" } or { "axis": "horizontal" }.
Operation: diagonal_swap
Swaps two diagonal corner cells based on direction.
Swap mapping (2×2 overlay at [ox, oy]):
| Direction | Cell A | Cell B |
|---|---|---|
up_left |
[ox+1, oy+1] (bottom-right) |
[ox, oy] (top-left) |
up_right |
[ox, oy+1] (bottom-left) |
[ox+1, oy] (top-right) |
down_left |
[ox+1, oy] (top-right) |
[ox, oy+1] (bottom-left) |
down_right |
[ox, oy] (top-left) |
[ox+1, oy+1] (bottom-right) |
Behavior:
- On the configured action, determine the operation type.
- For rotate/flip: collect all entities within the overlay bounds on affected layers, apply the spatial mapping, reposition.
- For diagonal_swap: swap the two mapped cells.
- Emit the corresponding event.
2.9 flood_fill
Purpose: Flood fill from a source position, changing connected same-kind/same-color cells.
Phase: action_resolution
Events emitted: cells_flooded
Config:
| Field | Type | Default | Description |
|---|---|---|---|
floodAction |
string | "flood" |
Action id that triggers flood fill. |
sourcePosition |
string | "avatar" |
"avatar" (avatar position) or "overlay_center". |
affectedLayer |
string | "objects" |
Layer to flood fill on. |
matchBy |
string | "color" |
"color" (match entities with same color param) or "kind" (match same entity kind). |
colorCycle |
array of strings | ["red","blue","green","yellow","purple","orange"] |
Color cycle for matchBy: "color". Current color advances to next in cycle. |
kindTransform |
object | {} |
For matchBy: "kind", maps current kind → new kind. |
Behavior:
- On
floodAction, determine source position. - Read the entity at source position on
affectedLayer. - Find all connected cells with the same match criterion (4-directional adjacency).
- Apply the transformation (advance color in cycle, or transform kind).
- Emit
cells_flooded.
3. System Summary Table
| System | Type | Phase | Primary Action |
|---|---|---|---|
| Avatar Navigation | avatar_navigation |
action_resolution |
move |
| Push Objects | push_objects |
movement_resolution |
(automatic on move into pushable) |
| Portals | portals |
movement_resolution |
(automatic on portal entry) |
| Slide Merge | slide_merge |
action_resolution |
move |
| Queued Emitters | queued_emitters |
cascade_resolution |
(event-triggered) |
| Gravity | gravity |
cascade_resolution |
(automatic after state changes) |
| Overlay Cursor | overlay_cursor |
action_resolution |
move |
| Region Transform | region_transform |
action_resolution |
rotate, flip, diagonal_swap |
| Flood Fill | flood_fill |
action_resolution |
flood |
Demoted to rule recipes (see 05_rules.md §9): single-slot inventory, consumable interactions, liquid transitions. These use the standard event–condition–effect primitives and no longer require dedicated engine systems.
4. System Combinations by Game Type
Flag-style games (avatar navigation puzzles)
avatar_navigation + push_objects + portals + inventory/consumable/liquid rule recipes
Number-style games (slide and merge)
slide_merge + queued_emitters + gravity (with sequence_match goal)
Number-style with diagonal swaps
slide_merge + overlay_cursor + region_transform (diagonal_swap op) (with sequence_match goal)
Transformation-style games (pattern matching)
overlay_cursor + region_transform (rotate + flip ops) + flood_fill
Hybrid games
Any combination of the above. The system architecture supports free composition as long as there are no conflicting action handlers.
5. Reserved System Types (v1+)
These types are reserved for future built-in systems:
line_push— push entire rows/columnsmulti_slot_inventory— carry multiple itemstimers— turn-count-based triggerscollectibles— collect N of M itemsrule_tiles— Baba-Is-You-style mutable rule objectsrotate_flip_board— rotate/flip the entire board (not just an overlay region)spawners— periodic entity spawning