Abenteuer Trinket: Maze Game Stage 1

Trinket
Python
Turtle
Spieleprogrammierung
Autor:in

Jörg Kantel

Veröffentlichungsdatum

5. März 2023

Das Abenteuer Trinket geht weiter, denn die Beschäftigung mit dieser extrem leistungsfähigen Online-IDE macht einfach Spaß. Daher habe ich ein weiteres Spiel hervorgekramt, das ich schon vor einem Jahr in Pygame Zero implementiert hatte. Damals sollte es eigentlich als künftige Blaupause für mein Projekt »Retrogaming und Künstliche Intelligenz« dienen. Dann war ich jedoch zu der Überzeugung gekommen, daß Pygame Zero doch nicht die richtige Umgebung dafür sei. Eigentlich tendierte ich dann zu Pygame, ohne die Idee jedoch weiter zu verfolgen. Vielleicht bringt nun Trinket (das ja sowohl eine Art von Processing(.py) wie auch eine Turtle bewegen kann) wieder neuen Schwung in die Angelegenheit.

Auf jeden Fall habe ich mir dafür zum wiederholten Male die Playlist »Python Maze Game Tutorial« von Christian Thompson1 hereingezogen und begonnen, daraus eine Version in Trinket zu erstellen. Während mein letzter Versuch mit Trinket und der Turtle alles rausholte, was aus Trinket rauszuholen war, sollte es diesmal etwas anfängerfreundlicher und übersichtlicher werden. Daher sind auch mehrere Lieferungen geplant.

Die heutige erste Lieferung zeigt ein kleines Labyrinth2, durch das der mit den Pfeiltasten gesteuerte Spieler irren kann. Er kann auf seinem Irrweg drei Schatzkästlein leeren und schließlich auch unten rechts den Irrgarten verlassen. Damit endet dann auch das Spiel.

Im Gegensatz zu der Version von Christian Thompson, der – bedingt durch die Einschränkungen von Pythons Standard-Turtle – nur 24x24 Pixel große Sprites vewenden konnte, konnte ich – da Trinket hier keine Einschränkungen besitzt – auf 16x16 große Tiles und Sprites zurückgreifen. In der momentanen Version habe ich für die Mauern ein Tile aus Kenneys – wie immer freien (CCO 1.0 Universal) – 1-Bit-Pack verwendet, da es zwischen den einzelnen Elementen jeweils einen Rand läßt, so daß man den Aufbau des Irrgarten nachvollziehen kann. Später möchte ich jedoch – wie hier schon angekündigt – auf Kenneys ebenfalls unter CCO 1.0 Universal stehende Tiny Dungeon Tileset nebst der dazugehörenden Tiny Town Erweiterung zurückgreifen. Die Figur des Zauberers wie auch die Schatzkästlein habe ich jedenfalls schon aus diesem Tile- und Spriteset entnommen3.

Doch jetzt erst einmal den kompletten Quellcode, damit Ihr das Spiel nachvollziehen und auf Wunsch auch eweitern könnt:

import turtle, math

WIDTH, HEIGHT = 440, 440
WALL   = 63
DOOR   = 62
CHEST  = 22
PLAYER = 10
TILESIZE = 16

maze_map_0 = [[63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63],
            [63,10,-1,63,63,63,63,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,63,63,63,63,63],
            [63,-1,-1,63,63,63,63,63,63,63,-1,-1,63,63,63,63,63,63,-1,-1,63,63,63,63,63],
            [63,-1,-1,-1,-1,-1,-1,-1,63,63,-1,-1,63,63,63,63,63,63,-1,-1,63,63,63,63,63],
            [63,-1,-1,-1,-1,-1,-1,-1,63,63,-1,-1,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,63,63],
            [63,63,63,63,63,63,-1,-1,63,63,-1,-1,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,63,63],
            [63,63,63,63,63,63,-1,-1,63,63,-1,-1,63,63,63,63,63,63,-1,-1,63,63,63,63,63],
            [63,63,63,63,63,63,-1,-1,63,63,-1,-1,-1,-1,63,63,63,63,-1,-1,63,63,63,63,63],
            [63,-1,-1,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,63,63,63,63,22,-1,63,63,63,63,63],
            [63,-1,-1,63,63,63,-1,-1,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63],
            [63,-1,-1,-1,-1,-1,-1,-1,-1,-1,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63],
            [63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,63,63,63,63,63,63,63,63],
            [63,63,63,63,63,63,63,63,63,63,63,63,-1,-1,-1,-1,-1,63,63,63,63,63,-1,22,63],
            [63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,-1,-1,63,63,63,63,63,-1,-1,63],
            [63,-1,-1,63,63,63,63,63,63,63,63,63,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,-1,63],
            [63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,63],
            [63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,63,63,63,63,63,63,63,63,63,63,63,63,63],
            [63,63,63,63,63,63,63,63,63,63,-1,-1,63,63,63,63,63,63,63,63,63,63,63,63,63],
            [63,63,63,63,63,63,63,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,63],
            [63,22,-1,-1,63,63,63,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,63],
            [63,-1,-1,-1,63,63,63,63,63,63,63,63,63,63,63,63,63,63,-1,-1,63,63,63,63,63],
            [63,-1,-1,-1,-1,-1,63,63,63,63,63,63,63,63,63,63,63,63,-1,-1,63,63,63,63,63],
            [63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,63,63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,63],
            [63,63,63,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62],
            [63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63]]

screen = turtle.Screen()
screen.setup(WIDTH, HEIGHT)
screen.bgcolor("#2b3e50")

# Bildschirm-Refresh ausschalten
screen.tracer(0)

# Die Turtle-Bilder registrieren
images = ["wall1.png", "wizard.png", "chest.png", "key.png"]
for image in images:
  screen.addshape(image)

class Player(turtle.Turtle):
  
  def __init__(self):
    turtle.Turtle.__init__(self)
    self.penup()
    self.right(270)
    self.shape("wizard.png")
    self.speed(0)
    self.gold = 0
    
  # Player Movement
  def go_up(self):
    next_x, next_y = self.xcor(), self.ycor() + TILESIZE
    if (next_x, next_y) not in walls:
      self.goto(next_x, next_y)
    
  def go_down(self):
    next_x, next_y = self.xcor(), self.ycor() - TILESIZE
    if (next_x, next_y) not in walls:
      self.goto(next_x, next_y)
    
  def go_left(self):
    next_x, next_y = self.xcor() - TILESIZE, self.ycor()
    if (next_x, next_y) not in walls:
      self.goto(next_x, next_y)
    
  def go_right(self):
    next_x, next_y = self.xcor() + TILESIZE, self.ycor()
    if (next_x, next_y) not in walls:
      self.goto(next_x, next_y)
      
  # Kollisionserkennung (Pythagoras)
  def is_collision(self, other):
    a = self.xcor() - other.xcor()
    b = self.ycor() - other.ycor()
    distance = math.sqrt((a**2) + (b**2))
    if distance < 5:
      return True
    else:
      return False

class Wall(turtle.Turtle):
  
  def __init__(self):
    turtle.Turtle.__init__(self)
    self.penup()
    self.shape("wall1.png")
    self.speed(0)

class Chest(turtle.Turtle):
  
  def __init__(self, _x, _y):
    turtle.Turtle.__init__(self)
    self.penup()
    self.right(270)
    self.shape("chest.png")
    self.speed(0)
    self.gold = 100
    self.goto(_x, _y)
  
  def destroy(self):
    self.goto(2000, 2000)
    self.hideturtle()
  
levels = []
levels.append(maze_map_0)

def setup_maze(level):
  for y in range(len(level)):
    for x in range(len(level[y])):
      # Get the mumber of every item in the mace
      item_number = level[y][x]
      # Berechne die Bildschirmkoordinaten
      screen_x = -192 + (x*TILESIZE)
      screen_y =  192 - (y*TILESIZE)
      
      # Prüfe, ob Item ein Wall ist
      if item_number == WALL:
        wall.goto(screen_x, screen_y)
        wall.stamp()
        walls.append((screen_x, screen_y))
        
      # Prüfe, ob Item der Spieler ist
      if item_number == PLAYER:
        player.goto(screen_x, screen_y)
        
      # Prüfe, ob Item eine Schatztrue ist
      if item_number == CHEST:
        chests.append(Chest(screen_x, screen_y))

wall = Wall()
walls = []
chests = []
player = Player()

# Level Setup
setup_maze(levels[0])

# Das Spiel beenden
def exit_game():
  global keep_going
  print("Bye, bye, Baby")
  keep_going = False

# Auf Tastaturereignisse lauschen
screen.listen()
screen.onkey(player.go_up, "Up")
screen.onkey(player.go_down, "Down")
screen.onkey(player.go_left, "Left")
screen.onkey(player.go_right, "Right")

screen.onkey(exit_game, "q")   # Das Spiel beenden

# Spiel starten
print("🧙 Simple Maze Game Stage 1 🧙")
keep_going = True
while keep_going:
  # Hat der Spieler eine Schatzkiste gefunden?
  for chest in chests:
    if player.is_collision(chest):
      player.gold += chest.gold
      print("Player Gold: {}".format(player.gold))
      # Verberge die Schatzkiste
      chest.destroy()
      # Lösche die Schatzkiste aus der Liste
      chests.remove(chest)
  # Ist der Spieler dem Labyrint entkommen?
  if player.xcor() > 192:
    print("**Gewonen!**")
    exit_game()
    
  screen.update()   # den gesamten Bildschirm neu zeichnen

Natürlich gibt es das Spiel als Trinket und den Quellcode und alle bisher benutzten Bildchen habe ich auch auf GitHub hochgeladen.

Bisher habe ich nur eine vage Vorstellung davon, wie ich weitermachen soll. Ich bin daher mindestens genau so gespannt wie Ihr, wohin die Reise geht. Still digging!

Fußnoten

  1. Ich kann auch dies nicht oft genug wiederholen: Christian Thompson aka Tokyo Edtech ist der Mann, der Unglaubliches mit Pythons (Standard-) Turtle anstellt.↩︎

  2. Ich weiß, es ist strenggenommen kein Labyrint, sondern ein Irrgarten. Aber da im Deutschen beide Begriffe oft synonym verwendet werden, habe auch ich mir diese kleine, sprachliche Ungenauigkeit erlaubt.↩︎

  3. Falls mir dann dennoch die Bildchen ausgehen sollten: Auch Kenneys Pixel Plattformer paßt von der Anmutung her noch gut in diese Sammlung.↩︎