Zum Inhalt springen
BetaEN
Lektion 3 von 4Spiele programmieren

Spiele programmieren · Lektion 3 · 60 Minuten

Projekt: Schlangenspiel

Das lernst du

  • Ein Spielfeld aus Kästchen mit spiel.raster() und spiel.feld() bauen
  • Eine Schlange als Liste von Positionen speichern und wachsen lassen
  • Mit spiel.gedrueckt() die Richtung wechseln und das Spielende erkennen

Eine Schlange aus Kästchen

Beim Schlangenspiel kriecht eine Schlange über ein Spielfeld aus Kästchen. Frisst sie Futter, wird sie länger. Stößt sie an die Wand oder an sich selbst, ist das Spiel vorbei. Das ist ein Klassiker – und du baust ihn jetzt nach.

Das Raster

spiel.raster(20, 15, 20) macht ein Spielfeld aus 20 Spalten und 15 Zeilen. Jedes Kästchen ist 20 Pixel groß. Mit spiel.feld(spalte, zeile, farbe) füllst du ein Kästchen. Gezählt wird ab 0:

from codelix import spiel

spiel.raster(20, 15, 20)
spiel.feld(0, 0, "white")      # oben links
spiel.feld(19, 14, "white")    # unten rechts
spiel.feld(10, 7, "red")       # ungefähr in der Mitte
print("Spalten:", spiel.spalten(), "Zeilen:", spiel.zeilen())

Die Schlange ist eine Liste

Jedes Stück der Schlange ist eine Position [spalte, zeile]. Die ganze Schlange ist eine Liste davon. Das erste Element ist der Kopf:

from codelix import spiel

spiel.raster(20, 15, 20)
schlange = [[5, 7], [4, 7], [3, 7]]

for stueck in schlange:
    spiel.feld(stueck[0], stueck[1], "lime")

print("Kopf:", schlange[0])
print("Länge:", len(schlange))

So kriecht eine Schlange

Der Trick: Du schiebst nicht jedes Stück einzeln weiter. Du setzt vorne einen neuen Kopf ein und nimmst hinten ein Stück weg. Die Mitte bleibt, wie sie ist:

schlange = [[5, 7], [4, 7], [3, 7]]
print("vorher: ", schlange)

spalte, zeile = schlange[0]
schlange.insert(0, [spalte + 1, zeile])   # neuer Kopf rechts daneben
schlange.pop()                             # letztes Stück weg
print("nachher:", schlange)

Und wenn die Schlange frisst? Dann lässt du pop() einfach weg. Schon ist sie ein Stück länger.

Richtung wechseln

Die Schlange soll nicht in jedem Bild ein Feld weiterspringen – das wäre viel zu schnell. Mit spiel.bildnummer() % 6 == 0 bewegt sie sich nur in jedem sechsten Bild.

Für die Richtung nimmst du spiel.gedrueckt(). Es ist nur in dem Bild True, nachdem du die Taste gedrückt hast. So geht auch ein ganz kurzer Tipper nicht verloren:

from codelix import spiel

spiel.raster(20, 15, 20)
kopf = [10, 7]
stand = {"richtung": "rechts"}


def schritt():
    for taste in ["links", "rechts", "oben", "unten"]:
        if spiel.gedrueckt(taste):
            stand["richtung"] = taste
    if spiel.bildnummer() % 6 == 0:
        if stand["richtung"] == "rechts":
            kopf[0] = (kopf[0] + 1) % 20
        elif stand["richtung"] == "links":
            kopf[0] = (kopf[0] - 1) % 20
        elif stand["richtung"] == "oben":
            kopf[1] = (kopf[1] - 1) % 15
        else:
            kopf[1] = (kopf[1] + 1) % 15
    spiel.feld(kopf[0], kopf[1], "lime")
    spiel.text(8, 6, stand["richtung"], "white", 16)


spiel.starten(schritt)

Siehst du das % 20? Damit kommt der Kopf auf der anderen Seite wieder herein. Im echten Spiel ist die Wand dagegen tödlich.

Weiterbauen: Wenn deine Aufgabe klappt: Verhindere, dass die Schlange direkt umdrehen kann (von rechts sofort nach links). Lass sie mit jedem Punkt schneller werden – zum Beispiel mit % 5 statt % 6. Und achte darauf, dass das Futter nicht auf der Schlange erscheint.

Deine Aufgabe

Jetzt bist du dran

Das Spiel ist fast fertig – es fehlen die zwei wichtigsten Funktionen. 1. bewegen(schlange, richtung, frisst): Setz vorne einen neuen Kopf ein, ein Feld weiter in richtung ("rechts", "links", "oben" oder "unten"). Frisst die Schlange gerade nicht, nimm hinten das letzte Stück weg. 2. ist_vorbei(schlange, spalten, zeilen): Gib True zurück, wenn der Kopf außerhalb des Spielfelds liegt oder auf einem anderen Teil der Schlange. Sonst False.

import random
from codelix import spiel

spiel.raster(20, 15, 20)
schlange = [[5, 7], [4, 7], [3, 7]]
stand = {"richtung": "rechts", "futter": [12, 7], "punkte": 0}


def bewegen(schlange, richtung, frisst):
    pass


def ist_vorbei(schlange, spalten, zeilen):
    return False


def schritt():
    for taste in ["links", "rechts", "oben", "unten"]:
        if spiel.gedrueckt(taste):
            stand["richtung"] = taste

    if spiel.bildnummer() % 6 == 0:
        spalte, zeile = schlange[0]
        ziel = {"rechts": [spalte + 1, zeile], "links": [spalte - 1, zeile],
                "oben": [spalte, zeile - 1], "unten": [spalte, zeile + 1]}[stand["richtung"]]
        frisst = ziel == stand["futter"]
        bewegen(schlange, stand["richtung"], frisst)
        if frisst:
            stand["punkte"] = stand["punkte"] + 1
            stand["futter"] = [random.randint(0, 19), random.randint(0, 14)]
        if ist_vorbei(schlange, spiel.spalten(), spiel.zeilen()):
            spiel.text(110, 130, "Spiel vorbei!", "tomato", 30)
            spiel.ende()

    spiel.feld(stand["futter"][0], stand["futter"][1], "red")
    for stueck in schlange:
        spiel.feld(stueck[0], stueck[1], "lime")
    spiel.text(8, 6, f"Punkte: {stand['punkte']}", "white", 16)


spiel.starten(schritt)

Ohne Hinweise gelöst gibt es einen ⭐ auf deiner Fortschrittsseite.

Lösung anzeigen

Versuch es erst selbst – aus Fehlern lernt man am meisten. Hier ist eine mögliche Lösung:

import random
from codelix import spiel

spiel.raster(20, 15, 20)
schlange = [[5, 7], [4, 7], [3, 7]]
stand = {"richtung": "rechts", "futter": [12, 7], "punkte": 0}


def bewegen(schlange, richtung, frisst):
    spalte, zeile = schlange[0]
    if richtung == "rechts":
        spalte = spalte + 1
    elif richtung == "links":
        spalte = spalte - 1
    elif richtung == "oben":
        zeile = zeile - 1
    elif richtung == "unten":
        zeile = zeile + 1
    schlange.insert(0, [spalte, zeile])
    if not frisst:
        schlange.pop()


def ist_vorbei(schlange, spalten, zeilen):
    spalte, zeile = schlange[0]
    if spalte < 0 or spalte >= spalten or zeile < 0 or zeile >= zeilen:
        return True
    return schlange[0] in schlange[1:]


def schritt():
    for taste in ["links", "rechts", "oben", "unten"]:
        if spiel.gedrueckt(taste):
            stand["richtung"] = taste

    if spiel.bildnummer() % 6 == 0:
        spalte, zeile = schlange[0]
        ziel = {"rechts": [spalte + 1, zeile], "links": [spalte - 1, zeile],
                "oben": [spalte, zeile - 1], "unten": [spalte, zeile + 1]}[stand["richtung"]]
        frisst = ziel == stand["futter"]
        bewegen(schlange, stand["richtung"], frisst)
        if frisst:
            stand["punkte"] = stand["punkte"] + 1
            stand["futter"] = [random.randint(0, 19), random.randint(0, 14)]
        if ist_vorbei(schlange, spiel.spalten(), spiel.zeilen()):
            spiel.text(110, 130, "Spiel vorbei!", "tomato", 30)
            spiel.ende()

    spiel.feld(stand["futter"][0], stand["futter"][1], "red")
    for stueck in schlange:
        spiel.feld(stueck[0], stueck[1], "lime")
    spiel.text(8, 6, f"Punkte: {stand['punkte']}", "white", 16)


spiel.starten(schritt)