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
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Ich habe mich mal ein einem Hangman-Spiel versucht und habe ein paar Fehler. Die Variable "fehler" wird z.B. falsch gezählt, sicherlich weil die for-Schleife jeden Buchstaben prüft. Mir fällt leider nur keine andere Möglichkeit ein. Auch die Funktion "erneut spielen" funktioniert noch nicht. Es wäre nett, wenn mir einer hilft.

Code: Alles auswählen

import random
import sys

def wortWahl():
	auswahl = random.randint(0, 4) 
	global wort
	if auswahl == 1:
		neu = ['M','A','U','S']
		wort.extend(neu)
	elif auswahl == 2:
		neu = ['T','A','S','T','A','T','U','R']
		wort.extend(neu)
	elif auswahl == 3:
		neu = ['D','R','U','C','K','E','R']
		wort.extend(neu)
	elif auswahl == 0:
		neu = ['M','A','C']
		wort.extend(neu)
	else:
		neu = ['B','U','C','H']
		wort.extend(neu)

def linie():
	print("------------------------------------------------------------ ##")

def aufgabe():
	print("\n************************************************************ ##\n")

def erneutSpielen():
	linie()
	erneut = raw_input("Erneut spielen? (j/n) ")
	if erneut == "n" or erneut == "no" or erneut == "nein": 
		sys.exit()

wort = []
wortWahl()
strich = ['_'] * len(wort) 

gesucht = len(wort)
spielen = True
fehler = 0

print("Galgenraten Version 1")

while spielen == True:
	
	while gesucht > 0:
		aufgabe()
		print strich
		aufgabe()
		eingabe = raw_input("Buchstaben eingeben: ").upper()
		i = 0
		for x in wort: 
			# ------ Eingabe wird mit jedem Buchstaben des Liste verglichen ------ #
			if eingabe == x: 	
				linie()
				print("Richtig")
				strich[i] = wort[i]
				gesucht -= 1
				if gesucht == 0:
					break
			if eingabe != 0:
				fehler += 1
				if fehler > len(wort)*len(wort):
					print("Du wurdest gehaengt!")
					print("Gesucht war: ", wort)
					sys.exit()
			i += 1
	else:
		linie()
		print("Gewonnen!")
		print("Anzahl der Fehler: ", fehler)
		print("Gesucht war: ", wort)
		erneutSpielen()
Design-Ratschläge wären auch gut. :D
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@_nohtyp_: "global" kannst Du als Anfänger sofort wieder vergessen. Überall wo Du "global" einsetzt machst Du einen Designfehler.
Statt einer Zahl kannst Du mit "random.choice" direkt eine Wort aus einer Liste zufällig auswerten.
Alle Berechnungen sollten in einer Funktion stattfinden und nicht auf oberster Ebene. Dafür kannst Du zum Beispiel eine "main"-Funktion definieren und alles was jetzt außerhalb steht hineinpacken. "i" wird nirgends benutzt. Statt "gesucht" mitzuzählen, kannst Du einfach über " '_' in strich " prüfen, ob noch ein Buchstabe fehlt.
Schreib am Besten eine Funktion, die "strich" und "wort" und "eingabe" als Parameter nimmt und eine neue Liste zurückgibt, wo die passenden Striche durch diesen Buchstaben ersetzt wurde. Falls der Buchstabe nicht gefunden wurde, kannst Du eine Exception werfen und die im Hauptteil abfangen und den Fehler dann hochzählen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo und willkommen im Forum!

Bei deinem Programm kann man noch eine ganze Menge verbessern, ich fange einfach mal von oben nach unten an:

- Funktionen sollten (in der Regel) eine Tätigkeit beschreiben, daher enthalten diese typischerweise ein Verb. Deine Funktion "wortWahl" sollte daher besser "wort_waehlen" oder ähnlich heißen. Bei der Gelegenheit kannst du auch einen Blick auf PEP 8 werfen. Funktionsnamen werden in Python durch_unterstriche_getrennt.

- Die Verwendung von "global" ist eine ganz schlechte Idee. Damit schaffst du Abhängigkeiten im Code, welche nur schwer zu überblicken sind. Dadurch entstehen sehr leicht Fehler, welche dazu noch schwer zu finden sind. Werte betreten eine Funktion in der Regel über Parameter und verlassen sie über den Rückgabewert. Das solltest du dir ganz schnell angewöhnen. Vergiss also am besten gleich, dass es "global" überhaupt gibt. Wenn du das verwendest, dann machst du etwas falsch.

- Deine Funktion zur Wortauswahl ist äußert ungeschickt, du machst ganz oft das selbe. Du wandelst das Wort, auch noch per Hand, in eine Liste um und hängst das an "wort" an (was eine ganz schlechte Idee ist; sie Rückgabewerte). Wie stellst du dir das vor, wenn du tausende von Wörtern hast? Willst du für jedes Worte extra drei Zeilen Code schreibe? Sicher nicht, daher verwendest du am besten entsprechende Datenstrukturen und passt die Auswahlfunktion entsprechen an:

Code: Alles auswählen

WOERTER = "Maus", "Tastatur", "Drucker", "Mac", "Buch"

def wort_waehlen():
    return random.choice(WOERTER).lower()
