Sitzung 4

Sitzung 4: Erstellen eines Spiels mithilfe von KI.

Signed-off-by: glmacedo <gian.mocerinomacedo@stud.ph-weingarten.de>
This commit is contained in:
glmacedo 2026-05-05 17:22:16 +02:00
parent 7654f85e65
commit 3378f35c77
2 changed files with 518 additions and 0 deletions

474
game.py Normal file
View file

@ -0,0 +1,474 @@
"""
=============================================================
LABYRINTH-QUEST Ein Pygame-Lernspiel für Klasse 11
=============================================================
Didaktische Ziele (Kompetenzbereiche nach Du Boulay 1989):
- Schleifen (for, while)
- Bedingungen (if/elif/else)
- Listen & 2D-Listen (Labyrinth-Grid)
- Funktionen & Modularisierung
- Event-Handling (Tastatureingabe)
- Kollisionserkennung
- Spielzustand & Logik (State Machine)
- Rekursion (Maze-Generierung via Backtracking)
=============================================================
"""
import pygame
import random
import time
# ─────────────────────────────────────────
# KONSTANTEN
# ─────────────────────────────────────────
TILE = 40 # Größe einer Zelle in Pixel
COLS = 19 # Spalten (ungerade!)
ROWS = 15 # Zeilen (ungerade!)
WIDTH = COLS * TILE
HEIGHT = ROWS * TILE + 80 # +80 für HUD-Leiste
FPS = 60
# Farben
C_BG = (15, 20, 35)
C_WALL = (30, 50, 90)
C_WALL_HL = (50, 80, 130)
C_PATH = (20, 30, 55)
C_PLAYER = (80, 200, 120)
C_PLAYER_EYE= (15, 20, 35)
C_EXIT = (255, 200, 50)
C_EXIT_GLOW = (255, 230, 100)
C_COIN = (255, 215, 0)
C_TRAP = (220, 60, 60)
C_HUD_BG = (10, 15, 28)
C_TEXT = (200, 220, 255)
C_TITLE = (100, 180, 255)
C_WHITE = (255, 255, 255)
C_GREEN = ( 60, 200, 100)
C_RED = (220, 60, 60)
C_OVERLAY = (0, 0, 0, 180)
# Zell-Typen
WALL = 1
PATH = 0
# ─────────────────────────────────────────
# LABYRINTH GENERIEREN (Recursive Backtracking)
# ─────────────────────────────────────────
def erstelle_labyrinth(cols, rows):
"""
Generiert ein perfektes Labyrinth mit rekursivem Backtracking.
Gibt eine 2D-Liste zurück: WALL=1, PATH=0
"""
# Alles mit Wänden füllen
gitter = [[WALL] * cols for _ in range(rows)]
def ist_gueltig(r, c):
return 0 < r < rows - 1 and 0 < c < cols - 1
def grabe(r, c):
gitter[r][c] = PATH
richtungen = [(0, 2), (0, -2), (2, 0), (-2, 0)]
random.shuffle(richtungen)
for dr, dc in richtungen:
nr, nc = r + dr, c + dc
if ist_gueltig(nr, nc) and gitter[nr][nc] == WALL:
# Wand zwischen aktuellem und nächstem Feld entfernen
gitter[r + dr // 2][c + dc // 2] = PATH
grabe(nr, nc)
grabe(1, 1)
# Start & Ziel öffnen
gitter[1][1] = PATH
gitter[rows-2][cols-2] = PATH
return gitter
# ─────────────────────────────────────────
# MÜNZEN & FALLEN PLATZIEREN
# ─────────────────────────────────────────
def platziere_items(gitter, anzahl_muenzen=8, anzahl_fallen=5):
"""
Platziert Münzen und Fallen auf freien Pfadzellen.
Gibt zwei Listen mit (row, col)-Tupeln zurück.
"""
freie_felder = []
for r in range(len(gitter)):
for c in range(len(gitter[0])):
if gitter[r][c] == PATH:
# Start und Ziel aussparen
if (r, c) not in [(1, 1), (len(gitter)-2, len(gitter[0])-2)]:
freie_felder.append((r, c))
random.shuffle(freie_felder)
muenzen = freie_felder[:anzahl_muenzen]
fallen = freie_felder[anzahl_muenzen:anzahl_muenzen + anzahl_fallen]
return muenzen, fallen
# ─────────────────────────────────────────
# SPIELER-KLASSE
# ─────────────────────────────────────────
class Spieler:
def __init__(self, reihe, spalte):
self.reihe = reihe
self.spalte = spalte
self.punkte = 0
self.leben = 3
self.bewegt = False # für Animation
self.anim_x = 0.0 # interpoliertes X (Pixel)
self.anim_y = 0.0 # interpoliertes Y (Pixel)
self.ziel_x = float(spalte * TILE)
self.ziel_y = float(reihe * TILE)
self.anim_x = self.ziel_x
self.anim_y = self.ziel_y
self.blick = (0, 1) # Blickrichtung (dr, dc)
def bewege(self, dr, dc, gitter):
"""Bewegt den Spieler, wenn das Zielfeld kein WALL ist."""
nr = self.reihe + dr
nc = self.spalte + dc
self.blick = (dr, dc)
if 0 <= nr < len(gitter) and 0 <= nc < len(gitter[0]):
if gitter[nr][nc] == PATH:
self.reihe = nr
self.spalte = nc
self.ziel_x = float(nc * TILE)
self.ziel_y = float(nr * TILE)
return True
return False
def update(self, dt):
"""Weiche Interpolation zur Zielposition."""
speed = 12.0
self.anim_x += (self.ziel_x - self.anim_x) * speed * dt
self.anim_y += (self.ziel_y - self.anim_y) * speed * dt
def zeichne(self, surface):
cx = int(self.anim_x) + TILE // 2
cy = int(self.anim_y) + TILE // 2
r = TILE // 2 - 4
# Körper
pygame.draw.circle(surface, C_PLAYER, (cx, cy), r)
# Glanz
pygame.draw.circle(surface, (130, 240, 160), (cx - r//4, cy - r//4), r//3)
# Augen (folgen Blickrichtung)
dr, dc = self.blick
ex = cx + dc * (r // 2)
ey = cy + dr * (r // 2)
pygame.draw.circle(surface, C_PLAYER_EYE, (ex, ey), 4)
pygame.draw.circle(surface, C_WHITE, (ex + 1, ey - 1), 2)
# ─────────────────────────────────────────
# ZEICHENFUNKTIONEN
# ─────────────────────────────────────────
def zeichne_labyrinth(surface, gitter, tick):
for r in range(len(gitter)):
for c in range(len(gitter[0])):
rect = pygame.Rect(c * TILE, r * TILE, TILE, TILE)
if gitter[r][c] == WALL:
pygame.draw.rect(surface, C_WALL, rect)
# Dezentes Highlight oben/links
pygame.draw.line(surface, C_WALL_HL,
(c*TILE, r*TILE), (c*TILE+TILE, r*TILE), 1)
pygame.draw.line(surface, C_WALL_HL,
(c*TILE, r*TILE), (c*TILE, r*TILE+TILE), 1)
else:
pygame.draw.rect(surface, C_PATH, rect)
def zeichne_exit(surface, gitter, tick):
er = len(gitter) - 2
ec = len(gitter[0]) - 2
puls = abs((tick % 60) - 30) / 30.0 # 0..1 pulsierend
glow_r = int(TILE // 2 - 4 + puls * 5)
cx = ec * TILE + TILE // 2
cy = er * TILE + TILE // 2
# Glüheffekt (größerer halbtransparenter Kreis)
glow_surf = pygame.Surface((TILE * 2, TILE * 2), pygame.SRCALPHA)
pygame.draw.circle(glow_surf, (*C_EXIT_GLOW, 60),
(TILE, TILE), glow_r + 6)
surface.blit(glow_surf, (cx - TILE, cy - TILE))
pygame.draw.circle(surface, C_EXIT, (cx, cy), TILE // 2 - 4)
pygame.draw.circle(surface, C_WHITE, (cx, cy), TILE // 2 - 10)
# "E" für Exit
font = pygame.font.SysFont("monospace", 18, bold=True)
txt = font.render("E", True, C_HUD_BG)
surface.blit(txt, txt.get_rect(center=(cx, cy)))
def zeichne_muenzen(surface, muenzen, tick):
puls = abs((tick % 40) - 20) / 20.0
r = int(TILE // 2 - 8 + puls * 3)
for (mr, mc) in muenzen:
cx = mc * TILE + TILE // 2
cy = mr * TILE + TILE // 2
pygame.draw.circle(surface, C_COIN, (cx, cy), r)
pygame.draw.circle(surface, C_WHITE, (cx - 2, cy - 2), r // 3)
def zeichne_fallen(surface, fallen, tick):
for (fr, fc) in fallen:
margin = 6
rect = pygame.Rect(fc * TILE + margin, fr * TILE + margin,
TILE - 2*margin, TILE - 2*margin)
pygame.draw.rect(surface, C_TRAP, rect, border_radius=4)
# Kreuz-Symbol
cx = fc * TILE + TILE // 2
cy = fr * TILE + TILE // 2
d = TILE // 2 - margin - 2
pygame.draw.line(surface, C_WHITE, (cx-d, cy-d), (cx+d, cy+d), 2)
pygame.draw.line(surface, C_WHITE, (cx+d, cy-d), (cx-d, cy+d), 2)
def zeichne_hud(surface, spieler, level, zeit_rest, font, font_klein):
hud_rect = pygame.Rect(0, ROWS * TILE, WIDTH, 80)
pygame.draw.rect(surface, C_HUD_BG, hud_rect)
pygame.draw.line(surface, C_WALL_HL, (0, ROWS*TILE), (WIDTH, ROWS*TILE), 2)
y = ROWS * TILE + 10
# Level
txt = font.render(f"Level {level}", True, C_TITLE)
surface.blit(txt, (10, y))
# Punkte
txt = font.render(f"Punkte: {spieler.punkte}", True, C_TEXT)
surface.blit(txt, (10, y + 30))
# Leben (Herzen)
herz_txt = font_klein.render("Leben: " + "" * spieler.leben, True, C_RED)
surface.blit(herz_txt, (WIDTH // 2 - herz_txt.get_width() // 2, y + 5))
# Zeit
farbe = C_GREEN if zeit_rest > 15 else C_RED
txt = font.render(f"Zeit: {int(zeit_rest)}s", True, farbe)
surface.blit(txt, (WIDTH - txt.get_width() - 10, y))
# Steuerung-Hinweis
hint = font_klein.render("Pfeiltasten / WASD zum Bewegen | ESC: Beenden", True, (80, 100, 140))
surface.blit(hint, (WIDTH // 2 - hint.get_width() // 2, y + 50))
def zeige_overlay(surface, zeilen, font_gross, font):
"""Halbtransparentes Overlay mit zentriertem Text."""
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 200))
surface.blit(overlay, (0, 0))
for i, (text, farbe, gross) in enumerate(zeilen):
f = font_gross if gross else font
txt = f.render(text, True, farbe)
y = HEIGHT // 2 - len(zeilen) * 20 + i * 50
surface.blit(txt, txt.get_rect(center=(WIDTH // 2, y)))
# ─────────────────────────────────────────
# SPIELZUSTÄNDE (State Machine)
# ─────────────────────────────────────────
ZUSTAND_TITEL = "titel"
ZUSTAND_SPIEL = "spiel"
ZUSTAND_GEWINN = "gewinn"
ZUSTAND_VERLOR = "verloren"
ZUSTAND_PAUSE = "pause"
ZEIT_LIMIT = 60 # Sekunden pro Level
def spiel_starten(level):
"""Gibt alle Spielvariablen für ein neues Level zurück."""
gitter = erstelle_labyrinth(COLS, ROWS)
muenzen, fallen = platziere_items(gitter,
anzahl_muenzen=6 + level * 2,
anzahl_fallen=3 + level)
spieler = Spieler(1, 1)
start_zeit = time.time()
return gitter, muenzen, fallen, spieler, start_zeit
# ─────────────────────────────────────────
# HAUPTPROGRAMM
# ─────────────────────────────────────────
def main():
pygame.init()
pygame.display.set_caption("Labyrinth-Quest Klasse 11 Informatik")
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
# Schriftarten
try:
font_gross = pygame.font.SysFont("monospace", 42, bold=True)
font = pygame.font.SysFont("monospace", 24, bold=True)
font_klein = pygame.font.SysFont("monospace", 16)
except:
font_gross = pygame.font.Font(None, 48)
font = pygame.font.Font(None, 28)
font_klein = pygame.font.Font(None, 20)
# Spielstart-Variablen
zustand = ZUSTAND_TITEL
level = 1
gesamt_pkt = 0
gitter = muenzen = fallen = spieler = start_zeit = None
tick = 0
running = True
while running:
dt = clock.tick(FPS) / 1000.0
tick += 1
# ── Events ──────────────────────────────────
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
if zustand == ZUSTAND_SPIEL:
zustand = ZUSTAND_PAUSE
elif zustand == ZUSTAND_PAUSE:
zustand = ZUSTAND_SPIEL
else:
running = False
# Titelschirm → Spiel starten
elif zustand == ZUSTAND_TITEL and event.key in (
pygame.K_RETURN, pygame.K_SPACE):
level = 1
gitter, muenzen, fallen, spieler, start_zeit = spiel_starten(level)
zustand = ZUSTAND_SPIEL
# Nach Gewinn / Verlieren → weiter oder neu
elif zustand in (ZUSTAND_GEWINN, ZUSTAND_VERLOR):
if event.key == pygame.K_RETURN:
if zustand == ZUSTAND_GEWINN:
level += 1
gesamt_pkt += spieler.punkte
else:
level = 1
gesamt_pkt = 0
gitter, muenzen, fallen, spieler, start_zeit = spiel_starten(level)
zustand = ZUSTAND_SPIEL
elif event.key == pygame.K_ESCAPE:
zustand = ZUSTAND_TITEL
# Pause → weiter
elif zustand == ZUSTAND_PAUSE and event.key == pygame.K_RETURN:
zustand = ZUSTAND_SPIEL
# Spielerbewegung
elif zustand == ZUSTAND_SPIEL:
dr, dc = 0, 0
if event.key in (pygame.K_UP, pygame.K_w): dr, dc = -1, 0
if event.key in (pygame.K_DOWN, pygame.K_s): dr, dc = 1, 0
if event.key in (pygame.K_LEFT, pygame.K_a): dr, dc = 0, -1
if event.key in (pygame.K_RIGHT, pygame.K_d): dr, dc = 0, 1
if (dr, dc) != (0, 0):
spieler.bewege(dr, dc, gitter)
# ── Spiellogik ──────────────────────────────
if zustand == ZUSTAND_SPIEL and spieler:
spieler.update(dt)
pos = (spieler.reihe, spieler.spalte)
# Münze einsammeln
if pos in muenzen:
muenzen.remove(pos)
spieler.punkte += 10
# Falle auslösen
if pos in fallen:
fallen.remove(pos)
spieler.leben -= 1
spieler.punkte = max(0, spieler.punkte - 5)
if spieler.leben <= 0:
zustand = ZUSTAND_VERLOR
# Ziel erreicht
if pos == (ROWS - 2, COLS - 2):
spieler.punkte += 50 + len(muenzen) * 5 # Bonus für übrige Münzen
zustand = ZUSTAND_GEWINN
# Zeitlimit
zeit_vergangen = time.time() - start_zeit
zeit_rest = max(0, ZEIT_LIMIT - (level - 1) * 5 - zeit_vergangen)
if zeit_rest <= 0:
spieler.leben -= 1
if spieler.leben <= 0:
zustand = ZUSTAND_VERLOR
else:
# Neues Labyrinth, Leben-Abzug
gitter, muenzen, fallen, spieler_neu, start_zeit = spiel_starten(level)
spieler_neu.leben = spieler.leben
spieler_neu.punkte = spieler.punkte
spieler = spieler_neu
else:
zeit_rest = ZEIT_LIMIT
# ── Zeichnen ────────────────────────────────
screen.fill(C_BG)
if zustand == ZUSTAND_TITEL:
# Titelschirm
screen.fill(C_BG)
zeige_overlay(screen, [
("LABYRINTH-QUEST", C_TITLE, True),
("Finde den Ausgang!", C_TEXT, False),
("Sammle Münzen ♦ Meide Fallen ✕", C_COIN, False),
("", C_TEXT, False),
("[ENTER] Starten", C_GREEN, False),
("[ESC] Beenden", C_RED, False),
], font_gross, font)
elif zustand in (ZUSTAND_SPIEL, ZUSTAND_PAUSE):
zeichne_labyrinth(screen, gitter, tick)
zeichne_exit(screen, gitter, tick)
zeichne_muenzen(screen, muenzen, tick)
zeichne_fallen(screen, fallen, tick)
spieler.zeichne(screen)
zeichne_hud(screen, spieler, level, zeit_rest, font, font_klein)
if zustand == ZUSTAND_PAUSE:
zeige_overlay(screen, [
("PAUSE", C_TITLE, True),
("[ENTER] Weiter", C_GREEN, False),
("[ESC] Beenden", C_RED, False),
], font_gross, font)
elif zustand == ZUSTAND_GEWINN:
zeichne_labyrinth(screen, gitter, tick)
zeichne_exit(screen, gitter, tick)
spieler.zeichne(screen)
zeichne_hud(screen, spieler, level, 0, font, font_klein)
zeige_overlay(screen, [
("LEVEL GESCHAFFT! 🎉", C_COIN, True),
(f"Punkte: {spieler.punkte}", C_TEXT, False),
(f"Gesamt: {gesamt_pkt + spieler.punkte}", C_GREEN, False),
("[ENTER] Nächstes Level", C_TITLE, False),
("[ESC] Zum Titel", C_RED, False),
], font_gross, font)
elif zustand == ZUSTAND_VERLOR:
zeichne_labyrinth(screen, gitter, tick)
spieler.zeichne(screen)
zeichne_hud(screen, spieler, level, 0, font, font_klein)
zeige_overlay(screen, [
("GAME OVER", C_RED, True),
(f"Punkte: {spieler.punkte}", C_TEXT, False),
("[ENTER] Neu starten", C_GREEN, False),
("[ESC] Zum Titel", C_TITLE, False),
], font_gross, font)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()

44
sitzung4.md Normal file
View file

@ -0,0 +1,44 @@
# Prompt für Claude (Modell: Sonnet 4.6)
Entwickle mir nach folgendem Ansatz ein Spiel für eine 11 Klasse. Wichtig: Das Spiel soll nur mit der Library Pygame erstellt werden und mit den nativen Funktionen aus Python. Das Spiel soll ein Labyrinth-Spiel sein.
Ansatz: ③ Programmieren als Prozess Programmieren lernen ist anspruchsvoll. Folgende Kompetenzbereiche müssen abgedeckt werden:
* Ein generelles Verständnis dafür, was Programme sind und was man mit ihnen machen kann
* Eine Vorstellung vom Computer als Maschine, die Programme ausführen kann (notional machine)
* Kenntnisse der Notation einer Programmiersprache: Syntax und Semantik
* Repertoire an grundlegenden Lösungsansätzen (Strukturen, algorithmische Patterns)
* Praktische Fertigkeiten zum Planen, Implementieren, Testen und Debuggen (Du Boulay, 1989; Robins, Rountree & Rountree, 2003) Es folgen Bedingungen, die du umsetzen sollst bei der Erstellung des Spiels. Bedingungen der Schulklasse: Zielgruppe: Klassenstufe 11 (Gymnasium, Wahlfach Informatik) Lernumgebung: Computerraum mit Einzelarbeitsplätzen (1 Gerät pro Schüler*in) Lernvoraussetzungen: Heterogene Gruppe mit unterschiedlicher Programmiererfahrung Sie haben verschiedene didaktische Ansätze zum Programmierenlernen kennengelernt. Jetzt wenden Sie diese praktisch an: Sie erstellen mit KI-Unterstützung ein konkretes Spiel, das als Grundlage für Ihren Unterrichtsentwurf dient. Allgemeine Anforderungen: Versetzen Sie sich in die Rolle der Lehrkraft. Formulieren Sie eine Aufgabenstellung, die Sie Ihren Schülerinnen und Schülern geben würden:
* Spieltitel und Ziel des Spiels (z.B. Labyrinth, Klickspiel, 2D-Reaktionsspiel)
* Welche Spielmechanik oder Steuerung enthalten sein soll
* Welche Programmierelemente die Schüler*innen dabei lernen (z.B. Schleifen, Bedingungen, Events, Kollisionen)
# Code analysieren
Welche Programmierkonzepte erkennen Sie (Variablen, Schleifen, Funktionen, Klassen)?
Funktionen, Schleifen, If-Bedigungen, Klassen, Try and Catch Block.
Ist der Code für Schüler*innen verständlich? Was würde überfordern?
Nein, da er viele fortgeschrittene Konzepte (OOP, Error Handling) verlangt. Die SuS könnten sich überfordert fühlen, wenn sie sich noch nicht mit diesen Konzepten beschäftigt haben.
Wie könnte man den Code vereinfachen oder in Teilschritte zerlegen?
Zuerst einzelne Funktionen erstellen (z.B. zuerst das Spielfeld erstellen, dann den Spieler, usw.).
# Didaktischen Ansatz wählen und begründen
Spiralansatz
Warum ist dieser Ansatz für Ihr Spiel und diese Lerngruppe geeignet?
Für das Spiel eignet sich der Spiralansatz, da dieser die SuS schrittweise in die Konzepte des Programmierens einführt. Zuerst lernen die SuS Variablen, Funktionen (usw.), um schrittweise das Spiel aufzubauen und am Ende des Schuljahres das komplette Spiel fertig zu haben. Unabhängig des Wissens der SuS, kann jeder SuS nach seiner Fähigkeit, eigene Funktionen und Features dem Spiel hinzufügen.
Wie genau würden Sie vorgehen? (z.B. fertigen Code als Lösungsbeispiel geben, Teile weglassen als Vervollständigungsaufgabe, schrittweise live entwickeln)
Das Spiel soll schrittweise live entwickelt werden.
Welche Teile des Codes eignen sich als Einstieg, welche erst später?
Als Einstieg eignet sich das komplette Spiel. Das Spiel soll die Motivation der SuS fördern und sie motivieren, sich mit den Grundkonzepten der Programmierung zu befassen, um das vollständige Spiel zu programmieren.
# Reflexion
Welche Schwierigkeiten könnten Schüler*innen bei der Umsetzung haben?
Die SuS könnten Schwierigkeiten haben, Konzepte der Programmierung praktisch in einem Projekt umzusetzen.
Welche Unterstützungsformate (Kommentierung, Hilfestellungen, Partnerarbeit) sind sinnvoll?
Parternarbeit ist sinnvoll, da die SuS sich gegenseitig unterstützen können. Kommentare dienen für Verständnis des Codes und sind auch sinnvoll.
Wie hat die KI-Unterstützung Ihren eigenen Arbeitsprozess beeinflusst — und was bedeutet das für den Unterricht?
Die KI hat vollständig das Spiel erstellt, mit nur einem Prompt. Die Arbeit mit KI ist für eine Lehrkraft sehr praktisch, dennoch müssen die Ergebnisse überprüft und für die jeweilige Klasse angepasst werden.