Snake Programmieren

Snake Programmieren Titelbild

Folgende Teile braucht ihr für die Anleitung

Spiele Programmieren mit der LED Matrix Produktbild

Spiele Programmieren mit der LED Matrix | 19.95€

Snake Programmieren

Jetzt haben wir schon so viel mit der Matrix gearbeitet, dass es jetzt an der Zeit ist, diese auch mal wirklich praktisch einzusetzen. Wir werden dafür ein Snake Spiel programmieren. Das Spielprinzip von Snake ist recht simpel. Es gibt eine Schlange, die mit Pfeiltasten bewegt werden kann. Außerdem gibt es einen Apfel. Wen die Schlange einen Apfel gegessen hat, wird diese länger und ein neuer Apfel erscheint. Das Spiel ist dann verloren, wenn die Schlange sich selbst oder die Wand berührt. Je mehr Äpfel die Schlange gegessen hat, desto länger wird sie und auch die Bewegungen werden schneller. Denn mit den Pfeiltasten kann nur die Richtung festgelegt werden, aber nicht, wann sich die Schlange bewegt. Heißt, es wird immer schwieriger. Die Matrix eignet sich perfekt, um so ein Spiel darzustellen. Der Apfel ist eine einzelne angeschaltete LED, eine Schlange besteht natürlich auch aus LEDs und wird so auch immer länger. Wenn der Spieler gestorben ist, wird das angezeigt, ebenso wie die Punktezahl, die der Spieler erreicht hat. Um ein neues Spiel zu starten, muss dann nur eine Taste gedrückt werden.

Du bekommst zuerst den Programmcode und erst danach werden über die Umsetzung der Spiellogik und den genauen Programmcode sprechen. Wenn du willst, kannst du den Programmcode abtippen, Übung macht den Meister ;) Alternativ kannst du diesen aber auch im Internet mit dem folgenden Befehl herunterladen:

wget cw42.de/p/snake.py

snake.py

import RPi.GPIO as gpio
import time
import max7219.led as led
from random import randint

gpio.setmode(gpio.BCM)
taster = [14,15,18,23]
matrixe = 1
matrix = led.matrix(cascaded=matrixe)
height = 7
width = (8*matrixe)-1

def steuerung(gpio):
  global richtung
  if(gpio == 14):   #rechts
    richtung = [1,0]
  elif(gpio == 15): #oben
    richtung = [0,-1]
  elif(gpio == 18): #unten
     richtung = [0,1]
  elif(gpio == 23): #links
    richtung = [-1,0]

for i in taster:
  gpio.setup(i,gpio.IN,pull_up_down=gpio.PUD_UP)
  gpio.add_event_detect(i, gpio.FALLING, 
          callback=steuerung)
def startSpiel():
  global snake, richtung, apfel
  snake = [[randint(2,width-4),randint(3,height-3)]]
  richtung = [0,0]
  while richtung == [0,0]:
    matrix.show_message("READY")
  neuerApfel()

def neuerApfel():
  global apfel, snake
  apfelSnake = False
  while apfelSnake == False:
    apfelSnake = True
    apfel = [randint(0,width),randint(0,height)]
    for i in snake:
      if(i == apfel):
        apfelSnake = False
  print(apfel)

def endOfGame():
  for i in range(0,2):
    matrix.clear()
    for i in range(0,width+1):
      for j in range(0,height+1):
        matrix.pixel(i,j,1)
        time.sleep(0.001)
    time.sleep(0.01)
  matrix.show_message("GAME OVER")
  punkte = len(snake)-1
  matrix.show_message(str(punkte)+" PUNKTE")

  startSpiel()

startSpiel()

while True:
  keinePause = False
  newSnake = [snake[0][0]+richtung[0],
              snake[0][1]+richtung[1]]
  for i in snake:
    if(i == newSnake):
      endOfGame()
      pass

  if(newSnake == apfel):
     neuerApfel()
     keinePause = True
  else:
     snake.pop()
  snake.insert(0,newSnake)

  if(snake[0][0] > width or snake[0][1] > height
    or snake[0][0] < 0 or snake[0][1] < 0 ):
    endOfGame()
    pass

  matrix.clear()
  for i in snake:
    matrix.pixel(i[0],i[1], 1)
  matrix.pixel(apfel[0],apfel[1],1)
  if(keinePause == False):
    newLength = (len(snake)-2)*0.01
    time.sleep(0.3-newLength)
  else:
    time.sleep(0.3)

Wenn du das Programm ausführst und alles richtig angeschlossen hast, solltest du jetzt mit den Tasten die Schlange bewegen können. Damit du die Taster unterscheiden kannst, haben wir auch noch Aufkleber mit Pfeiltasten.

Grundsätzlich besteht das Programm erstmal aus dem Anfang mit den Funktionen und Einstellungen und dem Inhalt der while-Schleife, welche sich um die Bewegungen und die Regeln im Spiel kümmert. Es gibt folgende (globale) Variablen:

taster Eine Liste mit allen Tastern, damit wir diese schnell einrichten können
matrixe Die Anzahl der angeschlossenen Matrixen, in unserem Fall 1
height Ist ein Int, in dem gespeichert wird, wie hoch das Spielfeld ist
width Ist ein Int, in dem gespeichert wird, wie lang das Spielfeld ist und setzt sich aus der Anzahl der Matrixen zusammen
matrix Ist das Matrixobjekt, mit dem wir schon vorher gearbeitet haben
snake Ist die Schlange und eine multidimensionale Liste. Hier werden die Koordinaten der Schlange gespeichert
richtung Ist eine Liste mit x und y Koordinaten, in denen sich die Schlange bewegt
apfel Ist eine Liste mit Koordinaten für die Schlange
keinePause Ist ein Boolean, der für die Geschwindigkeit der Schlange entscheidend ist
newSnake Ist eine Liste, mit der neuen Position der Schlange
newLength Ist ein Integer, der die Länge der Schlange zählt und somit die Geschwindigkeit beeinflusst

Jetzt, da wir die Variablen durchgegangen sind, gehen wir jetzt Schritt für Schritt durch den Programm Code.

In Zeile 6 bis 22 kümmern wir uns um die Steuerung und das Festlegen der Größen der Matrix. Dafür werden in Zeile 7 zunächst die einzelnen GPIO Pins in der Variable taster gespeichert. In Zeile 24 werden diese dann mithilfe einer for-Schleife alle als Inputs gesetzt und mit der Funktion steuerung() verbunden.

Diese Funktion wird in Zeile 13 bis 22 definiert. Da es sich bei der Variablen richtung um eine globale Variable handelt, wir wollen also diese Variablen auch außerhalb der Funktion verändern, müssen wir das erst in noch in Zeile 14 angeben. Wenn wir diese Zeile einfach weglassen würden, würde zwar kein Fehler auftreten, aber wir würden nicht die Variable außerhalb der Funktion verändern. Diese müssen wir aber auch außerhalb der Funktion ändern, damit diese überhaupt einen Einfluss hat. Als Parameter wird der GPIO Pin übergeben, deswegen haben wir mit den if-Bedingungen in Zeile 15 bis 22 kontrolliert, welcher Taster gedrückt wurde. Darauf hin verändert sich die Richtung der x, y Koordinaten. Durch die Kommentare ist zu sehen, was welche Richtung ist.

In Zeile 28 ist die zweite Funktion startSpiel() . Mit dieser werden alle Variablen gesetzt, um das Spiel zu starten. Dafür müssen wir natürlich erstmal in Zeile 30 die Schlange erstellen. Damit diese nicht immer auf der gleichen Position anfängt, benutzen wir die randint() Funktion. Wir wollen ja auch nicht, dass der Spieler direkt am Rand anfängt und dann evtl. sofort stirbt, weil dieser in die Wand fährt, und nehmen nur die Koordinaten aus den maximal Werten und ziehen noch einen kleinen Sicherheitsabstand ab. Damit das Spiel nicht sofort anfängt, sondern erst wenn ein Taster gedrückt wurde, gibt es folgende Konstruktion in Zeile 32 bis 33, die nichts Anderes sagt als zeige "READY" an, bis die Richtung geändert wird und so eine Taste gedrückt wurde. In Zeile 34 wird dann die Funktion neuerApfel() aufgerufen und damit ein neuer Apfel erstellt.

Kommen wir also zu dieser Funktion. Natürlich wollen wir, dass der Apfel zufällig erstellt wird, aber wir wollen nicht, dass der Apfel dort auftaucht, wo sich der Körper der Schlange befindet. In Zeile 39 gibt es eine while-Schleife, die solange läuft wie die Variable apfelSnake False ist. In der Schleife wird in Zeile 40 diese Variable erstmal auf True gesetzt. Danach wird die Variable apfel mit zufälligen Koordinaten gefüllt. In Zeile 42 wird dann die ganze Schlange mit einer for-Schleife durchgegangen. In Zeile 43 wird dann geprüft, ob der momentane Teil der Schlange die gleichen Koordinaten hat wie der vorher erstellte apfel. Wenn das der Fall ist, wird die Variable apfelSnake wieder auf False gesetzt und folglich fängt das ganze Prozedere der while-Schleife wieder von vorne an, solange bis ein Apfel gefunden wird, der nicht auf den Koordinaten der Schlange liegt.

Die nächste Funktion heißt endOfGame() und kümmert sich um das Ende des Spiels. Wir wollen eine kleine Animation anzeigen und natürlich auch sagen, wie viele Punkte der Spieler geschafft hat. Diese Animation erzeugen wir in Zeile 48 bis 54, indem wir zweimal die gesamte Matrix zum Leuchten bringen. Anschließend zeigen wir die "GAME OVER" Nachricht an. In Zeile 56 rechnen wir dann die erreichte Punktzahl aus. Dafür müssen wir einfach nur die Länge der Variablen snake zählen und minus eins rechnen, nämlich die Schlange, die es schon am Anfang gab. In Zeile 59 wird dann die Funktion startSpiel() aufgerufen. Denn alles, was ein Ende hat, muss ja auch wieder einen Anfang haben.

Jetzt, da wir die ganzen Funktionen definiert haben, müssen wir das Spiel noch starten. Das machen wir in Zeile 61 indem die Funktion startSpiel() zum ersten Mal aufgerufen wird.

In Zeile 63 startet jetzt die while-Schleife für die ganzen Bewegungen in dem Spiel.

Zuerst setzen wir die Variable keinePause auf False. Diese entscheidet nachher wie lange die Pause ist. In Zeile 65 und 66 wird eine neue Liste erstellt mit den neuen Koordinaten vom Kopf der Schlange. Dafür wird die erste Position der Schlange (also der Kopf) mit der Richtung der momentanen Bewegung zusammen gerechnet. So kommen wir einfach auf die neue Position des Kopfes.

In Zeile 67 bis 70 wird eine erste Regel getestet. Wenn die Schlange in sich selber läuft, wird das Spiel beendet. Dafür wird wieder eine for-schleife und eine if-Abfrage benutzt. Sollte das zutreffen, wird die die Funktion endOfGame() aufgerufen und mit pass fängt die while-Schleife wieder von vorne an.

Im nächsten Schritt wird von Zeile 72 bis 76 geprüft, ob die Schlange den Apfel erreicht hat. Wenn das der Fall ist, wird zuerst in Zeile 73 die Funktion neuerApfel() aufgerufen, damit ein neuer Apfel erscheint. In Zeile 74 wird dann die Variable keinePause auf True gesetzt. Dies ist entscheidend, damit der Spieler nicht das Gefühl hat, dass die Schlange stehen geblieben ist. Da immer eine neue Position hinzugefügt wird, müssen wir jetzt auch das letzte Glied der Schlange entfernen, wenn diese keinen Apfel gefressen hat, ansonsten würde diese unkontrolliert wachsen. Dafür wird in Zeile 76 die Funktion .pop() aufgerufen, welche das letzte Element einer Liste entfernt.

Bevor wir die letzte Prüfung machen, fügen wir die neue Position der Schlange in Zeile 77 hinzu. Das machen wir mit der Funktion .insert(), mit der wir Inhalte in Listen hinzufügen können. Als ersten Parameter übergeben wir die Position. Da diese an den Anfang soll, wählen wir die 0 und als zweiten Parameter, was hinzugefügt werden soll.

In Zeile 79 bis 82 passiert etwas Ähnliches, nur das diesmal getestet wird, ob sich der neue Kopf außerhalb des Spielfeldes befindet. Wenn das der Fall ist, wird wieder die Funktion endOfGame() aufgerufen.

Im letzten Teil werden zuerst in Zeile 85 bis 87 zuerst die Schlange und dann der Apfel dargestellt.

Kommen wir zum letzten Punkt, der Geschwindigkeit. Wenn die Schlange nicht gewachsen ist, berechnet sich die Geschwindigkeit aus der Länge der Schlange. Dazu wird in der Variablen newLength die Länge der neu hinzugekommenen Glieder gemessen und mal 0.01 gerechnet. Diese werden dann in der nächsten Zeile der 0.8 sekündigen Pause abgezogen. Diese Werte kannst du natürlich auch anpassen, wenn du ein schnelleres Spiel haben willst. Die Ausnahme gibt es in den nächsten Zeilen. Wenn ein neuer Apfel dazugekommen ist, muss die Pause ein wenig kürzer sein.

Dieser Programmcode ist ein gutes Beispiel, das zeigt, welchen Vorteil Funktionen haben und wie sie den Code übersichtlicher machen können. Natürlich kannst du den Programmcode noch weiter an deine eigenen Bedürfnisse anpassen. Das gilt vor allem bei den Pausenzeiten und den Texten, die angezeigt werden.



Mehr als eine Matrix

Bis jetzt haben wir immer nur mit einer Matrix gearbeitet, aber diese lassen sich einfach erweitern. Mit den Anschlüssen, die oben sind, lässt sich eine weitere LED Matrix anschließen. Diese verbindest du einfach genau mit den Pins in der selben Reihenfolge, wie diese auch oben angeschlossen sind. Als Grundlage benutzen wir immer noch die Schaltung für das Snake Spiel.

2 LED Matrix an einen Raspberry Pi angeschlossen

Bevor das Snake Spiel jetzt auch beide Matrixen anzeigt, musst du erst noch in Zeile 8 folgende Variable auf die Anzahl der Matrixen ändern:


matrixe = 2 # Anzahl der Matrixen

Und wenn du jetzt das Snake Spiel startest, kannst du auf allen angeschlossenen Matrixen spielen. Das geht, da in Zeile 8-11 die Höhen und Matrixenangaben einfach in Variablen gespeichert haben und diese Änderungen einfach machen.

Noch Fragen oder Feedback?

Bevor du eine Frage stellen kannst musst du dich zuerst Anmelden oder Regestrieren!