Hangman

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
derrick
User
Beiträge: 34
Registriert: Mittwoch 8. Juni 2011, 20:32

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 :oops: also bitte an die Inputaufforderung halten :D!

Vielen Dank für eure Mühe
derrick
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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 ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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`).
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
derrick
User
Beiträge: 34
Registriert: Mittwoch 8. Juni 2011, 20:32

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
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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:

Code: Alles auswählen

len((_ for _ in guessed_chars if _ == "_"))
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 :-D -> 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.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

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
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
derrick
User
Beiträge: 34
Registriert: Mittwoch 8. Juni 2011, 20:32

Danke euch beiden für die Vorschläge hab sie nun eingebaut.
@Hyperion: Ich habe mich für

Code: Alles auswählen

 len((_ for _ in guessed_chars if _ == "_"))
entschieden wobei
die inneren runden Klammern eckig sein müssen :D
Viele Grüße
derrick
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

derrick hat geschrieben: die inneren runden Klammern eckig sein müssen :D
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?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

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:

Code: Alles auswählen

>>> 'ü'.isalpha()
True
:?: :?: :?:
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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 :D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
derrick
User
Beiträge: 34
Registriert: Mittwoch 8. Juni 2011, 20:32

Deshalb hab ich eine englische Wortliste(keine Umlaute) :D und benutz Python3 :P
@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 :D
Grüße
derrick
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
derrick
User
Beiträge: 34
Registriert: Mittwoch 8. Juni 2011, 20:32

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

Code: Alles auswählen

self.searched_word.pop(guess)
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
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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

Code: Alles auswählen

self.searched_word.pop(guess)
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.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
lunar

@mutetella: Das ist ein Fehler in IPython. Im Standardinterpeter oder in Modulen funktioniert es:

Code: Alles auswählen

>>> u'ü'.isalpha()
True
Natürlich nur mit unicode-Objekten.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@lunar:
Danke für den Hinweis. Bin schon mal über den Bug gestolpert, hab' ich doch glatt vergessen...
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten