Hangman - Anfänger fragt an

Code-Stücke können hier veröffentlicht werden.
Graf Wasserrutsche
User
Beiträge: 37
Registriert: Donnerstag 17. Juli 2008, 06:59
Wohnort: Köln
Kontaktdaten:

Hangman - Anfänger fragt an

Beitragvon Graf Wasserrutsche » Donnerstag 17. Juli 2008, 14:12

Guten Tag zusammen!

Ich bin gänzlich neu in der Pythonwelt und habe mich, nach ein wenig A Byte Of Python und googlen mal daran getraut Hangman zu schreiben. Einige Teile habe ich von der Funktionsweise her aus Codeschnippseln im Internet, doch wüsste ich nun gerne wie man alles optimieren kann, weil ich einige Sachen mit Sicherheit zu umständlich gemacht habe bzw. diese auch anders gehen würen.

Wäre es möglich das ihr euch den Code mal anschaut und mir sagt, was anders, oder auch besser gehen würde? Was für eine Übung könntet ihr mir empfehlen, damit ich von den Basics etwas weg komme?

Vielen Dank schonmal!

Code: Alles auswählen

import random

datei = open('wort.txt', 'r')
var = datei.read()
wort_liste = var.split(" ")
wort = random.choice(wort_liste)

versuche = 5
erraten = ''

print 'Hangman Ver. 0.1'
print
print '1) Spielen'
print '2) Beenden'
print '3) Administration'
print
i = raw_input('Waehlen Sie: ')

if i == '1':
    versuche = int(versuche)
    while versuche > 0:
        daneben = 0
        for buchstaben in wort:
            if buchstaben in erraten:
                print buchstaben,
            else:
                print '_',
                daneben += 1
           
        if daneben == 0:
            print
            print 'Sie haben gewonnen!'
            nochmal = raw_input('Moechten Sie noch einmal spielen? (j/n)')
            if nochmal == 'j':
                versuche = 5
                erraten = ''
                datei = open('wort.txt', 'r')
                var = datei.read()
                wort_liste = var.split(" ")
                wort = random.choice(wort_liste)
            else:
                break
   
        print   
        raten = raw_input('Bitte geben Sie einen Buchstaben ein:')
   
        if len(raten) > 1:
            raten = raw_input('Bitte geben Sie einen EINZELNEN Buchstaben ein:')
        else:
            erraten += raten

        if raten not in wort:
            versuche -= 1
            print 'Dieser Buchstabe ist nicht vorhanden'
            if versuche == 0:
                print
                print 'Sie haben verloren!'
                print
                nochmal = raw_input('Moechten Sie noch einmal spielen? (j/n)')
                if nochmal == 'j':
                    versuche = 5
                    erraten = ''
                    wort = random.choice(wort_liste)
                    datei = open('wort.txt', 'r')
                    var = datei.read()
                    wort_liste = var.split(" ")
                    wort = random.choice(wort_liste)
elif i == '3':
    print
    print 'Herzlich Willkommen in der Administration!'
    print
    print 'Welche Werte moechten Sie veraendern?'
    print
    print '1) Wort der Wortliste hinzufuegen'
    print '2) Wort aus der Wortliste loeschen'
    print
    t = raw_input('Waehlen Sie: ')
             
    if t == '1':
        print
        var = raw_input('Welches Wort moechten Sie hinzufuegen? ')
        datei = open('wort.txt', 'a')
        datei.writelines(' '+var)
    elif t == '2':
        print
        datei = open('wort.txt', 'r')
        gesamt = datei.read()
        print gesamt
        print
        var = raw_input('Welches Wort moechten Sie loeschen? ')
        if var in gesamt:
            gesamt = gesamt.replace(var+' ', '')
            print gesamt
        datei = open('wort.txt', 'w')
        datei.write(gesamt)
        print 'Wort wurde geloescht'
BlackJack

Beitragvon BlackJack » Donnerstag 17. Juli 2008, 14:38

Ein zu grosser Haufen Quelltext und alles auf Modulebene. Das sollte in sinnvoll aufgeteilten Funktionen verschwinden.

Man sollte keinen Quelltext unnötig wiederholen. So etwas kann schnell auseinanderlaufen, wenn man etwas ändert, weil man immer daran denken muss die Änderungen überall im Programm zu machen.

Ich sehe kein einziges `datei.close()` -- Dateien, die man öffnet, sollte man auch wieder schliessen.
Graf Wasserrutsche
User
Beiträge: 37
Registriert: Donnerstag 17. Juli 2008, 06:59
Wohnort: Köln
Kontaktdaten:

Beitragvon Graf Wasserrutsche » Freitag 18. Juli 2008, 06:36

BlackJack hat geschrieben:Ein zu grosser Haufen Quelltext und alles auf Modulebene. Das sollte in sinnvoll aufgeteilten Funktionen verschwinden.

Man sollte keinen Quelltext unnötig wiederholen. So etwas kann schnell auseinanderlaufen, wenn man etwas ändert, weil man immer daran denken muss die Änderungen überall im Programm zu machen.

Ich sehe kein einziges `datei.close()` -- Dateien, die man öffnet, sollte man auch wieder schliessen.


Welche Zeilen könnte ich denn sinnvoll in Funktionen unterbringen? Das Auslesen der Textdatei z.B.?

An das datei.close() werde ich mich in Zukunft halten.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Freitag 18. Juli 2008, 07:39

Graf Wasserrutsche hat geschrieben:
BlackJack hat geschrieben:Welche Zeilen könnte ich denn sinnvoll in Funktionen unterbringen? Das Auslesen der Textdatei z.B.?

Naja, Du kannst da sehr generisch vorgehen! z.B. das "Raten" als solches. Wieso nicht in eine funtkion "raten()"? Ähnlich kannst Du bei der restlichen Funktionalität vorgehen.
Graf Wasserrutsche
User
Beiträge: 37
Registriert: Donnerstag 17. Juli 2008, 06:59
Wohnort: Köln
Kontaktdaten:

Beitragvon Graf Wasserrutsche » Samstag 19. Juli 2008, 16:08

So, ich habe jetzt einige kleinere Teile des Codes in Funktionen untergebracht, nur rätsel ich immernoch, wie ich die Funktion raten() an sich schreiben soll, damit das Konzept, wie ich es bis jetzt verwende erhalten bleibt.

Oder wäre es eher angebracht, die Funktion bzw. die Funktionalität komplett neu zu schreiben?

Hier der neue Code:

Code: Alles auswählen

import random

versuche = 5
erraten = ''

print 'Hangman Ver. 0.1\n'
print '1) Spielen'
print '2) Beenden'
print '3) Administration\n'

i = raw_input('Waehlen Sie: ')

def zufall():
    datei = open('wort.txt', 'r')
    var = datei.read()
    wort_liste = var.split(" ")
    wort = random.choice(wort_liste)
    datei.close()
    return wort

def wort_add(var):
    datei = open('wort.txt', 'a')
    datei.writelines(' '+var)
    datei.close()
    print 'Wort wurde hinzugefuegt!'
   
def wort_del(var):
    datei = open('wort.txt', 'r')
    gesamt = datei.read()
    if var in gesamt:
        gesamt = gesamt.replace(var+' ', '')
    else:
        print 'Wort ist nicht vorhanden!'
    datei = open('wort.txt', 'w')
    datei.write(gesamt)
    datei.close()
    print 'Wort wurde geloescht'   

if i == '1':
    wort = zufall()
    while int(versuche) > 0:
        daneben = 0
        for buchstaben in wort:
            if buchstaben in erraten:
                print buchstaben,
            else:
                print '_',
                daneben += 1
           
        if daneben == 0:
            print '\nSie haben gewonnen!'
            nochmal = raw_input('Moechten Sie noch einmal spielen? (j/n)')
            if nochmal == 'j':               
                wort = zufall()
            else:
                break
   
        print   
        raten = raw_input('Bitte geben Sie einen Buchstaben ein:')
   
        if len(raten) > 1:
            raten = raw_input('Bitte geben Sie einen EINZELNEN Buchstaben ein:')
        else:
            erraten += raten

        if raten not in wort:
            versuche -= 1
            print 'Dieser Buchstabe ist nicht vorhanden'
            if versuche == 0:
                print '\nSie haben verloren!\n'
                nochmal = raw_input('Moechten Sie noch einmal spielen? (j/n)')
                if nochmal == 'j':
                    versuche, erraten = eigenschaften()
                    wort = zufall()
elif i == '3':
    print '\nHerzlich Willkommen in der Administration!\n'
    print 'Welche Werte moechten Sie veraendern?\n'
    print '1) Wort der Wortliste hinzufuegen'
    print '2) Wort aus der Wortliste loeschen\n'
    t = raw_input('Waehlen Sie: ')
             
    if t == '1':
        wort_add(raw_input('\nWelches Wort moechten Sie hinzufuegen?'))
    elif t == '2':
        wort_del(raw_input('\nWelches Wort moechten Sie loeschen?\n'))
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Samstag 19. Juli 2008, 16:28

Also ganz zu Beginn ist Deine Funktion zufall() wenig performant. Du lädst Die Datei ja zig mal! Da Du aber eh alle Wörter im Speicher hältst, lies die Daten einmal ein und merke Dir die Worte in einer List!

Dann reicht es später folgendes aus (ungestet):

Code: Alles auswählen

from random import shuffle

# list later from file
words = ["some", "different", "words", ...]

def get_rand_word(words):
    shuffle(words)
    return words.pop()


Nun zum Raten: Wie genau ist der Ablauf? Skizziere ihn dir mal einfach so. (zufälliges wort ermitteln, Buchstaben eingeben usw)

Ich denke in der Raten-Funtkion sollte es wohl eine Schleife geben, in der die Verusche abgehandelt werden. Die Eingabe eines Buchstabens würde ich in eine separate Funktion auslagern, um besser auf Fehleingaben reagieren zu können. Naja, dann fehlt noch die Auswertung. Das wäre wohl sicher Code, der in der raten()-Funktion stehen sollte.

Ich hoffe das bringt Dich weiter.
Graf Wasserrutsche
User
Beiträge: 37
Registriert: Donnerstag 17. Juli 2008, 06:59
Wohnort: Köln
Kontaktdaten:

Beitragvon Graf Wasserrutsche » Dienstag 26. August 2008, 13:45

So, lieber ultra spät als nie :)

Also, habe das jetzt mit Hilfe der Tipps so umgestrickt bzw. neu geschrieben. Einige Funktionen wie das Menü, oder Reaktionen auf die Eingabe sind noch nicht drin, wollte nur fragen ob ich das Prinzip jetzt besser verstanden habe bzw. das Ergebnis ein besseres ist?

Code: Alles auswählen

from random import shuffle

datei = open('wort.txt', 'r')
var = datei.read()
words = var.split(" ")
datei.close()

def get_words(words):
    shuffle(words)
    return words.pop()

def input_char():
    char = raw_input("\nBitte geben Sie einen Buchstaben ein:")
    return char

def output_word(wort, wortliste, daneben):
    for stelle in wort:
        if stelle in wortliste:
            print stelle,
        else:
            print "_",
            daneben += 1
    return daneben

def guess_char(wortliste, versuche, fehler):
    wort = get_words(words)
   
    while fehler < versuche:
        char = input_char()
        daneben = 0 
       
        if char in wort:
            print "Dieser Buchstabe ist richtig!"
            if char not in wortliste:
                wortliste += char
                if output_word(wort, wortliste, daneben) == 0:
                    print "\nSie haben das Wort erraten!"
                    break
            else:
                print "Dieser Buchstabe ist schon vorhanden!"     
        else:
            print "Dieser Buchstabe ist falsch!"
            fehler += 1       
             
guess_char('', 5, 0)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Beitragvon numerix » Dienstag 26. August 2008, 14:48

Auf jeden Fall schonmal besser als der erste Code ...

Die Funktionen get_words() und input_char() würde ich mir sparen.
Da es genügt, einmal shuffle() aufzurufen (und nicht bei jedem Holen eines Wortes erneut), könnte man das direkt nach dem Laden aus der Datei machen. Dann wäre get_words() (besser wäre auch: get_word()) funktional identisch mit words.pop() und das kannst du dann auch gleich so verwenden.

Entsprechendes gilt für input_char(). Es ist funktional identisch mit raw_input(), also kannst du es auch gleich dabei belassen.
Graf Wasserrutsche
User
Beiträge: 37
Registriert: Donnerstag 17. Juli 2008, 06:59
Wohnort: Köln
Kontaktdaten:

Beitragvon Graf Wasserrutsche » Mittwoch 27. August 2008, 07:49

So, die Funktion get_words() habe ich jetzt ganz normal in den Code eingefügt, das konnte ich nachvollziehen.

Der Übersichtlichkeit halber und auf Grund des Tipps von Hyperion habe ich die input_chars() dennoch als Funktion außerhalb gelassen, um auf Fehleingaben zu reagieren.

Die Länge der Variable ist ja kein Problem, nur weiß ich nicht wie man z.B. die Groß- und Kleinschreibung unterscheiden kann. Gibt es da eine extra Funktion für? Und gibt es eine Möglichkeit meine bisherige Fallunterscheidung noch einfacher zu schreiben?

Ich entschuldige mich jetzt schonmal für die ganzen blöden Fragen :)

Code: Alles auswählen

from random import shuffle

datei = open('wort.txt', 'r')
var = datei.read()
words = var.split(" ")
datei.close()

def input_char(char):   
    if len(char) > 1:
        char = raw_input('Bitte geben Sie einen EINZELNEN Buchstaben ein:')
    elif char == '':
        char = raw_input('Bitte geben Sie einen Buchstaben ein:')     
    return char

def output_word(wort, wortliste, daneben):
    for stelle in wort:
        if stelle in wortliste:
            print stelle,
        else:
            print "_",
            daneben += 1
    return daneben

def guess_char(wortliste, versuche, fehler, words):   
    shuffle(words)
    wort = words.pop()
    while fehler < versuche:
        char = input_char(raw_input('\nBitte geben Sie einen Buchstaben ein:'))
        daneben = 0 
       
        if char in wort:
            print "Dieser Buchstabe ist richtig!"
            if char not in wortliste:
                wortliste += char
                if output_word(wort, wortliste, daneben) == 0:
                    print "\nSie haben das Wort erraten!"
                    break
            else:
                print "Dieser Buchstabe ist schon vorhanden!"     
        else:
            print "Dieser Buchstabe ist falsch!"
            fehler += 1
            if fehler == 5:
                print "\nSie haben verloren!"     
             
guess_char('', 5, 0, words)
BlackJack

Beitragvon BlackJack » Mittwoch 27. August 2008, 08:32

`wortliste` ist ja wohl eher eine Buchstabenliste. Aber man sollte in vielen Fällen den Datentyp aus dem Namen heraus halten. Effizienter wäre es nämlich ein `set` zu verwenden und dann hätte man ein `set` das an einen Namen gebunden ist, der "liste" enthält. Das ist verwirrend.

Dann solltest Du Dir mal Deine Argunentlisten anschauen. Du übergibst da teilweise Werte von aussen, die eigentlich als Argumente keinen Sinn machen. Gibt es Gründe warum jemand etwas anderes als 0 für `daneben` in `output_word()` oder '' und 0 für `wortliste` und `fehler` in `guess_char()` übergeben sollte? Warum kann man das tun?

`guess_char()` müsste von der Semantik her `guess_chars()` heissen, weil man ja nicht nur einen, sondern alle Buchstaben damit raten kann.

`input_char()` sollte wirklich die komplette Eingabe übernehmen und nicht schon eine Eingabe bekommen, ausserdem wäre es sinnvoller, wenn die Funktion *wirklich* sicherstellen würde, dass nur ein einzelner Buchstabe eingegeben werden kann. So kann man die Nachfrage ja immer noch falsch beantworten.

Es gibt noch eine hart kodierte 5 die durch `versuche` ersetzt werden sollte.

Mit der Gross- und Kleinschreibung würde ich so verfahren, dass sowohl die Worte als auch die Eingaben konsequent in Gross- oder Kleinbuchstaben umgewandelt werden. Da gibt's Methoden auf Zeichenketten für.
Graf Wasserrutsche
User
Beiträge: 37
Registriert: Donnerstag 17. Juli 2008, 06:59
Wohnort: Köln
Kontaktdaten:

Beitragvon Graf Wasserrutsche » Dienstag 2. September 2008, 14:46

So, Vorschläge angenommen und umgesetzt :)

Bezüglich der String-Methoden habe ich noch eine Frage.

Entweder ich habe es übersehen, oder bin zu dumm, aber ich habe in der Referenz keine Methode gefunden die einfach alle Buchstaben in Kleinbuchstaben umwandelt.
Nur eine die alles in Großbuchstaben umwandelt und eine, die immer in die umgekehrte Größe umwandelt.

Stimmt das, oder gibt es noch eine extra für Kleinschreibung?

Habt ihr noch Ideen, welche Funktionen in das Spiel eingebaut werden könnten, oder wie einige Passagen verbessert bzw. vereinfacht werden könnten?

Code: Alles auswählen

from random import shuffle

datei = open('wort.txt', 'r')
var = datei.read()
words = var.split(" ")
datei.close()

def hangman_show(a, b):
    hangman = ["-----", "|/  |", "|   o  ", "|  \O/  ", "|  / \   "]
    for i in range(a, b):
        print hangman[i]

def input_char(): 
    char = raw_input('\nBitte geben Sie einen Buchstaben ein:')   
    while len(char) > 1 or char == '':
        char = raw_input('Bitte geben Sie einen EINZELNEN Buchstaben ein:')
    char = char.upper()
    return char.swapcase()

def output_word(wort, buchstaben):
    daneben = 0
    for stelle in wort:
        if stelle in buchstaben:
            print stelle,
        else:
            print "_",
            daneben += 1
    return daneben

def guess_chars(versuche, words):   
    shuffle(words)
    wort = words.pop()
    fehler = 0
    buchstaben = ''
    while fehler < versuche:
        char = input_char()
       
        if char in wort:
            print 'Dieser Buchstabe ist richtig!'
            if char not in buchstaben:
                buchstaben += char
                if output_word(wort, buchstaben) == 0:
                    print '\nSie haben das Wort erraten!'
                    break
            else:
                print 'Dieser Buchstabe ist schon vorhanden!'     
        else:
            print 'Dieser Buchstabe ist falsch!'
            hangman_show(0,fehler+1)
            fehler += 1
            if fehler == versuche:
                print '\nSie haben verloren!'     

nochmal = ''   
while nochmal == 'j':
    guess_chars(5, words)
    nochmal = raw_input('Wollen Sie noch einmal spielen? (j/n)')
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Beitragvon mkesper » Dienstag 2. September 2008, 14:53

Graf Wasserrutsche hat geschrieben:Bezüglich der String-Methoden habe ich noch eine Frage.

Entweder ich habe es übersehen, oder bin zu dumm, aber ich habe in der Referenz keine Methode gefunden die einfach alle Buchstaben in Kleinbuchstaben umwandelt.

Die Doku ist an der Stelle etwas irreführend. Alle Methoden, die du auf Strings anwenden kannst, finden sich unter 3.6.1 "String-methods".
EDIT: Die gesuchte Methode heißt wenig überraschend lower().
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Beitragvon Rebecca » Dienstag 2. September 2008, 14:55

Na, wir mag wohl das gegenteil von upper heissen?

Code: Alles auswählen

>>> "Hallo Welt".lower()
'hallo welt'


Falls du nochmal was suchtst: dir("sdf"), dir(str), help(str)
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Graf Wasserrutsche
User
Beiträge: 37
Registriert: Donnerstag 17. Juli 2008, 06:59
Wohnort: Köln
Kontaktdaten:

Beitragvon Graf Wasserrutsche » Dienstag 2. September 2008, 14:58

Rebecca hat geschrieben:Na, wir mag wohl das gegenteil von upper heissen?

Code: Alles auswählen

>>> "Hallo Welt".lower()
'hallo welt'


Falls du nochmal was suchtst: dir("sdf"), dir(str), help(str)


Oh, okay -_-

Danke.
Benutzeravatar
roschi
User
Beiträge: 225
Registriert: Samstag 29. März 2008, 18:58
Wohnort: Thueringen, Deutschland
Kontaktdaten:

Beitragvon roschi » Sonntag 12. Oktober 2008, 19:25

hallo!

hier noch ein kleines beispiel, das (hoffentlich) so in ordnung ist.
es funktioniert auf jeden fall.

hangmann.py:
http://paste.pocoo.org/show/87793/
data.py (wenn man spielen will, bitte vorerst nicht reinschauen!):
http://paste.pocoo.org/show/87797/

ich hoffe, das ist hilfreich.

mfg
roschi

PS: auch wenn dieser thread schon eine weile nicht mehr beposted wurde, vielleicht hilft es ja doch jemandem.
Fuer Alle, die in Python einsteigen wollen, kann ich das Buch A Byte of Python nur waermstens empfehlen!

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder