Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| lehrkraefte:snr:informatik:glf22:python:snake:snake-zu-verbessern [2023/01/27 05:55] – Olaf Schnürer | lehrkraefte:snr:informatik:glf22:python:snake:snake-zu-verbessern [2023/02/05 13:01] (current) – Olaf Schnürer | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Snake program ====== | ||
| + | |||
| + | <code python snake-zu-verbessern.py> | ||
| + | import pygame | ||
| + | from pygame.locals import * | ||
| + | from random import * | ||
| + | |||
| + | # Geschwindigkeit des Spiels bzw. genauer | ||
| + | # Frequenz, mit der das Spielfeld neu gezeichnet wird. | ||
| + | FRAMES_PER_SECOND_AM_ANFANG = 7 | ||
| + | VERGROESSERUNG_PRO_APFEL = 0.5 | ||
| + | |||
| + | # Das Spielfeld besteht aus kleinen rechteckigen Boxen mit Koordinaten | ||
| + | # 0 bis MAXX (jeweils einschliesslich) in x-Richtung und | ||
| + | # 0 bis MAXY (jeweils einschliesslich) in y-Richtung. | ||
| + | # Achtung: y-Achse zeigt nach unten (wie Zeilennummern) | ||
| + | MAXX = 24 | ||
| + | MAXY = 15 | ||
| + | |||
| + | # Festlegung von Breite und Höhe einer Box in Pixel. | ||
| + | # Meist sind Breite und Höhe gleich, was quadratische Boxen liefert. | ||
| + | SEITENLAENGE_BOX = 40 | ||
| + | |||
| + | # Es geht auch mit Bildern (Hintergrund, | ||
| + | # und Sound (Crash bzw. Game over). | ||
| + | # Suche dafür geeignete Dateien und speichere diese am besten | ||
| + | # in demselben Verzeichnis wie dieses Programm. | ||
| + | # In der Funktion '' | ||
| + | # dann die Namen deiner Dateien (evtl. inklusive Pfad) angeben. | ||
| + | |||
| + | BOXEN_STATT_BILDER = True | ||
| + | EINFARBIGER_HINTERGRUND = True | ||
| + | MIT_SOUND = False | ||
| + | |||
| + | # Ab hier Konstanten, die von den obigen abhängen. | ||
| + | ANZAHL_BOXEN_X = MAXX + 1 | ||
| + | ANZAHL_BOXEN_Y = MAXY + 1 | ||
| + | |||
| + | FENSTER_BREITE = ANZAHL_BOXEN_X * SEITENLAENGE_BOX | ||
| + | FENSTER_HOEHE = ANZAHL_BOXEN_Y * SEITENLAENGE_BOX | ||
| + | PLATZ_FUER_TEXT = 5 * SEITENLAENGE_BOX | ||
| + | |||
| + | # Farben per Rot-, Grün- und Blauwert | ||
| + | # (jeweils auf Skala von 0 bis 255). | ||
| + | # Wer will, kann hier zusätzliche eigene Farben festlegen. | ||
| + | # | ||
| + | ROT | ||
| + | GRUEN | ||
| + | BLAU = ( 0, 0, 255) | ||
| + | GELB = (255, 255, 0) | ||
| + | MAGENTA = (255, 0, 255) | ||
| + | CYAN = ( 0, 255, 255) | ||
| + | WEISS | ||
| + | SCHWARZ = ( 0, 0, 0) | ||
| + | HELLGRAU = | ||
| + | DUNKELGRAU = (160, 160, 160) | ||
| + | HINTERGRUND_FARBE = SCHWARZ | ||
| + | # HINTERGRUND_FARBE = HELLGRAU | ||
| + | |||
| + | # | ||
| + | # Definition der Klasse Punkt. Bitte einfach akzeptieren. | ||
| + | # | ||
| + | # Könnte stattdessen " | ||
| + | # jedoch sind solche Vektoren wohl nicht " | ||
| + | # die Zeile zur Ausgabe der " | ||
| + | # | ||
| + | class Punkt: | ||
| + | def __init__(self, | ||
| + | self.x = x | ||
| + | self.y = y | ||
| + | |||
| + | def __str__(self): | ||
| + | return f' | ||
| + | | ||
| + | def __repr__(self): | ||
| + | return f' | ||
| + | | ||
| + | def __eq__(self, | ||
| + | return self.x == other.x and self.y == other.y | ||
| + | |||
| + | def __add__(self, | ||
| + | return Punkt(self.x + other.x, self.y + other.y) | ||
| + | |||
| + | def __hash__(self): | ||
| + | return hash(str(self)) | ||
| + | # | ||
| + | # Ende der Definition der Klasse Punkt. | ||
| + | # | ||
| + | |||
| + | def zeichne_box(p, | ||
| + | rechteck = pygame.Rect(p.x * SEITENLAENGE_BOX + 1, p.y * SEITENLAENGE_BOX + 1, SEITENLAENGE_BOX - 1, SEITENLAENGE_BOX - 1) | ||
| + | pygame.draw.rect(leinwand, | ||
| + | |||
| + | def zeichne_gitter(): | ||
| + | for x in range(ANZAHL_BOXEN_X + 1): | ||
| + | pygame.draw.line(leinwand, | ||
| + | for y in range(ANZAHL_BOXEN_Y + 1): | ||
| + | pygame.draw.line(leinwand, | ||
| + | |||
| + | def schreibe(x, y, text, groesse): | ||
| + | schrift = pygame.font.SysFont(' | ||
| + | formatierter_text = schrift.render(text, | ||
| + | rechteck = formatierter_text.get_rect() | ||
| + | rechteck.center = (round((x + 0.5) * SEITENLAENGE_BOX), | ||
| + | leinwand.blit(formatierter_text, | ||
| + | |||
| + | def schreibe_transparent(x, | ||
| + | schrift = pygame.font.SysFont(' | ||
| + | formatierter_text = schrift.render(text, | ||
| + | rechteck = formatierter_text.get_rect() | ||
| + | rechteck.center = (round((x + 0.5) * SEITENLAENGE_BOX), | ||
| + | if rechteck.x >= 0 and rechteck.y >= 0: | ||
| + | text_flaeche = pygame.Surface((rechteck.x, | ||
| + | text_flaeche.fill(WEISS) | ||
| + | text_flaeche.blit(formatierter_text, | ||
| + | text_flaeche.set_alpha(50) | ||
| + | leinwand.blit(formatierter_text, | ||
| + | |||
| + | def neue_apfelposition(): | ||
| + | a = Punkt(randrange(0, | ||
| + | while a in schlange: | ||
| + | a = Punkt(randrange(0, | ||
| + | return a | ||
| + | |||
| + | def lade_bilder_und_sound(): | ||
| + | global hintergrundbild, | ||
| + | |||
| + | if not EINFARBIGER_HINTERGRUND: | ||
| + | hintergrundbild = pygame.image.load(' | ||
| + | hintergrundbild = pygame.transform.scale(hintergrundbild, | ||
| + | |||
| + | if not BOXEN_STATT_BILDER: | ||
| + | bild_apfel = pygame.image.load(' | ||
| + | bild_apfel = pygame.transform.scale(bild_apfel, | ||
| + | bild_kopf_der_schlange = pygame.image.load(' | ||
| + | bild_kopf_der_schlange = pygame.transform.scale(bild_kopf_der_schlange, | ||
| + | |||
| + | if MIT_SOUND: | ||
| + | crash_sound = pygame.mixer.Sound(' | ||
| + | apfel_ess_sound = pygame.mixer.Sound(' | ||
| + | |||
| + | def zeige_bild(bild, | ||
| + | rechteck = pygame.Rect(p.x * SEITENLAENGE_BOX + 1, p.y * SEITENLAENGE_BOX + 1, SEITENLAENGE_BOX - 1, SEITENLAENGE_BOX - 1) | ||
| + | leinwand.blit(bild, | ||
| + | |||
| + | pygame.init() | ||
| + | uhr = pygame.time.Clock() | ||
| + | leinwand = pygame.display.set_mode((FENSTER_BREITE + 1, FENSTER_HOEHE + 1 + PLATZ_FUER_TEXT)) | ||
| + | pygame.display.set_caption(' | ||
| + | lade_bilder_und_sound() | ||
| + | |||
| + | laenge = 4 | ||
| + | |||
| + | ### GEMEINSAM ANSCHAUEN: | ||
| + | # Initialisiere die Liste, deren Einträge die Koordinaten der Quadrate der Schlange sind. | ||
| + | # Der 0-te Eintrag enhält die Position des Kopfes der Schlange. | ||
| + | schlange = [Punkt(4, 7), Punkt(3, 7), Punkt(2, 7), Punkt(1, 7)] | ||
| + | ### | ||
| + | |||
| + | # Oder besser, da abhängig von der Variablen " | ||
| + | #I schlange = [] | ||
| + | #I for i in range(laenge): | ||
| + | #I | ||
| + | # Und noch besser bzw. kürzer geht das so: | ||
| + | # schlange = [Punkt(laenge - i, MAXY // 2) for i in range(laenge)] | ||
| + | farbe_schlange = GRUEN | ||
| + | |||
| + | # Änderung der Spielerposition pro Spielzyklus: | ||
| + | bewegungsrichtung = Punkt(0, 0) | ||
| + | |||
| + | # Hilfsvariablen, | ||
| + | neue_richtung = bewegungsrichtung | ||
| + | richtung_vor_stopp = Punkt(0, 0) | ||
| + | |||
| + | # Initialisiere Apfelposition (nicht auf Schlange!) und Farbe. | ||
| + | apfel = neue_apfelposition() | ||
| + | farbe_apfel = ROT | ||
| + | |||
| + | # Initialisierung der " | ||
| + | frames_per_second = FRAMES_PER_SECOND_AM_ANFANG | ||
| + | spiel_aktiv = True | ||
| + | crash = False | ||
| + | gefressene_aepfel = 0 | ||
| + | |||
| + | # Hier startet die "game loop". | ||
| + | while spiel_aktiv: | ||
| + | # Graphische Darstellung des Spielgeschehens: | ||
| + | |||
| + | # Hintergrund | ||
| + | if EINFARBIGER_HINTERGRUND: | ||
| + | leinwand.fill(HINTERGRUND_FARBE) | ||
| + | else: | ||
| + | leinwand.fill(HINTERGRUND_FARBE) | ||
| + | leinwand.blit(hintergrundbild, | ||
| + | |||
| + | # Ausgabe diverser Zahlen unter dem Spielfeld | ||
| + | schreibe(MAXX // 4, MAXY + 1, f' | ||
| + | schreibe(3 * MAXX // 4, MAXY + 1, f' | ||
| + | schreibe(MAXX // 4, MAXY + 2, f' | ||
| + | schreibe(3 * MAXX // 4, MAXY + 2, f' | ||
| + | schreibe(MAXX // 4, MAXY + 3, f' | ||
| + | schreibe(3 * MAXX // 4, MAXY + 3, f' | ||
| + | text = f' | ||
| + | schreibe(MAXX // 2, MAXY + 4, text, min(1, 1.5 * MAXX / len(text))) | ||
| + | schreibe(MAXX // 2, MAXY + 5, 'Space key: pause game', 0.8) | ||
| + | |||
| + | # Raster zeichnen | ||
| + | zeichne_gitter() | ||
| + | |||
| + | # Apfel zeichnen | ||
| + | if BOXEN_STATT_BILDER: | ||
| + | zeichne_box(apfel, | ||
| + | schreibe_transparent(apfel.x, | ||
| + | else: | ||
| + | zeige_bild(bild_apfel, | ||
| + | schreibe_transparent(apfel.x, | ||
| + | | ||
| + | ### GEMEINSAM ANSCHAUEN: | ||
| + | # Schlange zeichnen | ||
| + | for element in schlange: | ||
| + | zeichne_box(element, | ||
| + | schreibe_transparent(element.x, | ||
| + | ### | ||
| + | |||
| + | if not BOXEN_STATT_BILDER: | ||
| + | zeige_bild(bild_kopf_der_schlange, | ||
| + | schreibe_transparent(schlange[0].x, | ||
| + | |||
| + | # Alles bis jetzt " | ||
| + | pygame.display.update() | ||
| + | uhr.tick(frames_per_second) | ||
| + | |||
| + | # Verarbeitung von Tastatureingaben: | ||
| + | for ereignis in pygame.event.get(): | ||
| + | if ereignis.type == QUIT: | ||
| + | print(" | ||
| + | spiel_aktiv = False | ||
| + | elif ereignis.type == KEYDOWN: | ||
| + | if ereignis.key == K_ESCAPE: | ||
| + | print(" | ||
| + | spiel_aktiv = False | ||
| + | elif ereignis.key == K_q: | ||
| + | print(" | ||
| + | spiel_aktiv = False | ||
| + | elif ereignis.key == K_SPACE: | ||
| + | print(" | ||
| + | if bewegungsrichtung != Punkt(0, 0): | ||
| + | richtung_vor_stopp = bewegungsrichtung | ||
| + | bewegungsrichtung = Punkt(0, 0) | ||
| + | else: | ||
| + | bewegungsrichtung =richtung_vor_stopp | ||
| + | else: | ||
| + | if ereignis.key == K_r: | ||
| + | print(" | ||
| + | neue_richtung = Punkt(-1, -1) | ||
| + | elif ereignis.key == K_f: | ||
| + | print(" | ||
| + | neue_richtung = Punkt(-1, 1) | ||
| + | elif ereignis.key == K_t: | ||
| + | print(" | ||
| + | neue_richtung = Punkt(1, -1) | ||
| + | elif ereignis.key == K_g: | ||
| + | print(" | ||
| + | neue_richtung = Punkt(1, 1) | ||
| + | # Die folgende if-Bedingung verhindert, dass die | ||
| + | # Snake die Richtung umkehren kann (und mit sich selbst kollidiert). | ||
| + | ### GEMEINSAM ANSCHAUEN: | ||
| + | if schlange[1] != schlange[0] + neue_richtung: | ||
| + | bewegungsrichtung = neue_richtung | ||
| + | ### | ||
| + | | ||
| + | # Bewegen der Schlange: | ||
| + | if bewegungsrichtung != Punkt(0, 0): | ||
| + | # Falls die Schlange die gewünschte Länge hat: | ||
| + | # Beseitige das letzte Element aus der Liste " | ||
| + | if len(schlange) == laenge: | ||
| + | ### GEMEINSAM ANSCHAUEN: | ||
| + | schlange.pop() | ||
| + | ### | ||
| + | |||
| + | ### GEMEINSAM ANSCHAUEN: | ||
| + | # Neues Feld, auf das sich die Schlange bewegt: | ||
| + | neue_position = schlange[0] + bewegungsrichtung | ||
| + | ### | ||
| + | |||
| + | # Verhalten am Spielfeldrand: | ||
| + | if neue_position.x < 0: | ||
| + | neue_position.x = MAXX | ||
| + | if neue_position.x > MAXX: | ||
| + | neue_position.x = 0 | ||
| + | if neue_position.y < 0: | ||
| + | neue_position.y = MAXY | ||
| + | if neue_position.y > MAXY: | ||
| + | neue_position.y = 0 | ||
| + | |||
| + | ### GEMEINSAM ANSCHAUEN: | ||
| + | # Selbstkollision: | ||
| + | if neue_position in schlange: | ||
| + | crash = True | ||
| + | ### | ||
| + | |||
| + | spiel_aktiv = False | ||
| + | if MIT_SOUND: | ||
| + | pygame.mixer.Sound.play(crash_sound) | ||
| + | else: | ||
| + | ### GEMEINSAM ANSCHAUEN: | ||
| + | # Hänge die neue Position vorne an die Liste " | ||
| + | schlange.insert(0, | ||
| + | ### | ||
| + | |||
| + | # Apfel erreicht? | ||
| + | if neue_position == apfel: | ||
| + | gefressene_aepfel = 1 | ||
| + | if MIT_SOUND: | ||
| + | pygame.mixer.Sound.play(apfel_ess_sound) | ||
| + | laenge = laenge * 2 | ||
| + | frames_per_second = frames_per_second + VERGROESSERUNG_PRO_APFEL | ||
| + | apfel = neue_apfelposition() | ||
| + | # Ende der game loop. | ||
| + | |||
| + | # Nach Abbruch oder Crash: | ||
| + | |||
| + | if crash: | ||
| + | schreibe(MAXX // 2, MAXY // 2, 'GAME OVER', 2) | ||
| + | schreibe(MAXX // 2, MAXY // 2 + 3, 'press any key', 1) | ||
| + | pygame.display.update() | ||
| + | pygame.time.delay(500) | ||
| + | for ereignis in pygame.event.get(): | ||
| + | pygame.time.delay(100) | ||
| + | while pygame.event.get() == []: | ||
| + | pygame.time.delay(100) | ||
| + | | ||
| + | pygame.quit() | ||
| + | exit() | ||
| + | </ | ||
| + | |||
| + | ===== Link zur Snake-Seite ===== | ||
| + | |||
| + | * [[lehrkraefte: | ||