Hangman (Wörterraten)

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
maik
User
Beiträge: 6
Registriert: Dienstag 2. Mai 2017, 11:27

Hangman (Wörterraten)

Beitragvon maik » Freitag 4. August 2017, 09:11

Moin Moin,

ich habe ein kleines Spiel Programmiert mit Grafischer Benutzeroberfläche in dem man Wörter erraten muss. Möglich ist das ganze in Deutscher und in Englischer Sprache, Es ist ein kleines Punktesystem dabei und am Ende wird die erreichte Gesamtpunktzahl angezeigt. Als nächsten Schritt möchte ich eine Highscore-Liste einfügen und das ganze über eine Webseite nutzbar machen. Außerdem sollen Fehlerhafte Wörter gemeldet werden können, evtl. per e-mail Benachrichtigung an mich über den bereits eingefügten Button auf der rechten Seite.

Wichtig: Es müssen die zwei Dateien (wortliste_de.txt und wortliste_en.txt) mit den Wortlisten im gleichen Verzeichnis liegen.
Wortliste DE: https://www.dropbox.com/s/23iccovudy2z37x/wortliste_de.txt?dl=0
Wortliste EN: https://www.dropbox.com/s/b6tf2yd1qpcc2ai/wortliste_en.txt?dl=0


  1. from tkinter import *
  2. from random import *
  3.  
  4.  
  5. class Hangman:
  6.     """Spiel"""
  7.     def __init__(self):
  8.         # Fenster und Rahmen
  9.         self.fenster = Tk()
  10.         self.fenster.title('Wörterraten')
  11.         self.font = 'Helvetica'
  12.         self.entry = StringVar()
  13.         self.rahmen_oben = Frame(master=self.fenster, relief=FLAT)
  14.         self.rahmen_mitte = Frame(master=self.fenster, relief=FLAT, width=110)
  15.         self.rahmen_unten = Frame(master=self.fenster, relief=FLAT)
  16.         self.rahmen_unten_l = Frame(master=self.rahmen_unten, relief=FLAT, width=10)
  17.         self.rahmen_unten_r = Frame(master=self.rahmen_unten, relief=FLAT, width=20)
  18.         # Benutzeroberfläche
  19.         self.titel = Label(master=self.rahmen_oben, text='Wörterraten', font=(self.font, 24), width=30)
  20.         self.punkte_label = Label(master=self.rahmen_oben, text='Punkte: ', font=(self.font, 16))
  21.         self.wort_label = Label(master=self.rahmen_oben, text='Wort: ', font=(self.font, 16))
  22.         self.wortzahl = Wortzahl(self.rahmen_oben)
  23.         self.max_woerter = Label(master=self.rahmen_oben, text='/5', font=(self.font, 16), bg='LightCyan2')
  24.         self.tip = Label(master=self.rahmen_oben, text='Treffer: +1 Pkt.\tFehlversuch: -1 Pkt.\tWort erraten: +5 Pkt.',
  25.                          font=(self.font, 10), fg='black')
  26.         # Objekte
  27.         self.sprache = Sprache(self.rahmen_unten_l)
  28.         self.punkte = Punkte(self.rahmen_oben, 20)
  29.         self.ausgabe = Ausgabe(self.rahmen_mitte, self.sprache)
  30.         self.fehler_melden = Fehler(self.rahmen_mitte, self.sprache, self.ausgabe)
  31.         self.eingabe = Entry(master=self.rahmen_unten_r, textvariable=self.entry, font=(self.font, 24),
  32.                              width=2, bg='white')
  33.         self.ok = ButtonOK(self.rahmen_unten_r, self.eingabe, self.ausgabe, self.punkte, self.wortzahl)
  34.         self.neu = ButtonNeu(self.rahmen_unten_r, self.punkte, self.sprache, self.ausgabe, self.wortzahl, self.ok)
  35.         self.layout()
  36.         self.fenster.mainloop()
  37.  
  38.     def layout(self):
  39.         self.rahmen_oben.pack(side=TOP, padx=20)
  40.         self.rahmen_mitte.pack(side=TOP, fill=X)
  41.         self.rahmen_unten.pack(side=TOP, fill=X)
  42.         self.rahmen_unten_l.pack(side=LEFT)
  43.         self.rahmen_unten_r.pack(side=RIGHT)
  44.         self.titel.pack(padx=20, pady=10, side=TOP)
  45.         self.tip.pack(side=BOTTOM)
  46.         self.punkte.label.pack(side=RIGHT)
  47.         self.punkte_label.pack(side=RIGHT)
  48.         self.wort_label.pack(side=LEFT)
  49.         self.wortzahl.label.pack(side=LEFT)
  50.         self.max_woerter.pack(side=LEFT)
  51.         self.fehler_melden.button.pack(side=RIGHT, padx=20, anchor=E)
  52.         self.ausgabe.label.pack(padx=10, pady=10, side=TOP)
  53.         self.neu.button_neu.pack(side=LEFT, padx=20, pady=5)
  54.         self.eingabe.pack(side=LEFT)
  55.         self.ok.button.pack(side=RIGHT, padx=20, pady=5)
  56.  
  57.  
  58. class Punkte:
  59.     def __init__(self, master, p):
  60.         self.punkte = IntVar()
  61.         self.punkte.set(p)
  62.         self.label = Label(master, textvariable=self.punkte, width=2, font=('Helvetica', 16), bg='LightCyan2')
  63.  
  64.     def aktualisiere(self, wert):
  65.         self.punkte.set(self.punkte.get() + wert)
  66.  
  67.  
  68. class Wortzahl:
  69.     def __init__(self, master):
  70.         self.wortzahl = IntVar()
  71.         self.wortzahl.set(1)
  72.         self.label = Label(master, textvariable=self.wortzahl, font=('Helvetica', 16), bg='LightCyan2')
  73.  
  74.  
  75. class Ausgabe:
  76.     def __init__(self, master, sprache):
  77.         self.master = master
  78.         self.sprache = sprache
  79.         self.wort = self.sprache.wg.get_wort()
  80.         self.ausgabe = len(self.wort) * '-'
  81.         self.label = Label(self.master, text=self.ausgabe, width=int(len(self.wort)*1.5),
  82.                            font=('Helvetica', 30), bg='white')
  83.  
  84.     def neues_wort(self):
  85.         self.wort = self.sprache.wg.get_wort()
  86.         self.ausgabe = len(self.wort) * '-'
  87.         self.label.config(text=len(self.wort)*'-')
  88.         self.label.config(width=int(len(self.wort)*1.5))
  89.  
  90.  
  91. class Fehler:
  92.     def __init__(self, master, sprache, ausgabe):
  93.         self.master = master
  94.         self.sprache = sprache
  95.         self.ausgabe = ausgabe
  96.         self.button = Button(master, text='Fehlerhaftes\nWort melden',
  97.                              font=('Helvetica', 10), command=self.fehler_melden)
  98.  
  99.     def fehler_melden(self):
  100.         wort = self.ausgabe.wort
  101.         sprache = self.sprache.sprache.get()
  102.         print('Gemeldetes Wort: ' + wort + ', Sprache: ' + sprache)
  103.  
  104.  
  105. class Sprache:
  106.     def __init__(self, master):
  107.         self.master = master
  108.         self.sprache = StringVar()
  109.         self.font = 'Helvetica'
  110.         self.de = Radiobutton(master, text='Deutsch', font=self.font, value='de',
  111.                               variable=self.sprache)
  112.         self.en = Radiobutton(master, text='Englisch', font=self.font, value='en',
  113.                               variable=self.sprache)
  114.         self.de.pack(padx=20)
  115.         self.de.select()
  116.         self.en.pack(padx=20, pady=10)
  117.         self.wg = Wortgenerator(self.sprache)
  118.  
  119.     def get_datei(self):
  120.         return self.sprache.get()
  121.  
  122.     def neuer_wg(self):
  123.         self.wg = Wortgenerator(self.sprache)
  124.  
  125.  
  126. class ButtonNeu:
  127.     def __init__(self, master, punkte, sprache, ausgabe, wortzahl, ok):
  128.         self.master = master
  129.         self.punkte = punkte
  130.         self.sprache = sprache
  131.         self.ausgabe = ausgabe
  132.         self.wz = wortzahl
  133.         self.ok = ok
  134.         self.button_neu = Button(master, text='Neu', font=('Helvetica', 12), command=self.neues_spiel)
  135.  
  136.     def neues_spiel(self):
  137.         self.sprache.neuer_wg()
  138.         self.punkte.punkte.set(20)
  139.         self.wz.wortzahl.set(1)
  140.         self.ausgabe.neues_wort()
  141.         self.ok.button.config(command=self.ok.pruefe)
  142.  
  143.  
  144. class ButtonOK:
  145.     def __init__(self, master, eingabe, ausgabe, punkte, wortzahl):
  146.  
  147.         self.master = master
  148.         self.eingabe = eingabe
  149.         self.ausgabe = ausgabe
  150.         self.punkte = punkte
  151.         self.wz = wortzahl
  152.         self.button = Button(master, text='OK', font=('Helvetica', 12), command=self.pruefe)
  153.  
  154.     def pruefe(self):
  155.         z = self.eingabe.get().upper()
  156.         ausgabeliste = list(self.ausgabe.ausgabe)
  157.         self.eingabe.delete(0, 2)
  158.         if z in self.ausgabe.wort and z not in ausgabeliste:
  159.             # Ausgabe aktualisieren
  160.             for b in range(len(self.ausgabe.wort)):
  161.                 if self.ausgabe.wort[b] == z:
  162.                     ausgabeliste[b] = z
  163.             self.ausgabe.ausgabe = ausgabeliste
  164.             self.ausgabe.label.config(text=''.join(ausgabeliste))
  165.             self.punkte.aktualisiere(1)
  166.             # Wenn Wort vollständig, wird neues Wort erzeugt
  167.             if '-' not in self.ausgabe.ausgabe:
  168.                 if self.wz.wortzahl.get() == 5:
  169.                     self.ausgabe.label.config(text=str('Fertig! ' + str(self.punkte.punkte.get()) + ' Punkte'))
  170.                     self.ausgabe.label.config(width=18)
  171.                     self.button.config(command='')
  172.                 else:
  173.                     self.button.after(1000, self.ausgabe.neues_wort)
  174.                     self.punkte.aktualisiere(5)
  175.                     self.wz.wortzahl.set(self.wz.wortzahl.get() + 1)
  176.         else:
  177.             self.punkte.aktualisiere(-1)
  178.  
  179.  
  180. class Wortgenerator:
  181.     def __init__(self, sprache):
  182.         self.sprache = sprache.get()
  183.         datei = 'wortliste_' + self.sprache + '.txt'
  184.         self.wortliste = []
  185.         f = open(datei, 'r')
  186.         self.liste = f.readlines()
  187.         f.close()
  188.         for x in self.liste:
  189.             w = list(x)
  190.             del w[-1]
  191.             wort = ''
  192.             for buchstabe in w:
  193.                 wort += buchstabe
  194.             self.wortliste.append(wort.upper())
  195.  
  196.     def get_wort(self):
  197.         # Liefert aus Wortliste ein Zufallswort
  198.         return choice(self.wortliste)
  199.  
  200. Hangman()
