Ich beschäftige mich erst seit kurzem mit Python und möchte einer meiner ersten Python-Projekte hier vorstellen. Dabei habe ich mit Pygame und Tkinter gearbeitet, um das Spiel Hangman in Python umzusetzen (random wird auch verwendet). Das Programm an sich funktioniert ohne Fehlermeldungen, doch ich wäre sehr glücklich über Feedback, da bei meinen Codes als Anfänger wahrscheinlich viel Verbesserungspotenzial besteht. Wie könnte man z.B meinen Code "eleganter" gestalten oder vereinfachen? Welche Funktionen wären für das Programm noch praktisch? Ist die Struktur mit zwei Klassen sinnvoll? Welche evt. Schwachstellen müssen beseitigt werden? Ich bin dankbar für jegliche Art von Input und Tipps.
Hier ist der Code:
Code: Alles auswählen
# Imports
from tkinter import *
import random
import pygame as pg # Muss zusätzlich zu Python heruntergeladen werden
# Farben (nicht alle werden gebraucht)
Blue = (0, 0, 50)
Lila = (255, 0, 255)
White = (255, 255, 255)
Red = (255, 0, 0)
# Das self macht Variabeln von ausserhalb der Klasse und der Methode innerhalb der Klasse abrufbar
class choseWord():
def __init__(self) : # Wird beim Aufrufen der Klasse ausgeführt
self.createWindow()
def createWindow(self):
# Erstellung des Fensters und seiner Elemente
Fenster = Tk()
Fenster.title("Anfang")
Fenster.config(height=300, width=600)
Anzeige = Label(Fenster, text="Wie willst du spielen?")
Anzeige.place(width=500, height=100, x=50, y=20)
Eingabe = Entry(Fenster)
Eingabe.place(width=500, height=50, x=50, y=220)
# Erstellung von Buttons, welche mit commands verküpft werden (man braucht lamda, da zwei commands)
Knopf1 = Button(Fenster, text="Eigenes Wort einreichen", command=lambda: [self.ownWord(Eingabe), self.furtherProcessing(Fenster)])
Knopf2 = Button(Fenster, text="Zufallswort", command=lambda: [self.randomWord(), self.furtherProcessing(Fenster)])
Knopf1.place(width=200, height=100, x=50, y=100)
Knopf2.place(width=200, height=100, x=350, y=100)
# Schleife
Fenster.mainloop()
def ownWord(self, par1):
self.Wort = par1.get() # Man bekommt, den Input des Fenster-Elements Eingabe
def randomWord(self):
# Aus Datei werden Wörter in die Liste Wörter reinkopiert, und aus dieser wird ein Wort zufällig ausgewählt
Wörter = []
Datei = open("Wörter_Liste.txt", "r") # Textdatei muss im selbem Ordner wie der Code vorhanden sein
for Zeile in Datei:
Wörter.append(Zeile)
Datei.close()
self.Wort = Wörter[random.randint(0, 99)]
def furtherProcessing(self, par1):
# Wort wird für den späteren Gebrauch modifiziert, Variabeln die für die Klasse Hangman() benötigt werden, werden erstellt
self.Buchstaben = []
self.Wort = self.Wort.rstrip("\n").upper()
for Buchstabe in self.Wort:
self.Buchstaben.append(Buchstabe)
Länge = len(self.Wort)
self.Wortstand = Länge * "_ "
par1.destroy() # Nötig, da tkinter nicht mit pygame kompatibel
# Player-Klasse
class Hangman (pg.sprite.Sprite) :
def __init__(self) :
while True: # Beim Aufrufen der Klasse, wird immer wieder erstmal choseWord() ausgeführt und dann self.play(), die sich der Methoden der Klasse Hangman() bedient
# Try-except-Struktur braucht man um Fehlermeldung beim Schliessen des tkinter-Fensters zu vermeiden
try:
self.tkinterPart = choseWord()
super().__init__()
pg.init()
self.play()
pg.quit()
except: # Bei Fehler wird pygame und die while-Schleife verlassen
pg.quit()
break
def play(self):
# Fenster wird erstellt, Liste Bilder wird gefüllt
Fenster = pg.display.set_mode((1200, 600))
Bilder = self.initGraphics()
# Grundlegene Variabeln für das Programm werden für die Klasse erstellt
Buchstaben = self.tkinterPart.Buchstaben # Kein self, da das nicht abrufbar sein soll
self.Wortstand = self.tkinterPart.Wortstand
self.Eingabe = ""
self.beiVerlieren = "" # Wird nur geändert falls verloren (sonst kein Absatz möglich)
self.Fehler = 0
self.finished = False
self.running = True
# While-Schleife: 1. Was macht der User und auf Input reagieren; 2. Prüfen ob gewonnen und verloren; 3. Oberfläche updaten
while self.running:
for event in pg.event.get():
self.checkEvent(event, Buchstaben)
self.wonOrLost(Fenster, Buchstaben)
self.update(Fenster, Bilder)
def checkEvent(self, event, Buchstaben):
if event.type == pg.QUIT:
# Wenn rotes Kreuz gedrückt, pygame verlassen und while-Schleife von self.play() verlassen
# Dadurch kann bei der while-Schleife in der Initialisierung der nächste Schritt erfolgen
self.running = False
# Sei angemerkt: bei pygame gibt es keinen einfachen input()-Befehl wie bei tkinter
if event.type == pg.KEYDOWN and self.finished == False: # Wenn finished auf True gestzt kann man nicht mehr den Wortstand verändern und keine zusätzlichen Fehler machen
# Wenn etwas "eingereicht" wird, wird die Eingabe in Grossbuchstaben konvertiert (das gesuchte Wort wurde ja auch in Grossbuchstaben konvertiert)
if event.key == pg.K_RETURN:
self.Eingabe = self.Eingabe.upper()
# Wenn Eingabe in der Liste Buchtaben ist, wird folgende Methode aufgerufen
if self.Eingabe in Buchstaben:
self.changeWortstand(Buchstaben)
# Wenn Eingabe nicht in der Liste Buchtaben ist, hat man ein Fehler mehr gemacht
else:
self.Fehler += 1
# Nach dem Einreichen, Eingabe wieder frei
self.Eingabe = ""
elif event.key == pg.K_BACKSPACE: # Eingabe kann wieder gelöscht werden
self.Eingabe = self.Eingabe[:-1]
else: # Jedes Tastenereignis wird zur Eingabe angerechnet, wenn es sich nicht um Return oder Löschen handelt
self.Eingabe += event.unicode
def changeWortstand(self, Buchstaben):
while self.Eingabe in Buchstaben: # Wenn der eingegebene Buchtabe mehrmals in den übbrig gebliebenen Buchtaben des Wortes vorkommt
Pos = Buchstaben.index(self.Eingabe) # Wo befindet sich der eingegebene Buchtabe im Wort
Buchstaben[Pos] = False # Satt dem gefunden Buchstaben in der Liste Buchtaben, steht jetzt der Wert False
self.Wortstand = self.Wortstand[:2*Pos] + self.Eingabe + self.Wortstand[2*Pos + 1:] # Der Wortstand (dieser wird geblitet) wird an der richtigen Stelle verändert
def wonOrLost(self, Fenster, Buchstaben):
if self.Fehler > 7: # Wenn zu viele Fehler: Auflösung, und self.finished wird auf True gesetzt
self.Eingabe = "Das richtige Wort war: "
self.beiVerlieren = self.tkinterPart.Wort
self.finished = True
if all(element == False for element in Buchstaben) and self.finished == False: # Üperprüfen, ob in der Liste Buchtaben alles auf False gesetzt wurde: gewonnen!
Komplimente = ["Du bist ein wahrer Meister!", "Knapp dem Strang entkommen!", "Yeah, Hinrichtung verschoben!", "Du hast es geschafft!", "Du hast es verdient!", "Ein Wunder ist geschehen!", "Glückwunsch!"]
self.Eingabe = random.choice(Komplimente)
self.finished = True
def update (self, Fenster, Bilder):
# Sprites in Fenster (neu) positionieren
Fenster.fill(Blue) # Altes wird einfach überdeckt
Fenster.blit(Bilder[self.Fehler], (20, 20)) # Je mehr Fehler, desto weiter in der Bilder-Liste
Fenster.blit(self.showMessage(self.Wortstand, Lila), (20, 500))
Fenster.blit(self.showMessage(self.Eingabe, Lila), (400, 200))
Fenster.blit(self.showMessage(self.beiVerlieren, Lila), (400, 300))
pg.display.update()
def initGraphics(self):
Bilder = []
for Nr in range(1, 10):
Bild = pg.image.load("/Applications/Python 3.11/Hangman/Hangman_Projekt/Hangman_Bilder/Hang" + str(Nr) + ".jpeg") # Bilddateien müssen vorhanden sein, Pfadweg ggf. anpassen
Bild = pg.transform.scale(Bild, (350,450)) # Grösse der Bilder anngepasst
Bilder.append(Bild)
return Bilder # Ausgabe der Methode
def showMessage(self, text, Farbe): # parameter text wird so verarbeitet, dass man ihn blitten kann
Font = pg.font.SysFont("arial", 48)
Text = Font.render(text, True, Farbe)
return Text
pyHangman = Hangman()
PS: .
In der choseWord-Klasse wird eine Datei "Wörter_Liste.txt" genutzt, da ich die ca 100 Wörter nicht ins Programm selber reinschreiben wollte. Da ich nicht herausfinden konnte wie man Dateien anfügt, ist diese Datei leider nicht verfügbar.
Ausserdem kann ich die 9 verwendeten Bilder nicht anhängen, die das Galgenmännchen progressiv zeigen. Der Dateipfad müsste eh individuell angepasst werden.