Tetris example game #15

Closed
opened 2026-09-12 15:03:52 +02:00 by admin · 0 comments

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 stepTime im 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.


Übernommen aus todo-notes/done/tetris-example-game.md beim Umzug auf das Issue-System (2026-09-12).

  • Angelegt: 2026-09-09 · 4f78c75 — Give the graph a loop, and build Tetris with it
  • Abgeschlossen: 2026-09-09 · im selben Commit 4f78c75
  • Gemeldet von: admin
## 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 `stepTime` im 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. --- *Übernommen aus `todo-notes/done/tetris-example-game.md` beim Umzug auf das Issue-System (2026-09-12).* - **Angelegt:** 2026-09-09 · `4f78c75` — Give the graph a loop, and build Tetris with it - **Abgeschlossen:** 2026-09-09 · im selben Commit `4f78c75` - **Gemeldet von:** admin
admin added the todo-note label 2026-09-12 15:03:52 +02:00
admin closed this issue 2026-09-12 15:03:52 +02:00
admin added this to the Development project 2026-09-13 13:43:26 +02:00
admin moved this to Done in Development on 2026-09-13 13:44:28 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#15