Next move held four clusters of Set variable in a row, fourteen single nodes
in all; clear-lines read three variables one after the other. They are now
one plural node each, which takes Next move 40 -> 30 nodes and clear-lines
25 -> 23. Every remaining single node was checked and really is single: ok/no
are the same variable in opposite branches, take/keep the same one across a
write, and the three key setters hang on three different events.
Two contracts carry those merges, both pinned as tests. Set variables writes
per port with writeTyped exactly as the singular node does, so a Pack{rot}
still merges instead of overwriting - without it, turning would have erased
the other three fields. And the interpreter collects every input before the
node runs, so no input may come from a variable the same node writes; a data
node is evaluated once per pulse and keeps its answer. That second rule is
invisible in the game, because OnUpdate re-reads each frame and heals it, so
it lives in a unit test rather than in the smoke.
/opt/node22 and /opt/npm are gone from the tree. build.sh ran into the empty
path once they were moved away; the other seven mentions were documentation.
e2e.sh and check.sh replace the bare npm calls. A long run does not survive
the end of its shell - four full e2e runs died mid-suite with oom_kill 0 -
so e2e.sh start hands the run to a transient game-maker-e2e.service, which
does. e2e.sh status reports the running job or the last result, and calls a
log without a closing line ABORTED rather than reporting the passes it got
to. Both scripts end on an explicit exit line, because set -o pipefail inside
a script does not protect the caller: measured, ./check.sh | tail still
reports 0. Logs carry a timestamp and land in logs/, which is ignored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
2.3 KiB
Markdown
47 lines
2.3 KiB
Markdown
Tetris Game mit Structs neu bauen
|
|
...und mit neuen `Get variables` / `Set variables` node
|
|
|
|
---
|
|
|
|
**Gebaut (2026-09-10).** Beides, in zwei Durchgängen: erst die Structs, dann die Plural-Knoten.
|
|
|
|
| | vorher | jetzt |
|
|
|---|---|---|
|
|
| `shapes` | 224 lose Zahlen | 112 `{x, y}`-Bündel |
|
|
| Zellindex | `((shape·4+rot)·8 + corner·2)`, zweimal gelesen | `((shape·4+rot)·4 + corner)`, einmal |
|
|
| deklarierte Behavior-Ports | 49 | 35 |
|
|
| Variablen im Master | 18 | 13 |
|
|
| Master-Graph | 84 Knoten / 94 Kanten | 44 / 64 |
|
|
| `next-move` | 44 Knoten | 30 |
|
|
| `clear-lines` | 29 Knoten | 23 |
|
|
|
|
`piece` und der Kandidat `next` sind Structs aus shape/rot/x/y. `Next move` gibt einen ganzen
|
|
Kandidaten zurück, `Fits` beurteilt ihn, der Master übernimmt ihn mit *einem* `Set variable`.
|
|
Jeder Bewegungsstrang packt nur das Feld, das er ändert (`Pack{rot}`) — „dasselbe Teil, eine Reihe
|
|
tiefer" ist dadurch wörtlich ein Feld.
|
|
|
|
## Zwei Verträge, an denen die Zusammenlegung hängt
|
|
|
|
Beide gemessen, beide als Unit-Test gepinnt (`tests/core/variable-ports.test.ts`), beide mit
|
|
Gegenprobe abgesichert:
|
|
|
|
- **`Set variables` schreibt je Port mit `writeTyped`**, genau wie der Einzelknoten. Ein
|
|
`Pack{rot}` wird deshalb weiterhin per `mergeStruct` eingemischt statt das Bündel zu
|
|
überschreiben. Ohne das hätte Drehen die anderen drei Felder gelöscht.
|
|
- **Ein Datenknoten wird einmal pro Puls ausgewertet und behält die Antwort** (`dataCache`,
|
|
geleert je Schleifenrunde). Ein `Get variables` darf deshalb keine Schreibung einer seiner
|
|
Variablen überspannen — sonst liest der spätere Verbraucher den Wert von davor. Deshalb sind
|
|
`grid`/`outgrid` in `clear-lines` weiterhin zwei Knoten, und `take`/`keep` bleiben getrennt.
|
|
|
|
Die Cache-Regel ist im **Spiel** nicht messbar: eine absichtliche Verletzung ließ den Smoke grün,
|
|
weil `OnUpdate` jeden Frame neu liest und der Fehler sich einen Frame später selbst heilt. Der
|
|
Unit-Test fällt dagegen sauber (`py: 7` statt `1`).
|
|
|
|
## Was bewusst nicht passiert ist
|
|
|
|
Das **Brett bleibt eine flache Zahlenliste**. `-1`/Formnummer ist bereits die kompakte Kodierung;
|
|
ein `{shape}`-Struct pro Zelle wären 200 Bündel für ein einziges Feld.
|
|
|
|
Eine **Listen-Variable kann ihre Item-Art im Editor nicht wählen** — eine Liste aus Structs
|
|
entsteht heute nur aus einer Projektdatei. Steht in `struct-und-listen-restposten.md`.
|