Einfaches Schere,Stein,Papier Spiel

Code-Stücke können hier veröffentlicht werden.
Antworten
derrick
User
Beiträge: 34
Registriert: Mittwoch 8. Juni 2011, 20:32

Hallo Leute,
es geht um ein einfaches Schere,Stein,Papier Spiel dessen Code ihr hier findet.
http://www.python-forum.de/pastebin.php?mode=view&s=193
Ich bin leider ein blutiger Programmieranfänger und freue mich daher über jeden Tipp
der den Code eleganter und das Programm stabiler macht.
Gültige Eingaben sind r,p oder s.

derrick
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Da könnte man einiges zu sagen. Ich beschränke mich mal auf den Hinweis, dass return eine Anweisung und keine Funktion ist und infolgedessen ohne Klammern auskommt. Die Ermittlung des Gewinnners lässt sich erheblich kürzer fassen, etwa so:

Code: Alles auswählen

def winner(comp,user):
    if comp == user:
        return "draw"
    elif user+comp in ["rs","pr","sp"]:
        return "user"
    return "comp"
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

derrick hat geschrieben:es geht um ein einfaches Schere,Stein,Papier Spiel dessen Code ihr hier findet.
http://www.python-forum.de/pastebin.php?mode=view&s=193
Ich bin leider ein blutiger Programmieranfänger und freue mich daher über jeden Tipp
der den Code eleganter und das Programm stabiler macht.
Gültige Eingaben sind r,p oder s.
Na dann fangen wir doch genau da an. Du hast die Eingabe sehr schön in die Funktion get_userinput gepackt. Es wäre sicher sinnvoll, in dieser Funktion dann auch zu überprüfen, ob ein gültiger Buchstabe eingegeben wurde. Ich habe ein "a" eingegeben und das Programm hat mich nicht zur Neueingabe aufgefordert und hat auch beim Vergleich nicht gemerkt, dass der Wert "a" eigentlich nicht vorkommen darf. Statt dessen hatte ich das Spiel verloren - und das wo "a" (Atom bomb) ja nun wirklich eigentlich alles schlägt.

In compare_input sehe ich sehr viele Aufrufe von show_result(). Den könnte man ein einziges Mal am Ende ausführen. Eigentlich würde ich das Ergebnis überhaupt nicht aus der Vergleichsfunktion heraus anzeigen.

show_result lügt - zumindest vom Namen der Funktion her. Es zeigt nicht nur Resultate sondern rechnet auch noch. Es rechnet vor allem anhand eines Textes, der in erster Linie für die Ausgabe relevant ist. Eine Übersetzung der Ausgabetexte in eine andere Sprache oder auch nur die minimale Veränderung eines in compare_input definierten Ausgabetextes würde plötzlich falsche Ergebnisse erzeugen. Hier muss eine andere Lösung her und bei der Gelegenheit sollte man auch direkt die globals entsorgen.

Allgemein noch zum Code: Bei Rückgaben mit return solltest du die folgenden Klammern weglassen.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Mal auf die Schnelle ein paar kleine Veränderungen: http://www.python-forum.de/pastebin.php?mode=view&s=194. Man muss sich immer so zügeln, sonst entsteht gleich wieder ein Moloch der dann richtig ausgefuchst ist - und mit dem ein Anfänger nichts anfangen kann.

Mit dieser Variante ist es auf jeden Fall leichter möglich "Stein, Schere, Papier" durch "Stein, Schere, Papier, Spock, Echse" zu erweitern.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Man könnte auch noch einen "Computer vs. Computer" / "Mensch vs. Mensch"-Modus einbauen, indem man die Input-Funktionen übergibt bzw. den Code dafür auslegt, dass die Funktionen übergeben werden. Ich hatte das vor olims Zeiten mal in einem kleinen Script für das Ziegenproblem angewendet (Code ist nicht PEP8 konform!).

Dieses Spielchen ist nebenbei auch immer ein schönes Beispiel für eine nicht transitive Ordnung - schade, dass viele Lehrkräfte so etwas anschauliches nicht als Beispiel in diesem Kontext bringen. Ist bei mir zumindest nie vorgekommen ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

/me hat geschrieben:Man muss sich immer so zügeln, sonst entsteht gleich wieder ein Moloch der dann richtig ausgefuchst ist - und mit dem ein Anfänger nichts anfangen kann.
Du meinst sowas, oder? Den etwas fortgeschritteneren kann man damit immerhin zeigen, dass man oft auch ganz ohne if-Statement auskommen kann.

Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

pillmuncher hat geschrieben:Du meinst sowas, oder? Den etwas fortgeschritteneren kann man damit immerhin zeigen, dass man oft auch ganz ohne if-Statement auskommen kann.
Nice :-)

So, ich denke der Thread hat das Potenzial für Lösungen in verschiedenen Sprachen! Wer zeigt die erste Scheme / Lisp-Lösung? :mrgreen:
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 euch für euren Code!
Hab mir jetzt einiges abgeschaut und die Eingaben werden auch geprüft.
Das mit der Matrix ist genial, habe es aber mal mit numerix Vorschlag gemacht,
auch wenn das dann schwerer zu erweitern ist.
Also hier ist der neue Code(http://www.python-forum.de/pastebin.php?mode=view&s=196).
Was haltet ihr davon?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Also mal ein paar Anmerkungen:

- In Zeile 11 hast Du ein Klammerpaar zu viel

- `return` ist in Python ein Statement und keine Funktion; also solltest Du das nicht klammern (Zeilen 12 & 17)

- Operatoren sollten immer durch Leerzeichen getrennt werden (außer innerhalb von Funktionsaufrufen):

Code: Alles auswählen

# falsch
score['human']+=1
# richtig
score['human'] += 1
- die Funktion `get_computerinput` kann man einfacher fomulieren:

Code: Alles auswählen

def get_computer_input():
    return random.choice('rps')
Das Binden an eine lokale Variable ist hier unnötig, da der Name nur für das return-Statement aufgegriffen wird. Faustregel ist dabei (imho), dass man Objekte nur dann an Namen bindet, wenn man diesen Namen auch wirklich benötigt.

- in der Funktion `get_userinput` fände ich eine Fehlermeldung gut, wenn der Benutzer ein falsches Zeichen eingibt.

- Du verwendest `allowed_input` nicht konsequent (`get_computerinput`); damit ist der Vorteil einer globalen Definition quasi dahin ;-)

- in `show_winner` empfinde ich die Leerzeilen als störend.

- Shebang und Kodierungsangabe fehlen - muss natürlich nicht sein, aber bei einem "kompletten" Script gehört das imho dazu.

- Den Code ab Zeile 46 würde ich von Modulebene wegnehmen und in eine Funktion packen. Diese kann man dann über eine `main`-Funktion aufrufen.

Code: Alles auswählen

def main_loop():
    win_score = get_winscore()
    # ...

def main():
    # sys.args parsen o.ä.
    main_loop()

if __name__ == "__main__":
    main()
Darüber hinaus würde ich als nächsten Schritt weg vom statischen Mensch vs. Computer und die Funktionen aus der neuen `main`-Funktion an die `main_loop`-Funktion übergeben. Damit hast Du einfach zwei Funktionen, die die Entscheidung eines Spielers ermitteln - ob das nun ein Mensch ist, oder der PC oder eben zwei Menschen ist ja für das Spiel (also die Auswertung, das Scoring usw) egal.

Falls Du das noch nicht gesehen hast, verweise ich noch mal auf meinen obigen Post und das dort verlinkte Script. Du kannst Funktionen - wie jedes Objekt in Python - an einen Namen binden oder als Parameter übergeben.

Code: Alles auswählen

In [11]: def foo():
   ....:     print "Hallo"
   ....:     
   ....:     

In [12]: def bar():
   ....:     print "Ciao"
   ....:     
   ....:     

In [13]: def logic(func):
   ....:     func()
   ....:     
   ....:     

In [14]: logic(foo)
Hallo

In [15]: logic(bar)
Ciao

In [16]: f = foo

In [17]: f()
Hallo
Du brauchst also keine komplizierte Logik, um zu prüfen, welche Funktion Du für einen Spieler aufrufen musst, sondern übergibst einfach die gewünschte(n) Funktion(en) an Deine Spiellogik-Funktion und gut ist.

Die Namen `user_input` und `computer_input` passen dann natürlich nicht mehr; könnte man dann neutral `user_a` und `user_b` nennen¹ oder in eine Liste / Dict verpacken. Da Du ja bereits ein `score`-Dict hast, könnte man das um die Funktion erweitern? Ich würde dann noch `argparse` nutzen, um den `winscore` optional übergeben zu können und vor allem die Funktionen, welche die beiden Spielereingaben bestimmen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten