Tetris

Dieser Text war ursprünglich als weiteres Spielegerüst für das Buch „Spiele programmieren mit Blitz Basic“ von René Meyer vorgesehen. Aus Platz- und Zeitgründen wurden weder das Kapitel noch das Spiel beendet. Der aktuelle Status verdeutlicht aber, wie das Prinzip umgesetzt wird, und läßt sich leicht erweitern.

Kaum ein Spiel wurde häufiger umgesetzt als „Tetris“. Obwohl es erst Mitte der 80er Jahre entstand, also viel später als Pioniere wie „Pong“ und „Space Invaders“, erfüllt es die Voraussetzungen für ein echtes Kultspiel - blitz(basic)schnell zu erlernen und doch lange fesselnd.

Ich rate Ihnen nicht unbedingt, ein „Tetris“-Spiel zu veröffentlichen. Es gibt weit über hundert Versionen allein für den PC, so daß eine Neuerscheinung kaum jemanden interessieren wird. (Falls Sie es doch tun - nennen Sie Ihr Werk nicht „Tetris“; der Name ist geschützt.) Als Fingerübung ist die Spielidee jedoch interessant und leicht umzusetzen.

Die Regeln dürften klar sein: In einen Schacht fallen von oben unregelmäßig geformte Klötzer. Die Aufgabe ist es, sie so zu drehen und nach links und rechts zu bewegen, daß sie komplette Linien ergeben. Ist eine Linie komplett, also durchgängig gefüllt, verschwindet sie.

Es bietet sich an, den Schacht in einem Dim-Feld abzulegen. Er hat eine feste Größe von 10 Feldern waagerecht und 20 Feldern senkrecht:

Dim Schacht(10, 20)

Als Hintergrund laden wir ein Bild:

bild = LoadImage("racetrack.bmp")

ResizeImage bild, 1024, 768

·        Steinformen

In „Tetris“ gibt es sieben verschiedene Steine. Jeder Stein besteht aus vier Quadraten, die jeweils anders angeordnet sind - als langer Strich, als „T“, als „L“, als umgekehrtes „L“, als „S“, als „Z“ und als großes Viereck.

Da wir die Formen der Steine mit dem jeweiligen Zustand des Schachtes abgleichen müssen, um Zusammenstöße und Überschreitungen des Randes zu vermeiden, bietet es sich an, keine fertigen Graphiken zu verwenden, sondern jeden Stein aus seinen vier Quadraten zusammenzusetzen.

Jeder Stein ist maximal vier Quadrate breit und zwei hoch. Um uns das Programmieren zu erleichtern, legen wir die Steine in 4x2 große Areale ab. Für den „T“-Stein etwa so:

0100

1110

Auch hier bietet es sich an, die Form der sieben Steine in einem Dim-Feld abzulegen. Wir wählen dazu ein 7 x 4 x 2 großes Dim-Feld. Wir legen die Formen der Steine in Data-Zeilen ab ...

; Steinformen definieren

Data 0,0,1,0, 1,1,1,0 ; L

Data 1,0,0,0, 1,1,1,0 ; umgekehrtes L

Data 0,1,1,0, 1,1,0,0 ; S

Data 1,1,0,0, 0,1,1,0 ; Z

Data 0,1,0,0, 1,1,1,0 ; T

Data 1,1,0,0, 1,1,0,0 ; Quadrat

Data 1,1,1,1, 0,0,0,0 ; I

... und lesen sie in das Feld ein:

; Steinformen einlesen

Dim Steinform(7, 4, 2)

For X=1 To 7

 Read Steinform(X, 1, 1)

 Read Steinform(X, 2, 1)

 Read Steinform(X, 3, 1)

 Read Steinform(X, 4, 1)

 Read Steinform(X, 1, 2)

 Read Steinform(X, 2, 2)

 Read Steinform(X, 3, 2)

 Read Steinform(X, 4, 2)

Next

(Wir hätten zwei verschachtelte For-Schleifen verwenden können, doch das hätte nur wenig Aufwand gespart - die Arbeit für den Computer sogar erhöht.)

·        Steinfarben

Zum Darstellen der Steine würden wir normalerweise Graphikquadrate einlesen. Da wir einfarbige Steine verwenden, gehen wir anders vor: Wir erstellen die sieben Graphiken zur Laufzeit mit CreateImage(). Dann setzen wir den Bildpuffer nacheinander auf die Graphiken und färben sie ein:

; Farbquadrate erstellen

Dim Stein(7)

For I=1 To 7

 Stein(I) = CreateImage(32, 32)

 SetBuffer ImageBuffer(Stein(I))

 Read R,G,B

 Color R,G,B

 Rect 0, 0, 32, 32, 1

Next

Die Farbcodes lesen wir aus Data-Zeilen:

; Farben der Steine definieren

Data 153,51,204  ; L - violett

Data 255,255,255 ; umgekehrtes L - weiß

Data 0,255,0     ; S - grün

Data 0,255,255   ; Z - hellblau

Data 127,127,0   ; T - olivgrün

Data 0,0,127     ; Quadrat - blau

Data 255,0,0     ; I - Rot

Die Kombination ist nicht sonderlich schön; ich habe mich an dem Originalspiel von 1986 orientiert. Für ein Spiel, das veröffentlicht werden soll, ist das Verwenden von „ordentlichen“ Graphikquadraten unbedingt empfehlenswert.

Mit diesen Vorbereitungen haben wir den Zahlen 1-7 eine Steinform und eine Steinfarbe zugewiesen. Zeit für einen Probelauf.

·        Spielschleife

Wir schreiben eine kleine Spielschleife. Sie druckt das Hintergrundbild und den Rahmen für den Schacht. Na gut, in weiser Voraussicht können wir auch gleich den Inhalt des Schachts drucken:

; Schacht zeichnen

For I=1 To 10

 For J=1 To 20

  If Schacht(I,J) <> 0 Then DrawImage Stein(Schacht(I,J)), 318 + 32*I, 30 + 32*J

 Next

Next

Der ist freilich noch leer, doch wenn wir einige Felder mit Testdaten (1-7) füllen ...

Schacht(10, 20) = 7

... würden, könnten wir an eine beliebige Stelle Quadrate plazieren. So werden wir es später mit den Steinen machen, die unten gelandet sind und sich nicht mehr bewegen.

Ein bißchen Interaktivität fehlt noch. Wir tragen einige Codezeilen probehalber ein. Mit den Tasten [1] bis [7] wählen wir einen Stein aus, ...

A = GetKey()

If (A>48) And (A<56) Then Farbe = A - 48

... der an einer festen Position angezeigt wird.

For I=1 To 4

 If Steinform(Farbe, I, 1) <> 0 Then DrawImage Stein(Farbe), 286 + 32*X + 32*I, 30 + 32*Y

 If Steinform(Farbe, I, 2) <> 0 Then DrawImage Stein(Farbe), 286 + 32*X + 32*I, 62 + 32*Y

Next

Damit bereiten wir uns auf Folgeabschnitte vor:

1. Wir stellen sicher, daß wir die Formen der sieben Steine richtig festgelegt (codiert) haben.

2. Wir stellen sicher, daß die Steine korrekt und an der richtigen Stelle angezeigt werden.

Das ganze Programm:

; TETRIS1.BB

Graphics 1024,768

bild = LoadImage("c:\basic\racetrack.bmp")

ResizeImage bild, 1024, 768

Dim Schacht(10, 20)

 

; Steinformen definieren

Data 0,0,1,0, 1,1,1,0 ; L

Data 1,0,0,0, 1,1,1,0 ; umgekehrtes L

Data 0,1,1,0, 1,1,0,0 ; S

Data 1,1,0,0, 0,1,1,0 ; Z

Data 0,1,0,0, 1,1,1,0 ; T

Data 1,1,0,0, 1,1,0,0 ; Quadrat

Data 1,1,1,1, 0,0,0,0 ; I

 

; Steinformen einlesen

Dim Steinform(7, 4, 2)

For X=1 To 7

 Read Steinform(X, 1, 1)

 Read Steinform(X, 2, 1)

 Read Steinform(X, 3, 1)

 Read Steinform(X, 4, 1)

 Read Steinform(X, 1, 2)

 Read Steinform(X, 2, 2)

 Read Steinform(X, 3, 2)

 Read Steinform(X, 4, 2)

Next

 

; Farben der Steine definieren

Data 153,51,204  ; L - violett

Data 255,255,255 ; umgekehrtes L - weiß

Data 0,255,0     ; S - grün

Data 0,255,255   ; Z - hellblau

Data 127,127,0   ; T - olivgrün

Data 0,0,127     ; Quadrat - blau

Data 255,0,0     ; I - Rot

 

; Farbquadrate erstellen

Dim Stein(7)

For I=1 To 7

 Stein(I) = CreateImage(32, 32)

 SetBuffer ImageBuffer(Stein(I))

 Read R,G,B

 Color R,G,B

 Rect 0, 0, 32, 32, 1

Next

 

SetBuffer BackBuffer()

Farbe = 1 ; Test

X = 1 ; Test

Y = 1 ; Test

 

Repeat ; *** Spielschleife ***

 

 ; Hintergund zeichnen

 Color 0, 0, 30

 DrawImage bild, 0, 0

 Rect 350, 62, 320, 640, 1

 

 ; Schacht zeichnen

 For I=1 To 10

  For J=1 To 20

   If Schacht(I,J) <> 0 Then DrawImage Stein(Schacht(I,J)), 318 + 32*I, 30 + 32*J

  Next

 Next

 

 ;Test = Stein per Tastendruck festlegen

 A = GetKey()

 If (A>48) And (A<56) Then Farbe = A - 48

 

 ; Stein zeichnen

 For I=1 To 4

  If Steinform(Farbe, I, 1) <> 0 Then DrawImage Stein(Farbe), 286 + 32*X + 32*I, 30 + 32*Y

  If Steinform(Farbe, I, 2) <> 0 Then DrawImage Stein(Farbe), 286 + 32*X + 32*I, 62 + 32*Y

 Next

 

 Flip

Until KeyHit(1)

·        Nächsten Stein ausdenken

Zu Spielbeginn muß ein Stein „erstellt“ werden. Aber auch, wenn ein Stein unten gelandet ist, muß ein neuer erscheinen. Wir führen die Variable Aktiv ein. Sie merkt sich, ob und welcher Stein derzeit im Rennen ist. Hat sie den Wert 0, muß ein Stein erzeugt werden. Es liegt nahe, eine zufällige Auswahl zu treffen. Doch beim Originalspiel erscheinen manche Steine mehr und manche weniger häufig. Michael Gonzales, der Verfasser einer „Tetris FAQ“, hat's mühsam gezählt:

Stein

Code

Häufigkeit

L

1

11%

umgekehrtes L

2

11%

S

3

19%

Z

4

10%

T

5

19%

Quadrat

6

18%

I

7

12%

(Übrigens: Die FAQ enthält auch eine genaue Aufschlüsselung der Punkteverteilung, um die wir uns hier nicht kümmern. Der Text ist unter anderem auf der Seite www.mogelpower.de zu finden.)

Das lösen wir auf diese Weise:

; Stein "ausdenken"

If Aktiv = 0 Then

 Zufall = Rand(1,100)

 Aktiv = 1

 If Zufall > 11 Then Aktiv = 2

 If Zufall > 11 + 11 Then Aktiv = 3

 If Zufall > 11 + 11 + 19 Then Aktiv = 4

 If Zufall > 11 + 11 + 19 + 10 Then Aktiv = 5

 If Zufall > 11 + 11 + 19 + 10 + 19 Then Aktiv = 6

 If Zufall > 11 + 11 + 19 + 10 + 19 + 18 Then Aktiv = 7

EndIf

Solche Tests sollten getrost in einem separaten Programm ablaufen (PROZENTTEST.BB), um sicherzustellen, daß die Auswahl wirklich korrekt arbeitet. Nichts ist peinlicher, als ein „Tetris“-Spiel zu veröffentlichen, bei dem wegen eines Fehlers ein Stein nie erscheint.

Als weiteren Test lassen wir im Sekundentakt einen Stein am oberen Rand erscheinen:

Farbe = Aktiv ; Test

Delay 1000 ; Test

Aktiv = 0 ; Test

 

 

Folgende Schritte müssen noch programmiert werden:

1.     Drehen der Steine auf Tastendruck. Dafür ist es empfehlenswert, das Aussehen jeder der vier Positionen in Data-Zeilen zu speichern. Beim Drehen ist eine Kollision mit dem Rand und mit anderen Steinen zu vermeiden.

2.    Herunterfallen des aktuellen Steins. Ist er unten angekommen, folgt der nächste. Die Kollision mit den Steinen am Boden sollte bereits in Schritt 1 verhindert worden sein.

3.     Das Überprüfen und Löschen vollständiger Linien. Eine For-Schleife testet jede Linie, ob alle Felder besetzt sind. Ist das der Fall, werden alle darüberliegenden Linien um eine Zeile verschoben.