Das macht das gleiche wie deine Funktion, nur in kurz und in sauber.

- Du verwendest offensichtlich noch Python 2.x, dort ist print noch ein Statement. Die Klammern nach dem print solltest du daher weglassen

- Wenn du einen String sehr oft wiederholen möchtest, zum Beispiel in deiner "linie"- oder "aufgabe"-Funktion, dann kannst du dazu die Multiplikation verwenden:

Code: Alles auswählen

print "-"*50
- Warum heißt die "aufgabe"-Funktion "aufgabe"? Es wird doch gar keine Aufgabe gestellt.

- "erneutSpielen" ist auch verbesserungswürdig. Statt mehrfach auf Gleichheit zu testen, bietet sich der in-Operator an:

Code: Alles auswählen

if erneut in ("n", "no", "nein")
Auch sollte man das Programm nicht einfach so mit "sys.exit" verlassen. Schon gar nicht aus einer Funktion in die man hineingesprungen ist, das ist sehr unübersichtlich.

- Code sollte nie auf modulebene stehen, da gehören nur Importe, Konstanten und Definitionen von Funktion und Klassen hin. Andernfalls gibt es Probleme, falls du dein Modul mal importieren möchtest. In Python gibt es dazu ein Idiom:

Code: Alles auswählen

def main():
    ...

if __name__ == "__main__":
    main()
In die main-Funktion packst du dann alles rein, was dein Spiel startet. So kannst du einzelene Funktionen aus deinem Programm wiederverwenden und es ist gleich ersichtlich, wo in dein Programm eingestiegen wird.

- "spielen" ist vollkommen überflüssig, du verwendest es nirgends.

- ``while x == True`` ist total überflüssig. Wenn x den Wert True hatte, dann kommt nur wieder True raus, wenn nicht, dann eben False. Du kannst also gleich ``while x`` schreiben.

- Du solltest mehr auf Funktionen setzen. Damit lässt sich viel Übersichtlichkeit schaffen und du kannst einzelne Teile wiederverwenden. Die Abfrage eines neuen Buchstaben bietet sich dazu an:

Code: Alles auswählen

def buchstabe_abfragen():
    while True:
        buchstabe = raw_input("Buchstaben eingeben:").lower()
        
        if len(buchstabe) != 1:
            print "Es muss genau ein Buchstabe eingegeben werden!"
            print
        elif buchstabe not in string.ascii_lowercase:
            print "Es hadelt sich nicht um einen gültigen Buchstaben!"
            print
        else:
            return buchstabe
- Generell ist deine ganze Logik des Programms etwas zu kompliziert geworden. Ich mache einfach mal einen Gegenvorschlag:

Code: Alles auswählen

#coding: utf-8
import random
import string


WOERTER = "Maus", "Tastatur", "Drucker", "Mac", "Buch"
MAX_FEHLER = 8

def wort_waehlen():
    return random.choice(WOERTER).lower()


def buchstabe_abfragen():
    while True:
        buchstabe = raw_input("Buchstaben eingeben:").lower()
        
        if len(buchstabe) != 1:
            print "Es muss genau ein Buchstabe eingegeben werden!"
            print
        elif buchstabe not in string.ascii_lowercase:
            print "Es hadelt sich nicht um einen gültigen Buchstaben!"
            print
        else:
            return buchstabe


def wort_anzeigen(loesung, eingaben):
    print "".join("_" if c not in eingaben else c for c in loesung)


def abfragen(frage):
    eingabe = raw_input(frage)
    
    return eingabe in ("j", "ja")


def main():
    while True:
        loesung = wort_waehlen()
        eingaben = set()
        
        wort_anzeigen(loesung, eingaben)
        
        fehler = 0
        while fehler < MAX_FEHLER:
            buchstabe = buchstabe_abfragen()
            if buchstabe not in loesung:
                fehler += 1
            
            eingaben.add(buchstabe)
            
            wort_anzeigen(loesung, eingaben)
            
            if set(loesung).issubset(eingaben):
                #Lösung erraten
                print "Richtig!"
                print
                break
        else:
            #Lösung nicht erraten
            print "Es wurden zu viele Fehler gemacht, die Lösung lautet:"
            print loesung
            print
        
        if not abfragen("Noch eine Runde spielen?"):
            break

if __name__ == "__main__":
    main()
Bei dem Code kann man auch noch einiges besser machen, so weicht der Vorschlag aber nicht zu weit von deiner Lösung ab.
Das Leben ist wie ein Tennisball.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

@ EyDu
Danke für die ausführliche Antwort und die hilfreichen Tipps. Ich werde sie mir zu Herzen nehmen.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

@ EyDu
Kannst du mir diese Stelle erklären?

Code: Alles auswählen

def wort_anzeigen(loesung, eingaben):
    print "".join("_" if c not in eingaben else c for c in loesung)
Ich versuche gerade die Funktionsweise zu verstehen, aber ich scheitere. :D
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

Öffne mal python und probier den Ausdruck von rechts nach links auszugeben und stetig erweitern.
Ansonsten mal die Abschnitte Iterieren über String, Ternärer Operator und natürlich einmal ausprobieren was "".join(["a", "b", "c"]) macht.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Antworten