Kiste
EN

Kistris

Ein vollständiges Tetris — sieben Steine mit Wall-Kick, Ghost-Piece, Level-Tempo, Sound, Musik und einer Bestenliste mit Namen. Alles in einer einzigen Kiste-Datei, ohne Spiel-Engine.

Kistris im Spiel

Kostenlos selber bauen

Kistris ist quelloffen. Lade den Quelltext, öffne ihn in der Kiste-IDE (oder bau ihn auf der Kommandozeile mit kiste build kistris.ki) — es ist derselbe Code, aus dem das fertige Spiel entsteht. Die fünf Klänge und das Titelbild liegen bei.

Lieber Schritt für Schritt mitbauen und dabei Kiste von Grund auf lernen? Dafür gibt es ein ganzes Buch: Kiste lernen mit Kistris.

So gebaut

Kistris ist ein reines Kiste-Programm. Es nutzt nur die eingebauten Module gui (Fenster, Zeichnen, Tasten), klang, liste, zufall, wert, datei und text — keine fremde Spiel-Engine. Hier sind die interessantesten Stellen.

1 · Das Spielfeld ist eine flache Liste

Statt eines zweidimensionalen Rasters führt Kistris das ganze Feld als eine lange Liste mit 200 Zellen. Die Zelle an Spalte x, Zeile y liegt an Index y · BREITE + x. Zwei kleine Funktionen verstecken diese Rechnung — dahinter fühlt es sich an wie ein Gitter.

// Das Spielfeld ist EINE flache Liste, kein 2-D-Raster.
// Zelle (x, y) liegt an Index y * BREITE + x.
funktion feld_hole(x, y) {
    gib wert.als_ganz(feld[y * BREITE + x])
}
funktion feld_setze(x, y, w) {
    feld[y * BREITE + x] = w
}

2 · Sieben Steine aus einer Tabelle

Jeder der sieben Steine hat vier Drehlagen. Statt sie zur Laufzeit zu rotieren, stehen alle Formen fertig in Tabellen — vier Rotationen, je vier Blöcke als (x, y)-Paare relativ zum Drehpunkt. form_wert ist der Nachschlage-Dienst.

// Pro Steintyp eine flache Liste: 4 Rotationen x 4 Bloecke x (x, y).
// Rotation im Uhrzeigersinn ist in der Tabelle schon fertig hinterlegt.
funktion form_wert(t, r, k) {
    nimm idx = r * 8 + k
    wenn t == 1 { gib FORM_I[idx] }   // I-Stein
    wenn t == 2 { gib FORM_O[idx] }   // O-Stein
    wenn t == 3 { gib FORM_T[idx] }   // T-Stein
    // … 4=S · 5=Z · 6=J
    gib FORM_L[idx]                   // L-Stein
}

3 · Eine Frage entscheidet alles: passt der Stein?

Bewegen, Drehen, Fallen, Game-Over — alles hängt an einer einzigen Prüfung: Kollidiert der Stein in Lage r an Position (bx, by) mit Wand, Boden oder einem liegenden Stein? Die Rotation mit „Wall-Kick" probiert einfach dieselbe Frage ein paar Felder versetzt noch einmal.

// Eine einzige Frage entscheidet alles: Passt der Stein hierhin?
funktion kollidiert(t, r, bx, by) {
    für k = 0 bis 3 {
        nimm x = bx + form_wert(t, r, k * 2)
        nimm y = by + form_wert(t, r, k * 2 + 1)
        wenn x < 0 oder x >= BREITE { gib 1 }   // Wand
        wenn y >= HÖHE { gib 1 }                // Boden
        wenn y >= 0 {
            wenn feld_hole(x, y) != 0 { gib 1 } // anderer Stein
        }
    }
    gib 0
}

4 · Reihen löschen ohne Verschieben

Der schönste Trick: Eine volle Reihe wird nicht mühsam „nach unten kopiert". Weil das Feld eine flache Liste ist, schneiden wir die zehn Zellen der Reihe heraus und stellen zehn Nullen vorne an — schon rutscht der ganze Stapel darüber eine Zeile nach unten.

// Volle Reihe loeschen: die 10 Zellen herausschneiden und 10 Nullen
// VORANSTELLEN — alles darueber rutscht so automatisch eine Zeile nach unten.
für k = 1 bis BREITE { liste.entferne(feld, y * BREITE) }
für k = 1 bis BREITE { liste.stelle_voran(feld, 0) }

5 · Fairer Zufall: der 7-Bag

Echtes Tetris würfelt die Steine nicht rein zufällig. Eine „Tüte" enthält jeden der sieben Typen genau einmal in zufälliger Reihenfolge; ist sie leer, wird neu gefüllt. So gibt es nie zehnmal denselben Stein und nie ewiges Warten auf die lange Stange.

// Der 7-Bag: eine "Tuete" mit den Typen 1-7 in zufaelliger Reihenfolge.
// Fairer als reiner Zufall — nie 10x dasselbe Teil, nie ewige I-Duerre.
funktion fülle_beutel() {
    nimm kandidaten = [1, 2, 3, 4, 5, 6, 7]
    solange liste.länge(kandidaten) > 0 {
        nimm z = zufall.ganz(0, liste.länge(kandidaten) - 1)
        liste.hänge_an(beutel, wert.als_ganz(kandidaten[z]))
        liste.entferne(kandidaten, z)
    }
}

6 · Bestenliste mit Namen — nach dem Spiel getippt

Die besten Ergebnisse landen in einer Textdatei und überleben so den Neustart. Den Namen tippt der Spieler wie an alten Arcade-Automaten nach der Partie direkt auf den Bildschirm — kein Eingabefeld, das mitten im Spiel die Tasten wegschnappt. Der Tasten-Handler baut den Namen Buchstabe für Buchstabe selbst zusammen.

// Der Name wird NACH dem Spiel getippt — kein Eingabefeld, das
// waehrend des Spiels die Tasten wegfrisst. Der Handler liest selbst:
wenn taste == "eingabe" {
    speichern()                                   // Enter traegt ein
} sonstwenn taste == "BackSpace" {
    eingabe_name = text.erste_n(eingabe_name, länge(eingabe_name) - 1)
} sonstwenn länge(taste) == 1 {
    wenn länge(eingabe_name) < 14 { eingabe_name = "{eingabe_name}{taste}" }
}

Das ist das ganze Rezept: eine flache Liste, ein paar Tabellen, eine Kollisionsfrage und ein Zeitgeber. Der komplette Quelltext liegt dem Download oben bei — bau ihn, ändere die Farben, erfinde deinen eigenen Stein.