Sirius3
User
Beiträge: 6659
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Hangman (Wörterraten)

Beitragvon Sirius3 » Freitag 4. August 2017, 09:40

@maik: *-Importe sind böse, weil man nicht kontrollieren kann, was da einem in den Namensraum geladen wird. Die __init__-Methode einer Klasse soll eine Instanz initialisieren. Hangman.__init__ macht viel mehr, aka, das mainloop gehört da nicht rein. Warum sind Erzeugen von Widgets und deren Platzierung in unterschiedlichen Methoden? Würde das .pack immer gleich beim Erzeugen stehen, könnte man sie viele Instanzattribute sparen.
Die Klassen Punkte, Wortzahl, Ausgabe, etc. sind eigentlich keine wirklichen Klassen. Das Erzeugen der Labels wäre besser in Hangman.__init__ aufgehoben und das Aktualisieren der Labels als Methoden in Hangman. Bei ButtonNeu sieht man schön, dass das eigentlich keine eigenständige Klasse ist, weil 6! Objekte aus dem übergeordneten Fenster als Argumente übergeben werden müssen. Wenn man über mehrere Objekte hinweg auf etwas zugreifen muß (self.ok.button.config) ist das auch ein Zeichen dafür, dass hier Aufgaben falsch getrennt wurden.

Bei Wortgenerator solltest Du Dir anschauen, welche Methoden Strings haben, sprache sollte gleich ein String sein, so dass es egal ist, woher dieser Wert kommt. Nutze .format statt Strings mit + zusammenzustückeln. Kommentare, was eine Funktion macht, schreibt man in Python als sogenannten Doc-String:
  1. class Wortgenerator:
  2.     def __init__(self, sprache):
  3.         self.sprache = sprache
  4.         datei = 'wortliste_{}.txt'.format(sprache)
  5.         with open(datei, 'r') as lines:
  6.             self.wortliste = [line.strip() for line in lines]
  7.  
  8.     def get_wort(self):
  9.         """ Liefert aus Wortliste ein Zufallswort """
  10.         return choice(self.wortliste)
BlackJack

Re: Hangman (Wörterraten)

Beitragvon BlackJack » Freitag 4. August 2017, 09:59

@maik: Die Code-Aufteilung ist falsch würde ich sagen. Ich sehe da keine Trennung zwischen Programmlogik und GUI und Klassen die entweder nicht richtig kapseln oder einfach Funktionen sein könnten. Im Grunde sind das meiste Methoden die jeweils in eine eigene Klasse verschoben wurden. Klassen die nur aus einer `__init__()` und *einer* weitereren Methode bestehen sind ein „code smell“. Wenn das ganze Programm nur aus solchen Klassen besteht, ist ziemlich sicher das etwas nicht stimmt.

Die Objekte wissen teilweise auch zu viel voneinander und greifen zu tief auf andere Objekte durch. Beispielsweise `self.sprache.wg.get_wort()` in `Ausgabe`. Das ist ein Objekt das sich um die Darstellung der Ausgabe kümmern sollte, und keine Sprache kennen sollte, und noch weniger wissen sollte das eine Sprache einen Wortgenerator hat.

Sternchen-Importe sind Böse™. Damit müllt man sich den Namensraum voll, man sieht nicht mehr so einfach wo Namen definiert werden, und es besteht die Gefahr von Namenskollisionen.
maik
User
Beiträge: 6
Registriert: Dienstag 2. Mai 2017, 11:27

Re: Hangman (Wörterraten)

Beitragvon maik » Freitag 4. August 2017, 11:52

Vielen Dank erst mal für die Hinweise. Ich bin noch recht neu in der Materie und werde eure Hinweise mal umsetzen und daraus lernen.

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder