Anfänger mit Startschwierigkeiten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
slimcode
User
Beiträge: 8
Registriert: Mittwoch 11. Februar 2015, 09:02

Hallo Gemeinde

Frisch eingetragen will ich euch auch schon was fragen.

Sitz jetzt ca 1 Woche vor Online Tutorials für Python.
Nun hab ich mein erstes Spiel (Zahlen raten) programmiert, und scheitere bei einer label / variabel definition.

Es geht um das Label "Versuche" im folgenden Code.
Es soll einfach die benötigten Versuche mitzählen

Code: Alles auswählen

from Tkinter import *
import tkMessageBox
Spielfenster=Tk()
Spielfenster.title("Das Ratespiel")
Spielfenster.geometry("400x150")

secret = 22
guess = 0
a = 0
x = ""
i = 0


Aufgabe = Label(Spielfenster)
Aufgabe.pack()
Aufgabe.configure(text="Errate eine Zahl zwischen 1 und 100 - 0 unterbricht das Spiel")

guess = Entry(Spielfenster)
guess.pack()

def check():
    i = int(i + 1)
    guess2 = int(guess.get())
    Versuche.configure(text = i + ".Versuch")
    while guess2 != secret:
        if guess2 == 0:
            tkMessageBox.showinfo("Spiel beendet", "Schade, das Spiel wird beendet")
            Spielfenster.destroy()
            tkMessageBox.destroy()
        if guess2 < secret:
            x="Zahl ist zu klein"
                 
        if guess2 > secret:
            x="Zahl ist zu gross"
            zahl.configure(text = x)
        else:
            tkMessageBox.showinfo("Gratuliere","Super, Sie haben es geschafft!")
        return
        

zahl = Label(Spielfenster)
zahl.pack()

Versuche = Label(Spielfenster)
Versuche.pack()
     
Raten=Button(Spielfenster, text="Los",command=check)
Raten.pack()
Spielfenster.mainloop()

Wo hab ich den nun den denkfehler.
ich hab i schon innerhalb, unterhalb, überall definiert, nix gefällt ihm.

Ja ich weiß, tkMessageBox.destroy() is nicht schön, aber daran arbeite ich auch noch.

Vielen Dank für die Hilfe.

lg
slim
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@slimcode: Python legt Variablen immer in dem Namensraum an, in dem eine Zuweisung stattfindet, also bei Dir "i" als lokale Variable der Funktion check. Damit ist i in Zeile 22 nicht definiert, wenn sie zum ersten mal gelesen wird. Es wäre übrigens hilfreich, wenn Du hier den kompletten Traceback posten würdest, wenn ein Fehler auftritt, dann müßten wir nicht raten, was denn falsch ist.

Sobald Du GUIs programmierst, mußt Du Dich von einem linearen Programmfluß verabschieden. GUIs sind Event-basiert und jede Funktion die ein Event verarbeitet, darf nicht auf Eingaben warten. Oh, ich sehe gerade, Deine while-schleife ist gar keine, weil Du sie ja immer mit return am Ende wieder verläßt. Das ist sehr verwirrend. Das while ist also Maximal ein if, aber das ist von der Logik her auch unnötig.

Nun zum eigentlichen Problem: Wenn Du Zustände über das Ende einer Funktion speichern willst, wie hier bei einem Knopfdruck zum nächsten, brauchst Du eine Stelle, wo Du Dir das merken kannst, nennt sich in Programmierfachsprache Objekt, konkret bei Dir ein "Spiel"-Objekt das durch eine Klasse repräsentiert wird und schon sind wir bei Objektorientierter Programmierung, die man bei GUIs fast zwangsläufig braucht. Wenn Du also Dein Problem lösen willst, mußt Du noch ein paar Wochen Tutorials lernen, bis Du Objektorientierter Programmierung kennst und kannst dann zu GUIs und Deinem Spiel zurückkehren.
slimcode
User
Beiträge: 8
Registriert: Mittwoch 11. Februar 2015, 09:02

Hallo sirius

Danke, dann hab ich mich wohl übernommen.
ohne gui läufts schon ganz gut, dann werd ich noch ein bisschen dabei bleiben und die GUI später angehen.

Mein Ziel ist es, ein Quiz ähnlich Wer wird Millionär zu schreiben, wo meine die Fragen vorher selber eingeben kann.

Danke inzwischen, ich komme sicher wieder :D

lg
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

slimcode hat geschrieben:Mein Ziel ist es, ein Quiz ähnlich Wer wird Millionär zu schreiben, wo meine die Fragen vorher selber eingeben kann.
Das Ziel ist interessant und gut erreichbar. Auch ohne GUI. Schreib das Spiel so, dass es mit dem CLI funktioniert. Dann bekommst du die Grundlagen der Sprache vermittelt. Wenn die sitzen ist das Einsetzen eines GUI-Toolkits nur noch ein kleiner Schritt.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ergänzend zu sparrow: Du musst auch bei einer CLI Applikation darauf achten, Logik von Ein- und Ausgaben zu trennen! Denn dann kannst Du später Deine Funktionen und Objekte leicht aus einer GUI heraus aufrufen bzw. die Rückgaben in der GUI darstellen.

Als Faustregel: Kein ``print`` und kein ``input / raw_input`` in Funktionen oder Methoden, die *direkt* mit dem Spiel oder dem logischen Ablauf zu tun haben.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
slimcode
User
Beiträge: 8
Registriert: Mittwoch 11. Februar 2015, 09:02

Hallo

Dank des neuen Schwungs hab ich mich gleich mal hinter die Tastatur geklemmt

Hier das erste Ergebniss:

Code: Alles auswählen

#Quizspiel in der CLI

#Definieren der Variablen
#x ist die Abfrage ob man spielen oder fragen eingeben will
#z ist die Anzahl der richtigen Antworten
x = 0
z = 0
frage1 = 0
antwort1 = 0
antwort2 = 0
antwort3 = 0
antwort4 = 0
loesung1 = 0


print ("Willkommen zum Quiz")
print ("1 für Fragen erstellen, 2 für Quiz spielen")
#die vorwahl unterscheidet zwischen spielen oder fragen eingeben
def vorwahl():
    diewahl = int(input("Ihre Auswahl: "))
    def auswahl(e):
        global x
        if e == 1:
            print ("1 gedrückt")
            x = 1
            return (x)
        elif e == 2:
            print ("2 gedrückt")
            x = 2
            return (x)
        else:
            print ("Eingabe nicht korrekt, bitte 1 oder 2 drücken")
            return vorwahl()
    auswahl(diewahl)
    
vorwahl()

def frage1():
    global z
    print ("")
    fobj = open("frage1.txt", "r")
    for line in fobj: 
        print (line)
    fobj.close()
    fobj = open("loesung1.txt", "r")
    for line in fobj: 
        L1 = (line)
    fobj.close()
    Antwort1 = str(input("Ihre Antwort: "))
    if Antwort1 == L1:
        print ("Gratulation, die richtige Antwort war ", L1)
        z = z + 1
        return (z)
    else:
        print ("Schade, die richtige Antwort war ", L1)
        return ()
def frage2():
    global z
    print ("")
    fobj = open("frage2.txt", "r")
    for line in fobj: 
        print (line)
    fobj.close()
    Antwort1 = str(input("Ihre Antwort: "))
    if Antwort1 == "C" :
        print ("Gratulation, die richtige Antwort war C")
        z = z + 1
        return (z)
    else:
        print ("Schade, die richtige Antwort war C")
        return ()
def frage3():
    global z
    print ("")
    fobj = open("frage3.txt", "r")
    for line in fobj: 
        print (line)
    fobj.close()
    Antwort1 = str(input("Ihre Antwort: "))
    if Antwort1 == "D" :
        print ("Gratulation, die richtige Antwort war D")
        z = z + 1
        return (z)
    else:
        print ("Schade, die richtige Antwort war D")
        return ()

    
if x == 1:
    frage1 = input("Frage1: Geben sie die Frage ein: ")
    antwort1 = input("Geben sie die Antwort (A) ein: ")
    antwort2 = input("Geben sie die Antwort (B) ein: ")
    antwort3 = input("Geben sie die Antwort (C) ein: ")
    antwort4 = input("Geben sie die Antwort (D) ein: ")
    loesung1 = input("Lösung: Welches ist die richtige Antwort? A, B, C oder D: ")
    fobj = open("frage1.txt", "w")
    fobj.write("Frage: ")
    fobj.write(frage1)
    fobj.write('\n')
    fobj.write("(A) ")
    fobj.write(antwort1)
    fobj.write('\n')
    fobj.write("(B) ")
    fobj.write(antwort2)
    fobj.write('\n')
    fobj.write("(C) ")
    fobj.write(antwort3)
    fobj.write('\n')
    fobj.write("(D) ")
    fobj.write(antwort4)
    fobj.write('\n')
    fobj.close()
    fobj = open("loesung1.txt", "w")
    fobj.write(loesung1)
    fobj.close()
    
elif x == 2:
    frage1()
else:
    print ("die Rückgabe hat nicht funktioniert")

frage2()
frage3()

#Auswertung der richtigen Antworten
print ("Gratuliere, sie haben ", z , " von 3 Fragen richtig")



sieht sicher noch nach brutalem neuling aus
aber ich kann dateien schreiben und lesen
ein paar schwierigkeiten und verbesserungen hab ich noch, aber bin dran

bin ich von der idee her am richtigen weg?

ganz großes danke
lg
slim
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@slimcode: Du willst ja nicht für jede neue Frage eine neue Funktion schreiben. Versuch das allgemeiner, dass die gleiche Funktion für beliebige Fragen funktioniert. Vergiss dass es "global" gibt. global ist nicht die Lösung sondern macht nur Probleme. Auf Modulebene sollte nichts ausgeführt werden. Pack alles in Funktionen. Oft wird dafür eine "main"-Funktion benutzt die am Ende des Skripts mit

Code: Alles auswählen

if __name__ == '__main__':
    main()
aufgerufen wird.
BlackJack

@slimcode: Einbuchstabige Namen sind in der Regel keine gute Idee. Der Name sollte aussagen wofür der Wert steht und man sollte das nicht in einem extra Kommentar schreiben müssen.

Durchnummerieren von Namen ist ebenfalls nicht gut. In der Regel möchte man in dem Fall eine Datenstruktur verwenden. Oft ist das eine Liste.

``global`` solltest Du ganz schnell wieder vergessen. Saubere Programme kommen ohne aus. Funktionen sollten ausser auf Konstanten nur auf Werte zugreifen die sie als Argumente übergeben bekommen haben und wenn sie ein Ergebnis haben dann sollte das nicht an einen Namen ausserhalb der Funktion gebunden werden, sondern als Rückgabewert an den Aufrufer zurückgegeben werden. Insbesondere wenn man den Wert schon als Rückgabewert hat, macht es keinen Sinn den auch noch zusätzlich an einen modulglobalen Namen zu binden.

Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Die Funktion `auswahl()` in `vorwahl()` scheint nur als Schleifenersatz zu existieren, was aber ein Programmierfehler ist. Rekursive Aufrufe funkionieren nicht unbegrenzt, darum sollte man Rekursion auch nur verwenden wenn man tatsächlich eine Lösung braucht die nicht mit einer einfachen Schleife umgesetzt werden kann.

Die durchnummerierten `frage*()`-Funktionen haben alle fast den gleichen Inhalt: Das verletzt das „Don't Repeat Yourself”-Prinzip (DRY-Prinzip). Da würde man *eine* Funktion schreiben welche ein oder mehrere Argumente bekommt, zum Beispiel die Nummer der Frage. Stell Dir mal ein Quiz mit 100 Fragen vor, da will man doch nicht 100 mal eine fast identische Funktion im Quelltext stehen haben. Was die korrekte Antwort ist gehört vielleicht auch nicht in den Code wenn man die Frage selbst schon in einer Datei ausgelagert hat. Bei Frage 1 machst Du ja schon etwas in der Richtung. Allerdings würde ich das nicht auf zwei Dateien aufteilen.

``return`` ist keine Funktion also braucht man um den Ausdruck auch keine Klammern setzen. Insbesondere ist ``return`` etwas anderes als ``return ()``.
slimcode
User
Beiträge: 8
Registriert: Mittwoch 11. Februar 2015, 09:02

wow danke für das super feedback

hab mich jetzt tiefer eingelesen, und das kommt nun dabei raus

soooo schlecht ist das nicht mehr finde ich :D

Code: Alles auswählen

#Quizspiel in der CLI

print ("Willkommen zum Quiz")

def frage_eingeben(frage, antworta, antwortb, antwortc, antwortd, loesung):
    dateiname = str(frage)
    fobj = open(dateiname,".txt", "w")
    fobj.write("Frage: ")
    fobj.write(frage1)
    fobj.write('\n')
    fobj.write("(A) ")
    fobj.write(antwort1)
    fobj.write('\n')
    fobj.write("(B) ")
    fobj.write(antwort2)
    fobj.write('\n')
    fobj.write("(C) ")
    fobj.write(antwort3)
    fobj.write('\n')
    fobj.write("(D) ")
    fobj.write(antwort4)
    fobj.write('\n')
    fobj.close()
    fobj = open("loesung1.txt", "w")
    fobj.write(loesung1)
    fobj.close()
    return {
        "Frage" : frage,
        "(A)" : antworta,
        "(B)" : antwortb,
        "(C)" : antwortc,
        "(D)" : antwortd,
        "Lösung" : loesung
        }

def frage_ausgeben(frage):
    print (frage["Frage"])
    print ("(A) ", frage["(A)"])
    print ("(B) ", frage["(B)"])
    print ("(C) ", frage["(C)"])
    print ("(D) ", frage["(D)"])

def antwort_pruefen(frage):
    antwort_user = input("Ihre Antwort: ")
    if antwort_user == frage["Lösung"]:
        print ("Super, die richtige Antwort war ", frage["Lösung"])
    else:
        print ("Schade, die richtige Antwort war ", frage["Lösung"])
    
  
