3. Schleifen und
Sprünge
Bis jetzt können Sie
Ihre Programme nur Schritt für Schritt abarbeiten, und um eine Anweisung z.B. 100-mal
auszuführen, müssten Sie diese auch 100-mal eintippen. Dass dies sehr
ineffizient ist, dürfte wahrscheinlich bereits klar sein. Besser ist es
sicherlich, 100-mal zu einem Ausgangsunkt zurückspringen zu können, solange
eine bestimmte Bedingung erfüllt ist, wie z.B. ein Zähler, der noch nicht einen
bestimmten Wert erreicht hat.
3.1 Bedingte Sprünge
mit FOR…NEXT und IF…THEN
Diese sogenannte bedingte
Ausführung von Anweisungen gibt es in BASIC in Form von FOR und IF.
Der Befehl FOR hat hier das folgende Format:
FOR
[Zählvariable]=[Startwert] TO [Endwert] STEP [Schrittweite]
… Anweisungen …
NEXT [Zählvariable]
Für die Zählvariable
kann ein beliebiger Variablenname eingesetzt werden, außer natürlich ein String
(dieser kann keine Zahl enthalten). Der Startwert wird nun immer dann
gesetzt, wenn FOR zum ersten Mal aufgerufen wird, und der Endwert
ist dann der Wert, den der Zähler maximal erreichen muss, bevor BASIC die
Schleife wieder verlässt. Mit STEP kann auch optional noch eine
Schrittweite definiert werden.
Nach Erreichen des FOR-Schleifenkopfes
führt BASIC dann wieder ganz normal Zeile für Zeile aus, diese Anweisungen, die
FOR folgen, nennt man auch den Schleifenkörper. Dieser
Schleifenkörper kann durchaus auch sehr viele Anweisungen enthalten, die auch
in mehreren Zeilen stehen können. Trifft BASIC allerdings auf NEXT, wird
die Zählvariable, die hinter NEXT steht, um den zuvor gespeicherten STEP-Wert
erhöht (wird STEP weggelassen, ist dieser Wert 1). Anschließend schaut
BASIC nach, ob die hinter NEXT angegebene Zählvariable den zuvor bei FOR
angegebenen Endwert erreicht. Ist dies nicht der Fall, springt BASIC in die
Zeile direkt hinter dem letzten FOR zurück. Ansonsten wird die Schleife
verlassen und das Programm fährt mit der Zeile fort, die hinter NEXT
steht. Stimmt die nach NEXT angegebene Zählvariable nicht mit dem zuvor bei FOR
angegebenen Variablennamen überein, wird eine entsprechende Fehlermeldung,
nämlich
?NEXT WITHOUT FOR
ERROR
ausgegeben.
Schleifen
können auch ineinander geschachtelt werden, allerdings dürfen sich die
Zählvariablen nicht überkreuzen. Folgendes ist also nicht erlaubt:
10 FOR
I=1 TO 10
20 FOR
J=1 TO 10
30 PRINT “I=“;I;“J=“;J
40 NEXT J
50
NEXT I
Allerdings ist folgende
Schachtelung erlaubt:
10 FOR I=1 TO 10
20 FOR J=1 TO 10
30 PRINT “I=“;I;“J=“;J
40 NEXT J
50
NEXT I
In dem letzten Beispiel
wird immer die innere Schleife 10-mal ausgeführt, bevor die äußere Schleife
erneut (auch insgesamt 10-mal) ausgeführt wird, das ergibt dann also 100
Durchläufe, und nicht 20! Wenn Sie allerdings die Ausführung einer bestimmten
Anweisung von einer (durchaus auch komplexen) Bedingung abhängig machen wollen,
dann ist FOR der falsche Weg und Sie müssen IF benutzen:
IF [Bedingung] THEN
[Anweisung]
[Bedingung] ist hier
ein beliebiger BASIC-Ausdruck, der durchaus auch mathematisch sein kann (also
z.B. eine Berechnung enthalten kann). Dieser Ausdruck muss mathematisch korrekt
sein, aber der Ausdruck muss nicht logisch wahr sein. Logisch wahr ist
z.B. der folgende Ausdruck:
1+2+3=6
Logisch falsch ist dagegen der
folgende Ausdruck, falls A=1 ist:
A=1+2+3
IF prüft nun den
Ausdruck, der hinter IF steht darauf, ob er logisch wahr ist. Ist
er dies, wird die Anweisung ausgeführt, die hinter THEN steht (fehlt das
THEN, erscheint SYNTAX ERROR). Ansonsten fährt BASIC in der
nächsten Zeile fort. Leider ist C64 sehr beschränkt in Bezug auf IF und THEN,
und es gibt z.B. folgendes Konstrukt nicht, das es bei anderen BASIC-Versionen
durchaus auch auf „alten Maschinen“ gibt:
IF [Bedingung]
… Anweisungen ….
ENDIF
Das heißt, IF
besitzt nicht so etwas wie einen „Schleifenkörper“, und dies kann in der Tat zu
„Spaghetticode“ in der folgenden Weise Führen:
IF … GOTO/GOSUB …
IF … GOTO/GOSUB …
…
IF …
GOTO/GOSUB …
3.1.1 Der ON-Befehl
Eine kleine Abkürzung
können Sie durch den ON-Befehl erreichen, der das folgende Format hat:
ON [Variable]
GOTO/GOSUB [Zeile 1],…,[Zeile n]
ON betrachtet die hier
angegebene Variable als Zähler und schaut nach, ob der Wert des Zählers zu der
Aufzählung der Programmzeilen passt, die hinter GOTO bzw. GOSUB
steht. Angefangen wird bei 0. Wenn also der Zähler 0 ist, wird der GOTO/GOSUB-Befehl
mit der Zeile ausgeführt, den Sie als erstes angegeben haben, wenn der Zähler 1
ist, wird der GOTO/GOSUB-Befehl mit der Zeile ausgeführt, den Sie
als zweites angegeben haben.
3.1.2 Logische
Verknüpfungen mit AND,
OR und NOT
Mit IF können
auch mehrere Bedingungen gleichzeitig geprüft werden. Hierzu dienen die
Operatoren AND (und), OR (oder) und NOT (nicht). Werden
nach IF mehrere Ausdrücke durch AND verknüpft, so muss jeder
einzelne Ausdruck logisch wahr sein, damit die Anweisung, die zum IF
gehört ausgeführt wird. So ist z.B. folgende Aussage logisch wahr:
A=10
IF
A>5 AND A>9 THEN PRINT”A IST GROSS GENUG”
Allerdings wird die
Anweisung nach IF nicht ausgeführt, wenn A=7 ist. Werden dagegen
nach IF mehrere Ausdrücke durch OR verknüpft, so muss mindestens
einer der Ausdrücke logisch wahr sein. Das heißt, wenn A=10 ist, dann
ist der folgende Ausdruck logisch wahr, und die PRINT-Anweisung wird
ausgeführt:
IF
A<5 OR A>9 THEN PRINT”A HAT EINEN KORREKTEN WERT”
A darf hier also keine
Werte im Intervall [5,9] annehmen. Wenn Sie einen Ausdruck zusätzlich negieren
wollen, dann müssen Sie ein NOT voranstellen. So entspricht der folgende
Ausdruck dem vorigen:
IF NOT
(A>=5 AND A<=9) THEN PRINT”A HAT EINEN KORREKTEN WERT”
Also können AND,
OR und NOT beliebig gemischt, und Ausdrücke auch durch Klammern
gekapselt werden. Allerdings müssen sämtliche Ausdrücke mathematisch und
syntaktisch korrekt sein, und eine BASIC-Zeile darf nicht mehr als 20 geöffnete
Klammern enthalten.
3.1.3 Logische
Ausdrücke im Vergleich zu Bit-Operationen
AND und OR sind in
BASIC leider doppelt belegt. Im Zusammenhang mit IF verknüpfen AND
und OR logische Aussagen. So ist z.B. der Ausdruck
IF
3<2 THEN PRINT“HALLO“
logisch falsch, und der
Text „HALLO“ wird nicht ausgegeben. Außerhalb von IF…THEN…-Konstrukten
besitzen jedoch AND und OR eine andere Bedeutung: Es werden nun
die einzelnen Bits der entsprechenden Zahlen, die sich hinter den einzelnen
Ausdrücken verbergen, miteinander verknüpft. In der BASIC-Befehlsreferenz wird
noch ausgiebig auf logische Operationen und Bitoperationen eingegangen. Nur so
viel vorab: Die Bitoperation AND verknüpft die einzelnen Bits zweier
Zahlen n und m derart, dass das Bit im Ergebnis immer dann gesetzt (=1)
ist, wenn dasselbe Bit sowohl bei n, als auch bei m gesetzt ist.
Die Bitoperation OR verknüpft die einzelnen Bits zweier Zahlen n und m
derart, dass das Bit im Ergebnis immer dann gesetzt (=1) ist, wenn dasselbe
Bit entweder bei n, oder bei m, oder bei n und m gesetzt ist. Leider gibt
es beim C64-BASIC keine XOR-Operation, die bewirkt, dass ein gesetztes
Bit bei n und m zu einem gelöschten Bit im Ergebnis führt.
NOT entspricht auch bei
den Bits einem „nicht“, allerdings bezogen auf die einzelnen Bits. Das heißt, NOT
1234 dreht sämtliche Bits der Zahl 1234 um. Allerdings erhalten Sie bei NOT
1234 das Ergebnis -1235, was daran liegt, dass das oberste Bit einer
Zahl stets als Vorzeichen-Bit gewertet wird, zumindest, wenn Sie keine
zusätzlichen Operatoren benutzen, die dies verhindern.
Für die Bit-Operationen
gilt, dass jeder Ausdruck wahr ist, der nicht das Ergebnis 0 hat.
3.2 Nichtbedingte
direkte Sprünge mit GOTO und GOSUB
Nichtbedingte Sprünge,
auch als direkte Sprünge bezeichnet, sind nicht abhängig von einer
Bedingung und werden immer sofort ausgeführt. Sprünge in BASIC springen
stets zu einer bestimmten Programmzeile und setzen auch die Ausführung direkt
dort fort. Wollen Sie zu einer bestimmten Programmzeile springen, müssen Sie GOTO
benutzen:
GOTO [Programmzeile]
GOTO kann durchaus auch
(und so wird es auch häufig so benutzt) direkt hinter THEN stehen- in
diesem Fall kann dann auch GOTO (aber nur zusammen mit IF…THEN…)
für einen bedingten Sprung „missbraucht“ werden. GOTO alleine springt
aber immer ohne Wenn und Aber.
Wenn Sie dagegen GOSUB
verwenden (das Sie für bedingte Sprünge auch wieder direkt hinter THEN
stellen können), merkt sich BASIC vorher die Stelle, von der aus Sie gesprungen
sind. Mit GOSUB rufen Sie also Unterprogramme auf, die bestimmte
häufig benötigte Routinen enthalten, wie z.B. die Anzeige der Bestenliste oder
des aktuellen Punktestands. Unterprogramme können durchaus sehr viele
BASIC-Zeilen enthalten, müssen aber stets mit RETURN beendet werden. Im
Endeffekt entspricht RETURN dem C-Pendant return,
allerdings benutzt das C64-BASIC keine lokalen Variablen und kann auch keine
Parameter zurückgeben. Wenn also ein Unterprogramm Variablen verändert, dann
ist diese Änderung auch außerhalb des Unterprogramms sichtbar.
3.3 Beispiele
Weil Sie jetzt
Variablen, Arrays, bedingte und direkte Sprünge beherrschen, und auch schon
Schleifen programmieren können, wird es jetzt höchste Zeit für Beispiele, die
diese Konstrukte auch benutzen. Diese Beispiele sind noch einfach gehalten, tun
aber schon etwas Sinnvolles, das Sie später dann auch weiterverwenden können.
3.3.1 Laufschrift 1
04-LAUFSCHRIFT 1
10
POKE 53280,0:POKE 53281,0
20
PRINT"[WHT][SHIFT+CLR/HOME]"
30
A$="…………………………………." (die 40 Punkte sind hier
Leerzeichen)
40
A$=A$+"EINE LAUFSCHRIFT ZU PROGRAMMIEREN IST GARNICHT SO SCHWER."
50
A$=A$+" MAN MUSS NUR MIT DER MID$-FUNKTION RICHTIG UMGEHEN KOENNEN."
80
A$=A$+"…………………………………." (die 40 Punkte sind
hier Leerzeichen)
90 J=LEN(A$)-40
100 FOR I=1 TO J
110 PRINT"[CLR/HOME][12*Cursor
unten]";
120 PRINT MID$(A$,I,40)
130 FOR K=1 TO 100:NEXT
K
140
NEXT I
150
GOTO 100
Zeile 10 stellt erst einmal mit
POKE einen schwarzen Bildschirm ein. Damit man die Laufschrift später
gut lesen kann, wird die Farbe in Zeile 20 auf Weiß gestellt. Allerdings
muss nach dem Farbwechsel der Schrift der Bildschirm gelöscht werden, damit
nachher auch sämtliche Zeichen Weiß sind. In den Zeilen 30-80 wird nun
der String A$ definiert, der die Laufschrift enthält. Allerdings ist A$
mehr als 80 Zeichen lang, deshalb muss A$ dann auch stückweise
zusammengesetzt werden: Erst 40 Leerzeichen, dann der eigentliche Text, und
dann wieder 40 Leerzeichen. Die Laufschrift selbst wird dann in einer FOR-Schleife
(Zeile 100-140) angezeigt, allerdings muss vorher der Zähler J
auf die Position des ersten Zeichens in A$ gesetzt werden, das kein
Leerzeichen enthält. Dies kann mit der LEN-Funktion erreicht werden: LEN
liefert hier die Länge von A$ zurück, und von dieser wird nachher 40
abgezogen.
Die Schleife, die die
Laufschrift darstellt, zählt nun von 1 bis J (=LEN(A$)-40). Innerhalb der Schleife wird nun zunächst der
Cursor zurück an den Anfang der 12. Bildschirmzeile gesetzt (per PRINT-Anweisung
in Zeile 110). Nun wird die Laufschrift in Zeile 120 ausgegeben,
und zwar so: Es werden mit der MID$-Funktion ab der Position I 40
Zeichen von A$ ausgegeben, und dies ist immer genau eine ganze Zeile.
Dadurch werden die schon vorhandenen Zeichen gelöscht. Auf diese Weise erklärt
sich auch der „Vorlauf“ und „Nachlauf“ von jeweils 40 Zeichen in A$: Der
eigentliche Laufschrift-Text muss den Bildschirm erst vollständig auf der
linken Seite verlassen, ehe er rechts wieder hineinlaufen kann. Bevor aber die
Schleife in Zeile 140 verlassen werden kann, die die Laufschrift
anzeigt, muss in Zeile 130 noch eine kleine Schleife eingefügt werden,
die eigentlich nichts tut, außer den Zähler J 100-mal hochzuzählen.
Diese klassische Form der Warteschleife sorgt dafür, dass Sie den Text auch
wirklich lesen können, ohne dass dieser sehr schnell „vorbeiflimmert“.
Wenn die gesamte
Schrift „durchgelaufen“ ist, springt das Programm in Zeile 150 wieder
zurück zu Zeile 100 und zeigt dadurch die Laufschrift erneut an. Leider
führt dies dazu, dass Sie das Programm nur noch mit RUN/STOP
unterbrechen können.
3.3.2 Laufschrift 2
05-LAUFSCHRIFT 2
10
POKE 53280,0:POKE 53281,0
20
PRINT"[WHT][SHIFT+CLR/HOME]"
30
A$="…………………………………." (die 40 Punkte sind hier
Leerzeichen)
40
A$=A$+"EINE LAUFSCHRIFT ZU PROGRAMMIEREN IST GARNICHT SO SCHWER."
50
A$=A$+" MAN MUSS NUR MIT DER MID$-FUNKTION RICHTIG UMGEHEN KOENNEN."
80
A$=A$+"…………………………………." (die 40 Punkte sind
hier Leerzeichen)
90 J=LEN(A$)-40
100 FOR I=1 TO J
110 PRINT"[CLR/HOME][12*Cursor
unten]";
120 PRINT MID$(A$,I,40)
130 FOR K=1 TO 20
140 GET K$:IF
K$<>"" THEN GOTO 190
150 NEXT K
160 GET K$:IF
K$<>"" THEN GOTO 190
170 NEXT I
180 GOTO 100
190 PRINT"[COMMODORE+BLU][SHIFT+CLR/HOME]"
200
POKE 53280,14:POKE 53281,6
Das Listing LAUFSCHRIFT
2 ist identisch mit dem Listing LAUFSCHRIFT 1. Allerdings wurde
innerhalb der Schleife, die die Laufschrift darstellt, noch eine GET-Anweisung
eingefügt. GET schaut nach, ob gerade eine Taste gedrückt ist. Ist dies
der Fall, wird diese Taste an den String K$ als ASCII-Zeichen
weitergereicht. Ist keine Taste gedrückt, wird an K$ ein leerer String
zurückgegeben. Deshalb wird nun innerhalb der Warteschleife (Zähler K)
zusätzlich GET K$ ausgeführt, und wenn K$ (K ist hier die
Abkürzung von „keyboard“) nicht leer ist (A$<>““),
dann wird der entsprechende GOTO-Befehl zu Zeile 190 ausgeführt.
Aber auch direkt vor NEXT I (Zeile 170) wird noch einmal
nachgeschaut, ob nicht doch eine Taste gedrückt wurde.
Wurde nun eine Taste
gedrückt, fährt das Programm mit Zeile 190 und 200 fort, und beendet
sich dann. Zeile 190 und 200 stellen den Bildschirm wieder auf die
normalen Farben ein und löschen anschließend den Bildschirm. Auf diese Weise
kann das Programm LAUFSCHRIFT 2 im Gegensatz zum Programm LAUFSCHRIFT
1 vom Benutzer beendet werden.
3.3.3 Laufschrift 3
06-LAUFSCHRIFT 3
10 POKE 53280,0:POKE
53281,0
20 PRINT"[WHT][SHIFT+CLR/HOME]":
F$="[CTL+BLK][CTL+WHT][CTL+RED][CTL+CYN][CTL+PUR][CTL+GRN][CTL+BLU][CTR+YEL][CMD+BLK][CMD+WHT][CMD+RED]
[CMD+CYN][CMD+PUR][CMD+GRN][CMD+BLU][CMD+YEL]”:CO=0
(CTL=CTRL,CMD=COMMODORE)
30
A$="…………………………………."
40
A$=A$+"EINE LAUFSCHRIFT ZU PROGRAMMIEREN IST GARNICHT SO SCHWER."
50
A$=A$+" AUCH ZUSAETZLICHE EFFEKTE SIND GAR NICHT SO SCHWER
UMZUSETZEN."
80 A$=A$+"…………………………………."
90 J=LEN(A$)-40
100 FOR I=1 TO J
110 PRINT"[CLR/HOME][12*Cursor
unten]";
115 PRINT MID$(F$,CO+1,1);:CO=(CO+1)
AND 15
120 PRINT MID$(A$,I,40)
130 FOR K=1 TO 20
140 GET K$:IF
K$<>"" THEN GOTO 190
150 NEXT K
160 GET K$:IF
K$<>"" THEN GOTO 190
170 NEXT I
180 GOTO 100
190 PRINT"[COMMODORE+BLU][SHIFT+CLR/HOME]"
200 POKE
53280,14:POKE 53281,6
Dieses Listing, das
einen zusätzlichen Farbwechsel der Laufschrift einbaut, ist nun nicht mehr so
schwer zu verstehen, wenn man bedenkt, dass auch Strings (hier F$)
Steuerzeichen (hier für die Textfarbe) enthalten können, die anschließend auch
mit PRINT korrekt ausgegeben werden können (Zeile 115). Der
Zähler, der das entsprechende Farbzeichen auswählt, befindet sich in der
Variable CO (Abkürzung für „color“). CO
wird nach jeder Ausgabe der aktualisierten Laufschrift um 1 erhöht, doch der
zusätzliche AND-Befehl (CO=(CO+1) AND 15) bewirkt, dass CO
nur Werte zwischen 0 und 15 annehmen kann (mehr gültige Farbwerte gibt es auch
nicht auf dem C64).
3.3.4 Labyrinth 1
07-LABYRINTH 1
10 PRINT"[SHIFT+CLR/HOME]BITTE
EINE TASTE DRUECKEN"
20 GET A$:IF
A$="" THEN 20:REM GET-FUNKTION LIEST EINE TASTE
30 IN=RND(0):REM
ZUFALLSGENERATOR NEU INITIALISIEREN
40 PRINT"[WHT][SHIFT+CLR/HOME]":POKE 53280,11:POKE 53281,0
50 FOR
I=1 TO 250:REM BARRIEREN ZEICHNEN
60 X=INT(40*RND(1)):REM 40 ZEILEN
70 Y=INT(25*RND(1)):REM
25 SPALTEN
80 AD=1024+(40*Y)+X
90 POKE AD,102:REM
ASCII-CODE BARRIERE
100 NEXT I
110 POKE 1024+40*RND(1),30
120 X=INT(40*RND(1))
130 Y=24
140 AD=1024+(40*Y)+X
150 POKE AD,24:XO=X:YO=Y
160 GET A$:IF
A$="" THEN 160
170 IF A$="[Cursor oben]"
THEN Y=Y-1
180 IF A$="[Cursor unten]"
THEN Y=Y+1
190 IF A$="[Cursor links]" THEN X=X-1
200 IF A$="[Cursor rechts]"
THEN X=X+1
210 IF X<0 THEN X=0
220 IF X>39 THEN X=39
230 IF Y<0 THEN Y=0
240 IF Y>24 THEN Y=24
250 AO=1024+(40*YO)+XO
260 AD=1024+(40*Y)+X
270 POKE AO,32
280 IF PEEK(AD)=102 THEN GOTO 350
290 IF PEEK(AD)=30 THEN GOTO 320
300 XO=X:YO=Y:POKE
AD,24
310
GOTO 160
320
PRINT"[SHIFT+CLR/HOME]SIE HABEN GEWONNEN!"
330
PRINT"SIE HABEN DEN AUSGANG GEFUNDEN!"
340 END
350 PRINT"[SHIFT+CLR/HOME]SIE
SIND TOT!"
360
PRINT"SIE SIND VOR EINE ELEKTRISCHE BARRIERE"
370
PRINT"GELAUFEN!"
380
END
Das Listing LABYRINTH
1 ist ein kleines Labyrinth-Spiel, in dem Sie den Ausgang (dargestellt durch
einen Pfeil nach oben) finden müssen. Allerdings wird Ihnen der Weg durch
elektrische Barrieren verbaut. Wenn Sie diese Barrieren berühren, sind Sie tot,
und das Spiel wird beendet. Der Einfachheit halber haben Sie nur ein einziges
Leben, und auch nur einen einzigen Level (Ziel ist es also, den Ausgang zu
finden).
Da die Barrieren per
Zufall gesetzt werden, werden Sie in Zeile 10 erst einmal dazu
aufgefordert, eine Taste zu drücken. Wie lange dies genau dauert, können
wahrscheinlich nicht einmal Sie genau sagen. Wenn Sie dann eine Taste gedrückt
haben, wird in Zeile 20 der Zufallsgenerator mit RND(0)
neu initialisiert, und dies geschieht anhand der Computeruhr. Es werden also am
Anfang nicht immer die gleichen Barrieren gesetzt - das soll aber auch so sein.
In Zeile 30 und 40
wird dann der Bildschirm auf Schwarz gestellt, die Schrift auf Weiß und der
Bildschirm gelöscht. In Zeile 50-100 werden nun 250 Barriere-Zeichen
(ASCII-Code 102, Schleifenzähler I) in den Bildschirmspeicher
geschrieben. Da der Bildschirmspeicher 40*25 Zeichen beinhaltet (40 Spalten, 25
Zeilen), und die Koordinaten (X,Y) der Barrieren auch
per Zufall (also mit der RND-Funktion gesetzt werden, so ist die Formel
für die Koordinaten eine Barriere:
X=INT(40*RND(1)), Y=INT(25*RND(1))
RND(1) initialisiert nun den
Zufallsgenerator nicht noch einmal neu, was RND(1) aber dennoch tut, ist
für das Ergebnis nur Zahlen zwischen 0 und 1 zu benutzen. Da aber X
zwischen 0 und 39 und Y zwischen 0 und 24 liegen muss, und X und Y
nur ganze Zahlen enthalten sollen, müssen Sie das Ergebnis zuerst mit einem
entsprechenden Faktor skalieren, und anschließend mit INT in eine
Ganzzahl umwandeln. Erst jetzt kann in Zeile 80 die entsprechende
Speicheradresse durch die Formel
AD=1024+(40*Y)+X
ermittelt und in Zeile
90 mit dem Wert 102 beschrieben werden. Es werden auf diese Weise 250
Barrieren gezeichnet. Anschließend wird dann noch in Zeile 110-150 die
Position des Spielers (Variablen X und Y) und das Zeichen für den
Ausgang (Pfeil nach oben) per Zufall bestimmt.
Der Spieler (ein Kreuz
in Form eines X) soll nun mit den Cursortasten gesteuert werden. Hierzu dienen
die GET-Anweisungen in Zeile 160-200: Zunächst wird auf eine
Taste gewartet, und anschließend wird geschaut, welche Cursortaste gedrückt
wurde. Entsprechend wird dann auch die Variable X oder Y
geändert. Achten Sie hier beim Abtippen des Listings unbedingt darauf, die
Cursortasten richtig einzugeben, und auch wirklich z.B. [SHIFT+CURSOR OBEN]
einzutippen (also beide Tasten gleichzeitig zu drücken).
Zeile 210-240 dient nun dazu, X
und Y daraufhin zu prüfen, ob der Spieler eventuell das Spielfeld
verlassen könnte, und dieses ggf. durch Korrektur von X und Y zu
verhindern. Dies geschieht natürlich durch entsprechende IF-Anweisungen.
Nun hat sich der
Spieler tatsächlich bewegt, aber von wo nach wo muss nun das Kreuz gesetzt
werden? Wo befand sich der Spieler vor dem aktuellen Spielzug? Um dies
herauszufinden, dienen die Variablen XO (X old)
und YO (Y old), die stets die alte Position
vor dem letzten Tastendruck enthalten. Auf diese Weise kann immer die
Speicheradresse ermittelt werden, an der sich das Kreuz vor dem aktuellen
Spielzug befand, der stets X und Y verändert. In Zeile 250-270
wird nun das alte Kreuz entfernt (an die alte Position wird der Wert 32 in den
Bildschirmspeicher geschrieben), was einem Leerzeichen entspricht, und die neue
Adresse, an der das Kreuz nun erscheinen soll, wird in der Variablen AD
gespeichert. Bevor aber das neue Kreuz gesetzt wird, muss erst noch
festgestellt werden, ob sich an der neuen Position eine Barriere (Zeichen-Code
102) oder der Ausgang (Zeichen-Code 30) befindet (hier wird übrigens von POKE
PETSCII, und nicht ASCII benutzt). Wenn keine der beiden Bedingungen eintritt,
befindet sich an der neuen Position ein Leerzeichen, und in Zeile 300
wird dann das Kreuz erneut in die Adresse AD geschrieben. Anschließend
wird durch die GOTO-Anweisung in Zeile 310 die Hauptspielschleife
erneut ausgeführt.
Wenn allerdings die IF-Bedingung
in Zeile 280 zutrifft, dann sind Sie vor eine Barriere gelaufen, und das
Spiel wird mit der Meldung beendet, dass Sie tot sind. Dies wird durch ein GOTO
in die Zeile 350 erreicht. Wenn aber die IF-Bedingung in Zeile
290 zutrifft, dann haben Sie den Ausgang erreicht, und das entsprechende GOTO
in die Zeile 320 bewirkt, dass die Meldung angezeigt wird, dass Sie das
Spiel gewonnen haben.
Das Labyrinth-Spiel ist
noch nicht sehr spannend. Außer, dass Sie den Ausgang irgendwann erreicht
haben, bekommen Sie keine Belohnung, und es gibt auch nur einen einzigen Level.
Wenn Sie dann etwas geübt haben, gewinnen Sie fast immer, und das Spiel verliert
seinen Reiz. Ein gutes Spiel enthält dagegen stets zwei Elemente:
·
Das
Spiel muss mit der Zeit schwieriger werden (denn sonst beginnt sich Ihr Gehirn
zu langweilen und verliert die Freude)
·
Im
Spiel muss es mehr Belohnungen geben, als nur eine kleine Erfolgsmeldung am
Ende (auch bei fehlenden Belohnungen beginnt sich Ihr Gehirn zu langweilen, und
verliert die Freude am Spiel).
Genau deshalb werde ich
nun zwei Dinge einfügen: Erstens gibt es mehrere Level und in jedem Level
erscheinen mehr Barrieren (steigende Anforderungen an den Spieler verhindern
die Langeweile), und zweitens können Sie, bevor Sie den Ausgang erreichen, Gold
einsammeln (fortwährende Belohnungen halten die Freude am Spiel aufrecht). Das
nächste Listing enthält nun die verbesserte Version.
3.3.5 Labyrinth 2
08-LABYRINTH 2
10
PRINT"[SHIFT+CLR/HOME]BITTE EINE TASTE
DRUECKEN"
20 GET
A$:IF A$="" THEN 20:REM GET-FUNKTION LIEST
EINE TASTE
30 IN=RND(0):REM ZUFALLSGENERATOR NEU INITIALISIEREN
40 PRINT"[WHT][SHIFT+CLR/HOME]":POKE 53280,11:POKE 53281,0:LV=1:GD=0
50
GOSUB 1000:REM BARRIEREN SETZEN
60
GOSUB 2000:REM GOLD SETZEN
70
GOSUB 3000:REM SCORELINE ANZEIGEN
80
POKE 1064+40*RND(1),30:REM AUSGANG
120 X=INT(40*RND(1))
130 Y=23
140 AD=1064+(40*Y)+X
150 POKE AD,24:XO=X:YO=Y
160 GET A$:IF
A$="" THEN 160
170 IF A$="[Cursor oben]"
THEN Y=Y-1
180 IF A$="[Cursor unten]"
THEN Y=Y+1
190 IF A$="[Cursor links]" THEN X=X-1
200 IF A$="[Cursor rechts]"
THEN X=X+1
210 IF X<0 THEN X=0
220 IF X>39 THEN X=39
230 IF Y<0 THEN Y=0
240 IF Y>23 THEN Y=23
250 AO=1064+(40*YO)+XO:POKE
AO,32
260 AD=1064+(40*Y)+X
270 IF PEEK(AD)=102 THEN GOTO 350
280 IF PEEK(AD)=81 THEN GD=GD+1:GOSUB
3000
290 IF PEEK(AD)=30 THEN GOTO 320
300 XO=X:YO=Y:POKE
AD,24
310
GOTO 160
320
PRINT"[SHIFT+CLR/HOME]SIE HABEN DEN AUSGANG
GEFUNDEN!":LV=LV+1
330
PRINT"DER NAECHSTE LEVEL IST LEVEL";LV
335 GET A$:IF
A$="" THEN 335
340 PRINT"[SHIFT+CLR/HOME]":GOTO 50
350
PRINT"[SHIFT+CLR/HOME]SIE SIND TOT!"
360
PRINT"SIE SIND VOR EINE ELEKTRISCHE BARRIERE"
370
PRINT"GELAUFEN!"
380
PRINT"[Cursor unten]ANZAHL GOLDKLUMPEN :";GD
390 PRINT"NEUES SPIEL (J/N)";:INPUT
A$
400 IF A$="J" THEN GOTO 30
410 PRINT"[COMMODORE+BLU][SHIFT+CLR/HOME]DAS
SPIEL WURDE BEENDET"
420 POKE 53280,6:POKE
53281,14
430 END
1000 REM *** BARRIEREN ***
1010
FOR I=1 TO 50*LV:REM BARRIEREN ZEICHNEN
1020
X=INT(40*RND(1)):REM 40 ZEILEN
1030 Y=INT(24*RND(1)):REM
25 SPALTEN
1040 AD=1064+(40*Y)+X
1050 POKE AD,102:REM
ASCII-CODE BARRIERE
1060 NEXT I
1070 RETURN
2000 REM *** GOLD ***
2010
FOR I=1 TO 50:REM GOLDKLUMPEN ZEICHNEN
2020
X=INT(40*RND(1)):REM 40 ZEILEN
2030 Y=INT(24*RND(1)):REM
25 SPALTEN
2040 AD=1064+(40*Y)+X
2050 POKE AD,81:REM
ASCII-CODE GOLDKLUMPEN
2060 NEXT I
2070 RETURN
3000 REM *** SCORELINE ***
3010
PRINT"[CLR/HOME]……………" (Punkte sind 15
Leerzeichen)
3020 PRINT"[CLR/HOME]GOLD";GD
3030 RETURN
Versuchen Sie, nun als kleine
Abschlussübung, das Listing LABYRINTH 2 einmal selbständig zu verstehen.
Und das können Sie, denn es enthält keine neuen Dinge, die über POKE, GET,
FOR, IF, PRINT, GOTO, GOSUB und die
Erzeugung von ganzzahligen Zufallszahlen hinausgehen. Sie können das letzte
Labyrinth-Spiel auch mit dem Joystick steuern, wenn Sie die folgenden Zeilen
wie folgt abändern:
160 PO=56320
170 IF NOT PEEK(PO) AND 1 THEN Y=Y-1
180 IF NOT PEEK(PO) AND 2 THEN Y=Y+1
190 IF NOT PEEK(PO) AND 4 THEN X=X-1
200 IF NOT PEEK(PO) AND 8 THEN X=X+1
Setzen Sie für PO
den Wert 56321 ein, dann benutzen Sie den Joystickport
1 anstatt Port 2.