A Tetris example project needs to draw 200 board cells and scan rows for a full line. Neither is expressible without iteration, so the node system gets a Repeat node plus the missing text side: Substring, Find text and To number. The interpreter drops its data cache once per round -- otherwise every round would recompute with the first round's values. Object by name takes its name as an input now, not just as a setting; addressing "c" + index is impossible otherwise. An unconnected input reads the stored setting, so saved projects are untouched. The board itself is a 200-character text, "." for empty. That makes both operations of Tetris a single expression instead of a loop, and each frame attempts exactly one move -- rotate beats sideways beats fall -- so the fit check exists once in the whole graph. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.9 KiB
Nächstes spiel: Tetris
Ganz simpel halten, also wirklich nur das nötigste:
- die möglichen shapes die es halt so gibt
- ein zufallspawn eines dieser shapes das langsam nach unten geht
- links/rechts pfeilstasten um zu bewegen, pfeil rauf um im uhrzeigsinn zu drehen, pfeil runter gedrückt halten um nach unten zu beschleunigen
- eine line gefüllte line solved
- obere grenze = game over, ansonsten ist es einfach ein endlosspiel
- counter ui der einfach nur die gesolved lines zählt
was wir nicht brauchen:
- "levels", sprich dass shapes schneller werden beim runterfallen
- titlescreen oder gameover screen, wenn gameover einfach spiel stoppen
- animationen, spezielle texturen, oder sonst irgendwelchen schnick schnack
- e2e erweiterung zu dem projekt => dieses projekt braucht keine speziellen e2e tests die man immer wieder durchlaufen muss
habe ich irgendwas vergessen? wenn ja, rückfragen, ansonsten einfach project seed anlegen und projekt generieren
falls nodes fehlen, oder praktische nodes sinnvoll wären die noch fehlen, dürfen diese natürlich erstellt werden
Umgesetzt (2026-09-09). Projekt Tetris (2072b56583af), Seed tools/seed-tetris.mjs,
gespielt von tools/smoke-tetris.mjs. Alles aus deiner Liste ist drin, nichts davon, was du
ausgeschlossen hast: keine Level, kein Titel- oder Game-Over-Screen (Game Over stoppt das Spiel
per Stop game), keine Animationen, keine Texturen, und keine Erweiterung der E2E-Suite —
der Smoke-Lauf ist wie beim Jump-and-Run ein eigenes Skript, das nur laeuft, wenn dieses Projekt
oder die Engine darunter sich aendert.
Das Brett ist ein Text. 200 Zeichen, eins pro Zelle: . fuer leer, sonst die Ziffer der Form,
die dort liegengeblieben ist. Damit sind die beiden Operationen, auf denen Tetris steht, je ein
einziger Ausdruck statt einer Schleife: eine Zelle ist Substring(grid, zeile·10 + spalte, 1),
und eine volle Reihe zu loesen ist eine leere Reihe, vor alles darueber und darunter gejoint.
Gezeichnet wird aus einem Vorrat benannter Objekte — c0…c199 fuer das Brett, p0…p3 fuer
den fallenden Stein. Waehrend des Spielens wird nichts gespawnt und nichts zerstoert.
Vier neue Nodes, alle allgemein und nicht Tetris-eigen (danach gefragt, du hast (a) gewaehlt):
Repeat (flow) |
times → body / index / done. Der Interpreter kann seitdem Schleifen: er verwirft den Datencache pro Runde, sonst rechnete jede Runde mit den Werten der ersten. |
Substring |
count Zeichen ab start; negatives count reicht bis zum Ende |
Find text |
wo ein Text zuerst vorkommt, −1 wenn nicht — damit ist "Reihe voll" = kein Punkt darin |
To number |
die fehlende Gegenrichtung zu To text |
Dazu eine Aenderung an einem vorhandenen Node: Object by name nimmt den Namen jetzt als
Eingang statt nur als Einstellung. Ohne das waeren 200 Zellen nicht adressierbar — die Zeichen-
schleife baut "c" + index. Alte Projekte sind unberuehrt: ein gespeicherter Name liegt in
params.name, und genau von dort liest ein unverbundener Eingang (eigener Test dafuer).
Der Kunstgriff, der den Graphen klein haelt: jeder Frame versucht genau einen Zug —
Drehen schlaegt Seitwaerts schlaegt Fallen. Die Tasten bewegen nichts selbst, sie hinterlassen
einen Wunsch (wantDx, wantRot), den der naechste Frame einloest. Dadurch gibt es die
Pruefung "passen die vier Zellen dort hin?" einmal im ganzen Graphen statt einmal pro Taste.
Ein um einen Frame verzoegerter Seitwaertsschritt ist bei 60 fps unsichtbar; drei Kopien
derselben 30 Nodes waeren es nicht.
Wie das Landen ins Brett kommt: der gelandete Stein wird nicht noch einmal aus der Formtabelle
ausgerechnet, sondern von seinen vier Zellobjekten abgelesen (Get position → Spalte/Zeile).
Sie stehen genau dort, wo der Stein ist — das Zeichnen ist die einzige Stelle, die sie anfasst.
Belegt — tools/smoke-tetris.mjs, remote im echten Browser: ALL PASSED, 17 Checks.
PASS the piece falls on its own – row 1 → 4
PASS the left arrow moves the piece one column – 4,5,4,5 → 3,4,3,4
PASS the up arrow turns the piece – rot 0 → 1
PASS the board draws exactly the cells the board text names – {"lit":8,"inText":8}
PASS a full line disappears and is counted – lines 1, bottom row "....44...."
PASS a piece that cannot fall at the very top stops the game – stopped
Der Brett-Check ist der wichtigste: er haelt den Text gegen die Bildschirmzellen — ohne ihn koennte die Zeichenschleife irgendetwas anzeigen und alles andere trotzdem gruen sein. Fuer die volle Reihe wird die unterste Reihe vorher gefuellt, damit jeder Stein sie vollmacht, egal welcher kommt. Der Game-Over-Check wartet auf die Bedingung statt auf die Uhr — mit fester Wartezeit war er in einem von zwei Laeufen rot, und ein Check, der wuerfelt, ist keiner.
Was ich selbst entschieden habe:
- Kein Wall-Kick. Eine Drehung, die nicht passt, findet nicht statt — sie wird nicht an der Wand vorbeigeschoben. Das ist die simple Regel, und "ganz simpel" war die Vorgabe.
- Farbe pro Form, wie im Original. Kostet drei Nodes: die Ziffer im Bretttext sucht ihre
Farbe aus einem zweiten Text (
colors), acht Ziffern je Farbe. - Drehachse ist die Box der Form (I in 4×4, O in 2×2, Rest 3×3), nicht der Schwerpunkt. Ohne Wall-Kick ist das das Verhalten, das am wenigsten ueberrascht.
- Game Over = ein Stein kann ganz oben (
py ≤ 0) nicht einmal fallen. Kein zweiter Test beim Erscheinen, damit die Pruefung an der einen Stelle bleibt. - Tempo: eine Reihe pro halber Sekunde, gehaltene Pfeil-runter-Taste zehnmal so schnell. Beides
steht als Variable
stepTimeim Inspector.
Was du wissen solltest: der Board-Container traegt 205 Objekte (Wanne + 200 Zellen + 4 Steinzellen). Im Editor sieht man davon nichts — die Szene zeigt acht Objekte, der Rest liegt im Container. Wer ihn aber oeffnet, bekommt eine lange Liste. Das ist der Preis dafuer, dass nichts gespawnt wird.