Zum Abschluss ein vollständiges Spiel: Du steuerst mit dem Joystick eine Schlange über das Display und sammelst Äpfel. Jeder Apfel macht sie ein Stück länger. Das Spiel endet, wenn sie gegen den Rand oder gegen sich selbst stößt. Das Programm kommt mit dem aus, was du in den sieben Schritten gelernt hast: Variablen, Verzweigungen, Schleifen, Listen und Funktionen.
Öffne das PicoBoy Studio, klicke auf „Zu Text wechseln“ und „Bearbeitung aktivieren“, ersetze den Inhalt des Editors durch den Quelltext und klicke auf ▶ Testen. Im Emulator lenkst du mit den Pfeiltasten. Über „Dateien“ → „Auf den PicoBoy…“ speicherst du das Spiel auf dem Gerät, und es erscheint im Startmenü.
from picoboy import PicoBoy
import time
import random
import machine
xSize = 29
ySize = 15
pb = PicoBoy()
board = [[0 for x in range(ySize)] for y in range(xSize)]
##### Quellcode ueber dieser Linie bitte nicht aendern! #####
laenge = 3
xPos = 14
yPos = 7
richtung = random.randint(1, 4) # 1 hoch, 2 runter, 3 links, 4 rechts
def putApple(): # Apfel auf ein zufälliges leeres Feld legen
global board
x = random.randint(0, xSize-1)
y = random.randint(0, ySize-1)
while board[x][y] != 0: # Feld besetzt? Dann neu auslosen.
x = random.randint(0, xSize-1)
y = random.randint(0, ySize-1)
board[x][y] = -1 # -1 steht für den Apfel
def erlaubt(x, y): # Darf der Kopf auf dieses Feld?
if x < 0 or x >= xSize or y < 0 or y >= ySize or board[x][y] > 0:
return False
else:
return True
def spielfeldLeeren(): # Alle Felder auf 0 setzen
global board
i = 0
while i < xSize:
j = 0
while j < ySize:
board[i][j] = 0
j = j + 1
i = i + 1
def score(): # Punktestand rechts neben das Spielfeld
global laenge
punkte = laenge - 3 # Die Schlange beginnt mit Länge 3
pb.text(str(punkte%10), 120, 43, 1)
punkte = int(punkte/10)
pb.text(str(punkte%10), 120, 33, 1)
punkte = int(punkte/10)
pb.text(str(punkte%10), 120, 23, 1)
def spielfeldZeichnen(): # Rahmen, Punkte und alle Felder zeichnen
global xPos
global yPos
global board
global laenge
score()
pb.rect(0, 0, 4*(xSize+1), 4*(ySize+1), 1)
i = 0
while i < xSize:
j = 0
while j < ySize:
if board[i][j] > 0: # Ein Stück der Schlange
if board[i][j] == laenge: # Der Kopf ist gefüllt
pb.fill_rect(i*4+2, j*4+2, 4, 4, 1)
else: # Der Rest ist nur ein Umriss
pb.rect(i*4+2, j*4+2, 4, 4, 1)
board[i][j] = board[i][j] - 1 # Ein Durchlauf weniger
if board[i][j] == -1: # Der Apfel
pb.fill_rect(i*4+3, j*4+3, 2, 2, 1)
j = j + 1
i = i + 1
def schritt(): # Lenken, bewegen, Anstoß und Apfel prüfen
global xPos
global yPos
global board
global richtung
global laenge
if pb.pressedUp() and richtung != 2: # Nicht direkt umkehren
richtung = 1
if pb.pressedDown() and richtung != 1:
richtung = 2
if pb.pressedLeft() and richtung != 4:
richtung = 3
if pb.pressedRight() and richtung != 3:
richtung = 4
if richtung == 1:
yPos = yPos - 1
if richtung == 2:
yPos = yPos + 1
if richtung == 3:
xPos = xPos - 1
if richtung == 4:
xPos = xPos + 1
if not erlaubt(xPos, yPos): # Angestoßen: Punkte zeigen, Neustart
pb.fill_rect(10, 10, 4*(xSize+1)-20, 4*(ySize+1)-20, 0)
pb.rect(10, 10, 4*(xSize+1)-20, 4*(ySize+1)-20, 1)
pb.text("Score:", 18, 18)
pb.text(str(laenge - 3), 18, 28)
pb.show()
time.sleep_ms(4000)
machine.reset()
if board[xPos][yPos] == -1: # Apfel gefressen?
laenge = laenge + 1
putApple()
board[xPos][yPos] = laenge # Hier ist jetzt der Kopf
spielfeldLeeren()
putApple()
last = time.ticks_us() # Zeitpunkt des letzten Durchlaufs merken
while True:
schritt()
pb.fill(0)
spielfeldZeichnen()
pb.show()
while time.ticks_diff(time.ticks_us(), last) < 75000:
pass # "pass" heißt: hier nichts tun
last = time.ticks_us()
Das Spielfeld
Das Spielfeld board ist eine Liste aus Listen, wie das Schachbrett im Kapitel über Listen. Es hat 29 Spalten und 15 Zeilen (xSize und ySize). Hier steht die x-Position an erster Stelle: board[x][y]. Jedes Feld ist auf dem Display 4 mal 4 Pixel groß. Rechts daneben bleibt Platz für den Punktestand.
Die Zahl in einem Feld sagt, was dort liegt:
0: Das Feld ist leer.-1: Hier liegt ein Apfel.- Eine Zahl über 0: Hier liegt ein Stück der Schlange.
Der Trick mit der Schlange
Das Programm führt nirgends eine Liste aller Schlangenteile. Stattdessen schreibt schritt() in das Feld des Kopfs den Wert von laenge, und spielfeldZeichnen() zieht von jedem Schlangenfeld pro Durchlauf 1 ab. Die Zahl sagt also, wie viele Durchläufe dieses Stück noch bleibt. Bei 0 ist das Feld wieder leer, und das Ende der Schlange rückt von selbst nach.
Frisst die Schlange einen Apfel, wächst laenge um 1. Jedes neue Feld bleibt dann einen Durchlauf länger bestehen, und die Schlange wird länger.
Die Funktionen
putApple()lost ein Feld aus. Die while-Schleife lost so lange neu, bis das Feld leer ist.erlaubt(x, y)liefertFalse, wenn die Position außerhalb des Spielfelds liegt oder dort schon ein Stück der Schlange ist. Die Reihenfolge ist wichtig: Python prüft die Teile vonorvon links nach rechts und hört beim ersten erfüllten auf. Liegt x außerhalb, liest esboard[x][y]also gar nicht erst.spielfeldLeeren()setzt alle Felder auf 0.score()zerlegt den Punktestand in Ziffern:punkte%10ist die letzte Ziffer, undint(punkte/10)schneidet sie ab. Die drei Ziffern stehen untereinander rechts vom Spielfeld.spielfeldZeichnen()geht alle Felder durch. Der Kopf erscheint als gefülltes Quadrat, der Rest der Schlange als Umriss und der Apfel als kleiner Punkt.schritt()liest den Joystick, bewegt den Kopf und prüft, ob die Schlange anstößt oder einen Apfel frisst.richtungsteht für hoch (1), runter (2), links (3) und rechts (4). Bedingungen wierichtung != 2verhindern, dass die Schlange direkt umkehrt und sich selbst beißt.
Die Hauptschleife
Ganz unten steht die Spielschleife. Sie macht einen Schritt, zeichnet das Bild und zeigt es an. Die innere while-Schleife wartet dann, bis seit dem letzten Durchlauf 75000 Mikrosekunden vergangen sind, also 75 Millisekunden. Anders als ein festes pb.delay() berücksichtigt sie die Zeit, die das Rechnen und Zeichnen gebraucht hat. So läuft die Schlange immer gleich schnell.
Zwei Dinge sehen anders aus als in den Kapiteln: Der Quelltext rückt mit vier Leerzeichen ein statt mit zwei, und global steht öfter da als nötig. Gebraucht wird es nur für Variablen, die einen neuen Wert bekommen, etwa laenge in schritt().
Übung
- Ersetze 75000 durch 50000. Die Schlange wird schneller.
- Rufe
putApple()vor der Hauptschleife ein zweites Mal auf. Jetzt liegen ständig zwei Äpfel auf dem Feld, denn für jeden gefressenen Apfel legt das Programm einen neuen hin.