frage1 = frage_eingeben(input("Frage1: Geben sie die Frage ein: "), input("Geben sie die Antwort (A) ein: "), input("Geben sie die Antwort (B) ein: "), input("Geben sie die Antwort (C) ein: "), input("Geben sie die Antwort (D) ein: "), input("Lösung: Welches ist die richtige Antwort? A, B, C oder D: "))

frage_ausgeben(frage1)
antwort_pruefen(frage1)

es fehlen noch einige teile, wie die abfrage usw, aber aktuell hab ich eine andere frage
kann ich eine datei erstellen aus einer variable?
diese Zeilen sind noch ein problem

Code: Alles auswählen

    dateiname = str(frage)
    fobj = open(dateiname,".txt", "w")
Der Traceback

File "C:\Python34\Scripts\quiz2.py", line 7, in frage_eingeben
fobj = open(dateiname,".txt", "w")
TypeError: an integer is required (got type str)

also ich will natürlich für jede frage eine eigene txt datei erstellen, denn sonst müsste man ja vor jedem spiel zuerst die fragen eingeben

ja und allgemein, bin ich auf dem richtigen weg?

danke
lg
slim
BlackJack

@slimcode: Durchnummerierte Namen bleiben solche auch wenn man die Nummer durch einen Buchstaben ersetzt. Statt feste Namen für die erste, zweite, dritte, … Antwort zu haben, würde man wie gesagt eine Liste verwenden. Da kann man dann auch mehr oder weniger als vier Antworten für eine Frage haben. Und die Buchstaben sollte man auch nicht mehrfach in literalen Zeichenketten stehen haben sondern per Code erzeugen. Das `string`-Modul hat dafür ein paar nützliche Konstanten.

Dein Fehler ist das Du `open()` mit *drei* Argumenten aufrufst. Und wenn Du in die Dokumentation zu der Funktion schaust, dann steht da dass das erste Argument der Dateiname ist, da übergibst Du `dateiname`, das zweite Argument der Modus der Datei, da übergibst Du ``".txt"`` (was kein gültiger Dateimodus ist), und das dritte Argument eine *Zahl* sein muss die über den Puffermodus der Datei entscheidet, da übergibst Du ``"w"``, was keine Zahl ist und was hier zu der Ausnahme führt.

Du musst den Dateinamen zusammensetzen und nicht als zwei verschiedene Argumente übergeben die so von der Funktion nicht erwartet und verstanden werden. Schau Dir dazu mal die `format()`-Methode auf Zeichenketten an. Dann kannst Du auch diese ganzen vielen `write()`-Aufrufe deutlich reduzieren in dem Du nicht jede Teilzeichenkette einzeln in die Datei schreibst.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ohne Dich demotivieren zu wollen: Dein ganzer Ansatz des Speicherns ist imho wenig gelungen! Vergiss doch erst einmal, dass man Fragen speichern will. Konzentriere Dich zunächst darauf, wie Du einen Datentypen modellierst, der alle Informationen für eine Frage beinhaltet. Du hast ja erste Ansätze in ``frage_eingeben`` - nämlich ein Dictionary. Ich würde da aber die Darstellung vom Inhalt trennen. Bisher heißen Deine Schlüssel für die Optionen "(A)" - ich würde daraus "A" machen. Die Klammern kann man bei der *Ausgabe* hinzufügen, sie sind aber kein integraler Bestandteil der Daten. Grundsätzlich braucht man aber auch keinen nummerierten Schlüssel, sondern könnte da eine Liste nehmen.

Ich selber habe mal ein solches Spiel implementiert und per Konvention die Lösung immer an die erste Stelle gestellt. Damit spart man sich ein separates Feld für die Lösung. Vor dem Spielstart kann man dann die Fragen mischen und die Info über die richtige Antwort in der Datenstruktur *ergänzen* - in die Persistenz muss diese also nicht unbedingt rein.

Wenn Du eine Struktur hast, dann schau Dir mal verschiedene *Standard* Serialisierungsmechanismen an, insbesondere JSON. Dafür gibt es in der Standard-Lib ein Modul namens ``json``. Damit wird das Speichern und Laden trivial!

Ich an Deiner Stelle würde mich erst einmal auf den Spielablauf konzentrieren, denn auf die Eingabe von Fragen über CLI. So etwas kannst Du zu Beginn ganz einfach in einem Texteditor erledigen und später in Deiner GUI einen Mechanismus integrieren.

Also:
1.) Datenstrukturen modellieren
2.) Spielablauf

1.) wird sich ggf. durch 2.) noch ändern, wenn man neue Erkenntnisse hat, wie man etwas umsetzt oder was man ggf. an Infos vergessen hat.

So, noch einmal: Nicht demotivieren lassen, sondern das als Tipps aufnehmen :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten