Seite 1 von 1
Hangman
Verfasst: Sonntag 19. Juni 2011, 20:46
von derrick
Abend Leute,
ich habe ein Hangmanspiel geschrieben als Lösung für diese Aufgabe(Bitte anschauen damit ihr wisst was für eine Formatierung verlangt wird)
http://openbookproject.net/pybiblio/pra ... angman.php
Allerdings bin ich bei der Verwaltung der Zeichenfolge des zu suchenden Worts auf meine
Grenzen gestoßen, was den Code unnötig unelegant macht. Ich habe mich extra bemüht den Code ausführlich zu dokumentieren und würde mich daher freuen wenn sich jemand die analyse_guess() und end_round() Methoden
ansehen würde und mir hilft eine bessere Lösung zu finden.
Hier der Code:
http://www.python-forum.de/pastebin.php?mode=view&s=215
Solltet ihr das Programm testen wollen muss eine HangmanWords.txt im richtigen Verzeichniss verfügbar sein.
Eingabenprüfung ist noch nicht implementiert

also bitte an die Inputaufforderung halten

!
Vielen Dank für eure Mühe
derrick
Re: Hangman
Verfasst: Sonntag 19. Juni 2011, 20:56
von Hyperion
derrick hat geschrieben:
Solltet ihr das Programm testen wollen muss eine HangmanWords.txt im richtigen Verzeichniss verfügbar sein.
Und eine leere Datei diesen Namens reicht aus?
Will sagen: Wieso stellst Du die nicht zum Testen bereit. Alternativ baust Du ein default-Wort ein oder eine Liste von einigen Wörtern. Das wäre für potenzielle Tester angenehmer und macht einen besseren Eindruck

Re: Hangman
Verfasst: Sonntag 19. Juni 2011, 21:06
von Hyperion
Ohne es getestet zu haben und nur auf die Schneller:
- Ich glaube Du brauchst da keine Klassen, oder wenn eine bessere Struktur. Player ist zumindest so überflüssig
- Binde nicht verschiedene Datentypen nacheinander an denselben Namen:
Code: Alles auswählen
length_word = random.choice(length_list)
length_word = [char for char in length_word]
Zumal Du Dir die erste Zeile auch sparen könntest
Code: Alles auswählen
length_word = [char for char in random.choice(length_list)]
(über den Namen reden wir mal besser nicht

)
- Boolesche Zustände würde ich mit `True` und `False` abbilden, nicht mit "0" und "1" (self.go_on)
- Shebang und Kodierungsangabe fehlen
Du könntest mal die SuFu benutzen; iirc hatten wir schon zig Hangmans hier mit teilweise guten Ansätzen, die eigentliche Logik zu implementieren.
Re: Hangman
Verfasst: Sonntag 19. Juni 2011, 21:23
von Hyperion
So, hab mir mal was überlegt:
Code: Alles auswählen
In [1]: secret = "Programmiersprache"
In [3]: from collections import defaultdict
In [4]: indexes = defaultdict(list)
In [6]: for index, char in enumerate(secret.lower()):
...: indexes[char].append(index)
...:
...:
In [7]: indexes
Out[7]: defaultdict(<type 'list'>, {'a': [5, 14], 'c': [15], 'e': [9, 17], 'g':
[3], 'i': [8], 'h': [16], 'm': [6, 7], 'o': [2], 'p': [0, 12], 's': [11], 'r': [
1, 4, 10, 13]})
In [8]: guessed_chars = ["_" for _ in xrange(len(secret))]
In [9]: guess = "e"
In [10]: indexes[guess]
Out[10]: [9, 17]
In [11]: for index in indexes[guess]:
....: guessed_chars[index] = guess
....:
....:
In [12]: print " ".join(guessed_chars)
-------> print(" ".join(guessed_chars))
_ _ _ _ _ _ _ _ _ e _ _ _ _ _ _ _ e
Die Idee dahinter ist es, sich die vorkommenden Buchstaben zusammen mit ihren Indizes in einem dict zu merken. Damit kommt man schnell darauf, ob ein Buchstabe im Wort enthalten ist und zudem an die Positionen.
Zudem legt man sich eine Liste an, die der Länge des ursprünglichen Wortes umfasst und aus Platzhaltern besteht.
Bei einer Tippabgabe kann man nun die Positionen dieser Liste leicht gegen den richtigen Tipp ersetzen.
Damit kann man zudem leicht die minimal benötigten Rateversuche bestimmen (Anzahl der Schlüssel in `indexes`).
Re: Hangman
Verfasst: Montag 20. Juni 2011, 11:01
von derrick
Danke für den Ansatz Hyperion ist echt um einiges schöner und das gesuchte Wort wird dabei nicht zerstört.
Ich habe aber noch zusätzlich eine string-Version des gesuchten Worts speichern müssen um die korrekte Platzhalteranzahl mit len() zu ermitteln und um zu testen ob der User bereits alle Buchstaben eraten hat.Lässt sich das irgendwie umgehen?
Eine andere Frage die ich noch hätte: Wir überprüft man möglichst einfach ob ein input ein Buchstabe ist,mache das aktuell über eine liste aller Buchstaben des (englischen) Alphabets.
Hier der neue Code:
http://www.python-forum.de/pastebin.php?mode=view&s=216
Hier die HangmanWords.txt :
http://www.python-forum.de/pastebin.php?mode=view&s=217
Grüße
derrick
Re: Hangman
Verfasst: Montag 20. Juni 2011, 11:10
von Hyperion
derrick hat geschrieben:
Ich habe aber noch zusätzlich eine string-Version des gesuchten Worts speichern müssen um die korrekte Platzhalteranzahl mit len() zu ermitteln und um zu testen ob der User bereits alle Buchstaben eraten hat.Lässt sich das irgendwie umgehen?
Da gibt es einige Möglichkeiten:
Du könntest einen Zähler einbauen, der zu Beginn der Länge des Wortes entspricht. Anschließend kannst Du bei jeder Ersetzung reduzieren. Ist er 0 ist das Wort komplett erraten.
Oder aber Du berechnest eben die noch verbliebenen Stellen im Wort immer neu. Da die Wörter ja recht kurz sind, würde ich das trotz schlechterer Effizienz evtl. vorziehen, da man sich den Zähler spart:
Als nächstes fiele mir noch ein, die Einträge aus dem `indexes` Dict nach dem Erraten zu löschen. Ist das Dict leer, so ist das Wort erraten.
Um dann ggf. eine Meldung auszugeben, dass ein abgegebener Buchstabe schon erraten wurde, könnte man diese in einer separaten Liste führen. (`guessed_chars` wäre da ein passender Name

-> also die Liste mit Platzhaltern sinnvoller benennen

)
Ich denke letzteres ist am elegantesten.
Eine andere Frage die ich noch hätte: Wir überprüft man möglichst einfach ob ein input ein Buchstabe ist,mache das aktuell über eine liste aller Buchstaben des (englischen) Alphabets.
Guck mal ins `string`-Modul. Da gibts vorgefertigte "Konstanten" mit denen Du per "x iny"-Idiom das leicht testen kannst.
Re: Hangman
Verfasst: Montag 20. Juni 2011, 11:11
von mutetella
derrick hat geschrieben:Wir überprüft man möglichst einfach ob ein input ein Buchstabe ist,...
Code: Alles auswählen
In [27]: 'a'.isalpha()
Out[27]: True
In [28]: '1'.isdigit()
Out[28]: True
In [29]: 'a1'.isalnum()
Out[29]: True
In [30]: ' '.isspace()
Out[30]: True
In [31]: 'A'.isupper()
Out[31]: True
In [32]: 'a'.islower()
Out[32]: True
mutetella
Re: Hangman
Verfasst: Montag 20. Juni 2011, 11:33
von derrick
Danke euch beiden für die Vorschläge hab sie nun eingebaut.
@Hyperion: Ich habe mich für
entschieden wobei
die inneren runden Klammern eckig sein müssen

Viele Grüße
derrick
Re: Hangman
Verfasst: Montag 20. Juni 2011, 11:35
von Hyperion
derrick hat geschrieben:
die inneren runden Klammern eckig sein müssen
Ach Mist... hast Recht. Logisch eigentlich, dass ein Generator-Objekt nicht mit len() funzen kann; es kann ja unendlich Werte zurückliefern.
Wieso hast Du nicht den dritten Vorschlag gewählt? Habe ich da was in der Logik übersehen?
Re: Hangman
Verfasst: Montag 20. Juni 2011, 11:51
von mutetella
Bleibt nur die Frage, wie man Umlaute als Buchstaben erkennen kann:
Code: Alles auswählen
In [48]: 'ü'.isalpha()
Out[48]: False
In [49]: u'ü'.isalpha()
Out[49]: False
In [50]: from __future__ import unicode_literals
In [51]: 'ü'.isalpha()
Out[51]: False
Ab python3 funktionierts:

Re: Hangman
Verfasst: Montag 20. Juni 2011, 11:58
von Hyperion
mutetella hat geschrieben:Bleibt nur die Frage, wie man Umlaute als Buchstaben erkennen kann:
sma hatte da mal einen Vorschlag via RegExp gebracht:
Link.
Ansonsten eben: Python3 benutzen

Re: Hangman
Verfasst: Montag 20. Juni 2011, 13:47
von derrick
Deshalb hab ich eine englische Wortliste(keine Umlaute)

und benutz Python3

@Hyperion: Dein 3. Vorschlag wäre auch möglich aber iwie gefällt mir die Vorstellung nicht
etwas aus diesem dict zu löschen

Vor Allem da Ram und Rechenleistung in rauen Mengen vorhanden
sind

Grüße
derrick
Re: Hangman
Verfasst: Montag 20. Juni 2011, 13:58
von Hyperion
derrick hat geschrieben:
@Hyperion: Dein 3. Vorschlag wäre auch möglich aber iwie gefällt mir die Vorstellung nicht etwas aus diesem dict zu löschen
Naja, das ist ja ein spezielles Datenobjekt, welches nur temporären Bestand hat. Du änderst ja nach jedem (erfolgreichem) Tippzug auch die Liste mit den Platzhaltern. Letztlich bildest Du ja einen Zustand ab; dieser ist nun mal dynamisch. Mir fällt spontan da nichts ein, wieso man erratene Buchstaben da nicht rausnehmen könnte. Vorteil wäre, dass Du recht einfach rausfinden ohne Kalkulation rausfinden kannst, ob ein Wort eraten wurde.
Re: Hangman
Verfasst: Montag 20. Juni 2011, 14:23
von derrick
Grundsätzlich hast du mich überzeugt aber,
Code: Alles auswählen
for index in self.searched_word[guess]:
self.correct_guesses[index] = guess
Wenn man dann mit
anschliesen will muss man halt vorher überprüfen ob
guess überhaupt einen bzw. mehrere Indizes hat also vorhanden ist. Sonst gibts nen Key-Error.
Grüße
derrick
Re: Hangman
Verfasst: Montag 20. Juni 2011, 15:12
von Hyperion
derrick hat geschrieben:Grundsätzlich hast du mich überzeugt aber,
Code: Alles auswählen
for index in self.searched_word[guess]:
self.correct_guesses[index] = guess
Wenn man dann mit
anschliesen will muss man halt vorher überprüfen ob
guess überhaupt einen bzw. mehrere Indizes hat also vorhanden ist. Sonst gibts nen Key-Error.
Grüße
derrick
Naja, das ist ja nicht so wild. Damit kannst Du Dir ja dann sparen zu testen, ob der Test schon abgegeben wurde - da kommt also kein neuer Test hinzu. Ich würde da evtl. auch mit try...except arbeiten.
Re: Hangman
Verfasst: Montag 20. Juni 2011, 19:11
von lunar
@mutetella: Das ist ein Fehler in IPython. Im Standardinterpeter oder in Modulen funktioniert es:
Natürlich nur mit unicode-Objekten.
Re: Hangman
Verfasst: Montag 20. Juni 2011, 19:34
von mutetella
@lunar:
Danke für den Hinweis. Bin schon mal über den Bug gestolpert, hab' ich doch glatt vergessen...