This commit is contained in:
englingvi103466 committed 2026-11-05 12:32:53 +01:00
1 parent 84f0f9e8fb
commit ecb59a6571
1 file changed
+57 -12
+57 -12
View File
@@ -2,88 +2,133 @@ import pygame
class Game:
def __init__(self):
# Initialisiert alle Module von Pygame
pygame.init()
# Erstellt das Hauptfenster mit einer Auflösung von 800x600 Pixeln
self.screen = pygame.display.set_mode((800, 600))
# Setzt den Fenstertitel
pygame.display.set_caption("Undo Doodle")
# Erstellt eine Clock, um die Bildwiederholrate (Framerate) zu steuern
self.clock = pygame.time.Clock()
# Der Zustand ist eine Liste von Strichen.
# Jeder Strich ist eine Liste von Punkten (tuples).
# Initialisierung des Zustands:
# Der Zustand ist eine Liste von Strichen. Jeder Strich ist eine Liste von Punkten (Tuples).
initial_state = []
# Die Historie speichert die aufeinanderfolgenden, immutablen Zustände
# Die Historie speichert die aufeinanderfolgenden, unveränderbaren (immutablen) Zustände.
# Zu Beginn enthält sie nur den leeren Anfangszustand.
self.history = [initial_state]
# Steuerungs-Flags für den Zeichenvorgang
self.drawing = False
self.current_stroke = []
@property
def current_state(self):
"""Liefert den aktuellen Zustand (oberstes Element der Historie)."""
"""Der @property-Decorator sorgt dafür, dass diese Methode wie ein Attribut aufgerufen wird (ohne Klammern).
Sie liefert immer den aktuellen Zustand zurück – das ist das jeweils letzte Element am Ende der Historie.
Für den Code, der die Klasse von außen benutzt, sieht es so aus, als würde man einfach nur einen Wert ablesen.
Im Hintergrund kann die Methode aber komplizierte Berechnungen anstellen, Werte prüfen oder Daten umwandeln,
ohne dass der Aufrufer das merken muss.
"""
return self.history[-1]
def run(self):
# Die Hauptschleife des Spiels (Game Loop)
running = True
while running:
# 1. Eingaben verarbeiten (Events)
running = self.handle_events()
# 2. Spiel-Logik aktualisieren (hier nicht nötig, aber im Loop enthalten)
self.update()
# 3. Alles auf den Bildschirm zeichnen
self.draw()
# Begrenzt das Spiel auf 60 Bilder pro Sekunde
self.clock.tick(60)
pygame.quit()
def handle_events(self):
# Fängt alle Benutzer-Eingaben (Maus, Tastatur, Fenster schließen) ab
for event in pygame.event.get():
# Fall A: Benutzer schließt das Fenster
if event.type == pygame.QUIT:
return False
# Fall B: Eine Taste wird gedrückt
elif event.type == pygame.KEYDOWN:
# Mit 'u' oder Strg+Z einen Schritt zurückgehen (Undo)
# Prüft, ob 'u' oder Strg + 'Z' gedrückt wurde (Undo-Befehl)
if event.key == pygame.K_u or (event.key == pygame.K_z and (pygame.key.get_mods() & pygame.KMOD_CTRL)):
# Wir können nur Undo machen, wenn noch mehr als der Urzustand in der Historie ist
if len(self.history) > 1:
# Entfernt den obersten/neuesten Zustand aus der Historie -> springt zum vorherigen zurück
self.history.pop()
print("Undo ausgeführt!")
# Fall C: Maustaste wird heruntergedrückt (Zeichnen starten)
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Linke Maustaste gedrückt
if event.button == 1: # Linke Maustaste
self.drawing = True
# Startet einen neuen Strich und fügt direkt den ersten Punkt (Mausposition) hinzu
self.current_stroke = [event.pos]
# Fall D: Maus wird bei gedrückter Taste bewegt (Linie ziehen)
elif event.type == pygame.MOUSEMOTION:
if self.drawing:
# Fügt den aktuellen Punkt zur Liste des aktuellen Strichs hinzu
self.current_stroke.append(event.pos)
# Fall E: Maustaste wird losgelassen (Strich beenden)
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1 and self.drawing:
self.drawing = False
if self.current_stroke:
# IMMUTABLE ANSATZ:
# Erstelle einen völlig neuen Zustand (neue Liste),
# anstatt den alten Zustand direkt zu verändern.
# IMMUTABLE ANSATZ IN AKTION:
# Statt den alten Zustand direkt zu verändern (z.B. mit .append()),
# erstellen wir mit `self.current_state + [self.current_stroke]`
# eine völlig neue Liste (ein neues Objekt).
new_strokes = self.current_state + [self.current_stroke]
# Dieser neue Zustand wird als nächster Schritt an die Historie angehängt.
self.history.append(new_strokes)
# Setzt den temporären Strich für den nächsten Zug zurück
self.current_stroke = []
return True
def update(self):
# Platzhalter für Logik-Updates
pass
def draw(self):
self.screen.fill((255, 255, 255)) # Hintergrund weiß füllen
# Füllt den gesamten Bildschirm mit Weiß
self.screen.fill((255, 255, 255))
# 1. Zeichne alle fertigen Striche aus dem aktuellen Zustand
# 1. Zeichnet alle bereits fertigen Striche aus dem aktuellen Zustand
# Dank @property greifen wir hier ganz einfach ohne Klammern auf `self.current_state` zu.
for stroke in self.current_state:
if len(stroke) > 1:
# Zeichnet eine glatte Linie über alle Punkte des Striches (schwarz, Stärke 3)
pygame.draw.lines(self.screen, (0, 0, 0), False, stroke, 3)
# 2. Zeichne den aktuellen Strich während des Ziehens in einer Vorschau-Farbe
# 2. Zeichnet den aktuellen Strich in Echtzeit als Vorschau während des Ziehens
if self.drawing and len(self.current_stroke) > 1:
# Die Vorschau wird in einem helleren Grau angezeigt
pygame.draw.lines(self.screen, (120, 120, 120), False, self.current_stroke, 3)
# Bringt die gezeichnete Leinwand gebündelt auf den Bildschirm (verhindert Flackern)
pygame.display.flip()
if __name__ == "__main__":
# Startet das Spiel, wenn die Datei direkt ausgeführt wird
game = Game()
game.run()