Chuck-A-Luck

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
dulac
User
Beiträge: 7
Registriert: Donnerstag 27. März 2014, 21:03
Wohnort: Pythonesien

Hallo,

wir sollten für die Schule ein Programm zum Glücksspiel "Chuck-A-Luck" programmieren, welches übrigens verboten ist weil es extrem vorteilhaft für die Bank ist;)

Schnell die Regeln:
Man zahlt einen Einsatz, wählt dann eine Zahl aus und würfelt mit 3 Würfeln.
Wenn man die Zahl einmal trifft, erhält man zweimal den Einsatz, das heißt man gewinnt einmal den Einsatz.
Für jeden weiteren Treffer, erhält man nochmal den Einsatz, das ganze Spiel wird übrigens fair wenn man hier für jeden weiteren Treffer 2 mal den Einsatz erhält, falls ich mich nicht verrechnet hab...^^
Das ganze könnt ihr auch im Programm nachlesen, wenn ihr auf das Fragezeichen klickt(Allerdings sieht das iwie blöd aus, hab aber keinen Plan wie das besser aussehen könnte^^)

Hier unten der Code, ich bin ganz zufrieden damit und würde gerne eure Meinungen hören:)

Code: Alles auswählen

#-------------------------------------------------------------------------------
# Name:        module1
# Purpose:
#
# Author:      Eric
#
# Created:     03.06.2014
# Copyright:   (c) Eric 2014
# Licence:     <your licence>
#-------------------------------------------------------------------------------

try:
    from tkinter import *
except ImportError:
    from Tkinter import *

from random import randint

class Spielzahl(object):
    def __init__(self):
        self.spielzahl = None

    def setzen(self,wert):
        self.spielzahl = wert

class Wuerfel(object):
    def __init__(self):
        self.augen = randint(1,6)
    def werfen(self):
        self.augen = randint(1,6)

class Konto(object):
    def __init__(self,stand=0):
        self.stand = int(stand)

    def einzahlen(self,betrag):
        self.stand += betrag

    def auszahlen(self,betrag):
        self.stand -= betrag

class CaLM(object):
    def __init__(self):
        self.k = Konto(randint(50,150))
        self.s = Spielzahl()
        self.wA = Wuerfel()
        self.wB = Wuerfel()
        self.wC = Wuerfel()

    def einsatzZahlen(self,einsatz):
        if self.k.stand<=0:
            print("Konto leer! Bitte verlassen sie nun unser Kasino")
            return 1
        else:
            if self.k.stand-einsatz < 0:
                print("So viel Geld haben sie leider nicht mehr! Setzen sie weniger!")
                return 2
            else:
                self.k.auszahlen(einsatz)
                self.einsatz = einsatz
                return 3

    def spielzahlSetzen(self,zahl):
        self.s.setzen(zahl)

    def wuerfelWerfen(self):
        self.wA.werfen()
        self.wB.werfen()
        self.wC.werfen()

    def gewinnAuszahlen(self):
        anzahlTreffer=0
        if self.wA.augen == self.s.spielzahl:
            anzahlTreffer += 1
        if self.wB.augen == self.s.spielzahl:
            anzahlTreffer += 1
        if self.wC.augen == self.s.spielzahl:
            anzahlTreffer += 1
        if anzahlTreffer >= 1:
            self.k.einzahlen(2*self.einsatz)
            if anzahlTreffer >= 2:
                self.k.einzahlen(self.einsatz)
                if anzahlTreffer == 3:
                    self.k.einzahlen(self.einsatz)

class CaLGUI(Frame):
    def __init__(self,master,imageListe):

        self.imageListe = imageListe

        self.hilfzaehler=0
        self.model=CaLM()
        Frame.__init__(self,master)
        self.menu=Menu(self.master)
        self.master.config(menu=self.menu)
        self.menu.add_checkbutton(label="Beenden",command=self.master.destroy)
        self.optionmenu=Menu(self.menu)
        self.menu.add_cascade(label="Optionen",menu=self.optionmenu)
        self.optionmenu.add_command(label="Geld Einzahlen",command=self.geldEinzahlen)
        self.LabelChuckALuck = Label(master,background="darkgrey",text="Chuck-A-Luck",font=("timesnewroman",15))
        self.LabelChuckALuck.place(x=5,y=5,width=420,height=25)
        self.infoButton = Button(master,background="white",text="?",font=("timesnewroman",15),relief=SOLID,command=self.info)
        self.infoButton.place(x=430,y=5,width=30,height=25)

        self.master.geometry("465x141")
        self.master.title("Chuck-A-Luck")

        self.frameZahl = Frame(master,background="lightgreen")
        self.frameZahl.place(x=120,y=35,width=110,height=100)
        self.labelUeberschriftZahl = Label(self.frameZahl,text="Spielzahl",background="white")
        self.labelUeberschriftZahl.place(x=5,y=5,width=100,height=20)
        self.spielzahl = IntVar()
        self.radiobutton1 = Radiobutton(self.frameZahl,text="1",value=1,variable=self.spielzahl,command=self.zahl,state=DISABLED)
        self.radiobutton1.place(x=5,y=30,width=30,height=18)
        self.radiobutton2 = Radiobutton(self.frameZahl,text="2",value=2,variable=self.spielzahl,command=self.zahl,state=DISABLED)
        self.radiobutton2.place(x=40,y=30,width=30,height=18)
        self.radiobutton3 = Radiobutton(self.frameZahl,text="3",value=3,variable=self.spielzahl,command=self.zahl,state=DISABLED)
        self.radiobutton3.place(x=75,y=30,width=30,height=18)
        self.radiobutton4 = Radiobutton(self.frameZahl,text="4",value=4,variable=self.spielzahl,command=self.zahl,state=DISABLED)
        self.radiobutton4.place(x=5,y=53,width=30,height=18)
        self.radiobutton5 = Radiobutton(self.frameZahl,text="5",value=5,variable=self.spielzahl,command=self.zahl,state=DISABLED)
        self.radiobutton5.place(x=40,y=53,width=30,height=18)
        self.radiobutton6 = Radiobutton(self.frameZahl,text="6",value=6,variable=self.spielzahl,command=self.zahl,state=DISABLED)
        self.radiobutton6.place(x=75,y=53,width=30,height=18)
        self.gewinnButton = Button(self.frameZahl,text="Gewinn auszahlen",relief=SOLID,background="white",command=self.gewinn,state=DISABLED)
        self.gewinnButton.place(x=5,y=76,width=100,height=20)

        self.frameKonto = Frame(master,background="red")
        self.frameKonto.place(x=5,y=35,width=110,height=100)
        self.labelUeberschriftKonto = Label(self.frameKonto,text="Konto",background="white")
        self.labelUeberschriftKonto.place(x=5,y=5,width=100,height=20)
        self.einsatzButton = Button(self.frameKonto,text="Einsatz zahlen",relief=SOLID,background="white",command=self.einsatz)
        self.einsatzButton.place(x=5,y=76,width=100,height=20)
        self.einsatzEntry = Entry(self.frameKonto)
        self.einsatzEntry.place(x=5,y=51,width=100,height=20)
        self.standLabel = Label(self.frameKonto,text=self.model.k.stand,background="white")
        self.standLabel.place(x=40,y=30,width=30,height=15)

        self.frameWuerfel = Frame(master,background="orange")
        self.frameWuerfel.place(x=235,y=35,width=110,height=100)
        self.labelUeberschriftWuerfel = Label(self.frameWuerfel,text="Wuerfel",background="white")
        self.labelUeberschriftWuerfel.place(x=5,y=5,width=100,height=20)
        self.labelwA = Label(self.frameWuerfel,image=self.imageListe[self.model.wA.augen-1],relief=SOLID)
        self.labelwA.place(x=5,y=35,width=30,height=31)
        self.labelwB = Label(self.frameWuerfel,image=self.imageListe[self.model.wB.augen-1],relief=SOLID)
        self.labelwB.place(x=40,y=35,width=30,height=31)
        self.labelwC = Label(self.frameWuerfel,image=self.imageListe[self.model.wC.augen-1],relief=SOLID)
        self.labelwC.place(x=75,y=35,width=30,height=31)
        self.werfButton = Button(self.frameWuerfel,text="Wuerfel werfen",relief=SOLID,background="white",command=self.werfen,state=DISABLED)
        self.werfButton.place(x=5,y=76,width=100,height=20)

        self.frameZaehler=Frame(master,background="lightblue")
        self.frameZaehler.place(x=350,y=35,width=110,height=100)
        self.labelUeberschriftZaehler=Label(self.frameZaehler,text="Zaehler",background="white")
        self.labelUeberschriftZaehler.place(x=5,y=5,width=100,height=20)
        self.eingabe=Entry(self.frameZaehler)
        self.eingabe.place(x=5,y=35,width=100,height=31)
        self.zaehlButton=Button(self.frameZaehler,text="Spiele durchfuehren",relief=SOLID,background="white",command=self.testspiele)
        self.zaehlButton.place(x=5,y=76,width=100,height=20)

    def einsatz(self):
        x=self.model.einsatzZahlen(int(self.einsatzEntry.get()))
        self.standLabel.configure(text=self.model.k.stand)
        if x==1:
            self.infoButton.config(state=DISABLED)
        if x==1 or x==3:
            self.einsatzEntry.config(state=DISABLED)
            self.einsatzButton.config(state=DISABLED)
            self.zaehlButton.config(state=DISABLED)
        if x==3:
            self.radiobutton1.config(state=NORMAL)
            self.radiobutton2.config(state=NORMAL)
            self.radiobutton3.config(state=NORMAL)
            self.radiobutton4.config(state=NORMAL)
            self.radiobutton5.config(state=NORMAL)
            self.radiobutton6.config(state=NORMAL)

    def zahl(self):
        self.model.spielzahlSetzen(self.spielzahl.get())
        self.radiobutton1.config(state=DISABLED)
        self.radiobutton2.config(state=DISABLED)
        self.radiobutton3.config(state=DISABLED)
        self.radiobutton4.config(state=DISABLED)
        self.radiobutton5.config(state=DISABLED)
        self.radiobutton6.config(state=DISABLED)
        self.werfButton.config(state=NORMAL)

    def werfen(self):
        if self.hilfzaehler<=10:
            self.hilfzaehler+=1
            self.model.wuerfelWerfen()
            self.labelwA.config(image=self.imageListe[(self.model.wA.augen)-1])
            self.labelwB.config(image=self.imageListe[(self.model.wB.augen)-1])
            self.labelwC.config(image=self.imageListe[(self.model.wC.augen)-1])
            self.master.after(75,self.werfen)
        else:
            self.werfButton.config(state=DISABLED)
            self.gewinnButton.config(state=NORMAL)
            self.hilfzaehler=0

    def testspiele(self):
        hilf1=self.model.k.stand
        for i in range(int(self.eingabe.get())):
            self.model.einsatzZahlen(1)
            self.model.spielzahlSetzen(randint(1,6))
            self.model.wuerfelWerfen()
            self.gewinn()
            if self.model.k.stand<=0:
                print("Nach",i,"Spielen hatten sie kein Geld mehr!")
                hilf2=self.model.k.stand
                hilf3=hilf1-hilf2
                print("Sie haben",hilf3/i,"EUR pro Spiel verloren.")
                break
        self.standLabel.config(text=self.model.k.stand)

    def gewinn(self):
        self.model.gewinnAuszahlen()
        self.standLabel.configure(text=self.model.k.stand)
        self.gewinnButton.config(state=DISABLED)
        self.einsatzEntry.config(state=NORMAL)
        self.einsatzEntry.delete(0,END)
        self.einsatzButton.config(state=NORMAL)
        self.zaehlButton.config(state=NORMAL)

    def info(self):
        infText="""Setzen sie ihren Einsatz und treffen sie die richtige Zahl!
        Fuer einen Treffer erhalten sie ihren doppelten Einsatz!
        Fuer jeden weiteren Treffer gibt es nochmal den Einsatz!"""
        self.info=Toplevel()
        self.info.title("Chuck-A-Luck")
        self.info.geometry("300x210")
        self.infoFrame=Frame(self.info,background="yellow")
        self.infoFrame.place(x=5,y=5,width=290,height=200)
        self.msg=Message(self.infoFrame,background="white",text=infText,font=("timesnewroman",11),anchor=NW)
        self.msg.place(x=5,y=5,width=280,height=180)
        self.infoButton=Button(self.infoFrame,background="white",text="Bestaetigen!",command=self.zerstoeren)
        self.infoButton.place(x=5,y=170,height=25,width=280)
        self.info.transient(self.master)
        self.info.grab_set()
        self.master.wait_window(self.info)

    def zerstoeren(self):
        self.info.destroy()

    def geldEinzahlen(self):
        passwort=input("Passwort Bitte!")
        if passwort==str(self.model.k.stand):
            geld=int(input("Wie viel Geld soll eingezahlt werden? (In EUR)"))
        else:
            geld=0
        self.model.k.einzahlen(geld)
        self.standLabel.config(text=self.model.k.stand)

def main():
    root=Tk()
    bilder=[PhotoImage(file='w1.gif'),
            PhotoImage(file='w2.gif'),
            PhotoImage(file='w3.gif'),
            PhotoImage(file='w4.gif'),
            PhotoImage(file='w5.gif'),
            PhotoImage(file='w6.gif')]
    root=CaLGUI(root,bilder)
    root.mainloop()

if __name__=='__main__':
    main()
Ob man für die Spielzahl jetzt ne eigene Klasse machen sollte weiß ich nicht...unser Lehrer wollte das so.
Das Passwort um neues Geld einzuzahlen ist immer der aktuelle Kontostand^^, dazu will ich noch fragen ob es möglich ist bei Inputs "*" anzeigen zu lassen, bei Entrys geht das auf jeden Fall.

Ich freu mich auf eure Kommentare:D

Dulac

Code: Alles auswählen

dulac="anfaenger"
if dulac==anfaenger:
    print("Nehmt Rücksicht auf AnfaengerO_o")
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@dulac: Du solltest dich mal mit der sinnvollen Verwendung von Listen beschäftigen. Überall wo du durchnummerierst oder Benennungen nach dem Schema A, B, C anwendest, ist der Einsatz von Listen besser geeignet. Das spart einem die Durchnummerierung und sorgt dafür, dass man nicht mehr ganz soviele Instanzattribute für das Klassenexemplar rumschwirren hat. Außerdem kann man gleichlautende Aktionen auf den Objekten dann prima in einer Schleife erledigen anstatt ständig Code mit minimalen Unterschieden kopieren zu müssen.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@dulac: erstmal das Übliche: vermeide *-Importe, da Du dann nicht weißt, welche Namen Du Dir alles in den Namensraum holst, und eventuelle Namenskonflikte nur schwer entdecken kannst. Namen sollen dem Lesen sagen, was in der Variable gespeichert ist, bei 'k', 's', 'wA' usw. ist das nicht möglich.

Die Trennung zwischen Spiellogik und Anzeige ist fast vollständig. Aber bei `self.model.k.stand` weiß die GUI eindeutig zu viel vom Model.

`Spielzahl`, `Wuerfel` und `Konto` sind ein schönes Beispiel von Over-Design. Das sind alles einfache Zahlen und den Sinn einer eigenen Klasse muß ich sehr lange suchen.
dulac
User
Beiträge: 7
Registriert: Donnerstag 27. März 2014, 21:03
Wohnort: Pythonesien

Danke erstmal für die beiden Beiträge:D

@snafu: Du meinst ich soll z.B. bei den Radiobuttons die Alle in die Liste packen und bei der Statusänderung(DISABLED<--->NORMAL) dann schreiben:

Code: Alles auswählen

for radioButton in radioListe:
    radiobButton.config(state=DISABLED)
?
Da stellt sich mir grad die Frage wo ich überall ein "self" vorsetzen muss...nur beim Schleifenkopf vor "radioListe"?
Oder auch in der Schleife selbst...?

@Sirius3: Soll ich dann schreiben:

Code: Alles auswählen

from tkinter import Label,Radiobuttons[...]
Ist das nicht ein bischen viel...?
Und was meinst du die GUI "weiß" zu viel? soll ich eine Methode schreiben, welche den Stand zurückgibt? Ist doch iwie überflüssig?
Und mit dem Overdesign weiß ich das, allerdings wurden die Klassendiagramme von unserem Lehrer vorgegeben!

Und mit den Namen...--->Konto, s--->Spielzahl, wA--->WuerfelA, das alles immer auszuschreiben dauert doch länger...? und der BEnutzer liest die Variablennamen doch ehe nicht!

Danke für Antworten im Voraus;)

Dulac

Code: Alles auswählen

dulac="anfaenger"
if dulac==anfaenger:
    print("Nehmt Rücksicht auf AnfaengerO_o")
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@dulac: Mit Listen als Attribute funktioniert es genau wie mit anderen Objekten: Du holst dir das Objekt via ``self.objektnahme`` in den lokalen Namensraum und machst dann etwas auf diesem Objekt. Wenn du weißt, wie du über Listen mittels ``for``-Schleife iterieren kannst, dann musst du dieses Wissen halt nur auf ``self.liste`` anwenden. Das sind unter'm Strich keine Objekte mit Besonderheiten. Nur die Semantik für den Zugriff ist halt leicht anders. Du kannst theoretisch immer, wenn du ``do_something(self.obj)`` schreibst auch genau so gut schreiben:

Code: Alles auswählen

obj = self.obj
do_something(obj)
Vielleicht wird es dadurch ja klarer. Aber in der Praxis spart man sich diesen Zwischenschritt in aller Regel.
BlackJack

@dulac: Die Namen sind ja auch nicht für den Benutzer sondern für den Programmierer. Und Du magst *jetzt* noch wissen wofür diese kryptischen Abkürzungen stehen, aber in zwei, drei Monaten musst Du dann selber rätselraten oder immer wieder die Stellen suchen wo die Sachen definiert wurden.

Für das Klassendiagramm gehört der Lehrer geteert und gefedert. Klassen und OOP sind kein selbstszweck und man sollte schon darauf achten diese Sprachmittel *sinnvoll* einzusetzen.
dulac
User
Beiträge: 7
Registriert: Donnerstag 27. März 2014, 21:03
Wohnort: Pythonesien

@snafu: OK...Also würde es dann so gehen:

Code: Alles auswählen

try:
    from tkinter import Tk,Radiobutton,Frame,Button
except ImportError:
    from Tkinter import Tk,Radiobutton,Frame,Button

class RadioListe(Frame):
    def __init__(self,master):
        xpos=5
        ypos=5
        Frame.__init__(self,master)
        self.radioListe=[]
        for i in range(6):
            hilf=Radiobutton(self.master)
            hilf.place(x=xpos,y=ypos)
            self.radioListe.append(hilf)
            ypos+=20
        self.testButton=Button(self.master,command=self.test)
        self.testButton.place(x=xpos,y=ypos,width=20)

    def test(self):
        for z in self.radioListe:
            z.config(state=DISABLED)


root=Tk()
x=RadioListe(root)
root.mainloop()
Is das so mit dem Import richtig? Ich importiere ja nur was ich brauche...Labels ja z.B. nicht.
Und ansonsten hab ich es kapiert mit den listen, funktioniert auch so weit:)
Beim klicken werden die Radiobuttons halt Disabled.

Code: Alles auswählen

dulac="anfaenger"
if dulac==anfaenger:
    print("Nehmt Rücksicht auf AnfaengerO_o")
BlackJack

@dulac: Statt Sternchenimport ist bei `Tkinter` üblich das Modul beim Importieren an den kürzeren Namen `tk` zu binden, also ``import Tkinter as tk``, und dann mit `tk.Frame` usw. auf die Objekte in dem Modul zuzugreifen.

Namen nicht mit ein oder zwei Buchstaben abzukürzen sollte das schreiben des Programms übrigens auch nicht schneller machen, denn jeder vernünftige Editor bietet mindestens eine auf den bereits in der Datei vorhandenen Worten basierende Autovervollständigung an. Und selbst wenn das schreiben dadurch länger dauert, wäre das am falschen Ende gespart, denn Quelltext wird öfter gelesen, und muss dabei verstanden werden, als das er geschrieben wird.

Wenn man die Namen ausschreibt, dann wird auch klar wie hirntot dieser Klassenentwurf ist, denn dann steht da tatsächlich und mehrfach so etwas wie `self.spielzahl.spielzahl` im Quelltext.

Es wurde auch sehr mit Leerzeichen gespart. Es wäre leichter lesbar wenn nicht alles so aneinander kleben würde. Da hat der Style Guide for Python Code etwas zu. Auch zu der Namensschreibweise die sich nicht an die Konvention hält.

Das mit dem `place()` ist gar keine gute Idee. Absolute Platzierungen und Grössenvorgaben gehen heutzutage bei den vielen Variationen von Anzeigegrössen und Pixelauflösungen überhaupt nicht. Bei mir wird bei Deiner GUI zum Beispiel einiges an Text einfach abgeschnitten, weil es nicht in den Platz passt den Du dafür absolut vorgegeben hast. Es wird auch schwierig Teile der GUI zu verändern, wenn man dann von Hand auch alles was von einer Veränderung betroffen ist, entsprechend verschieben und/oder in der Grösse anpassen muss. Und Du hast ja echt *alles* mit `place()` angeordnet.

Ich finde die Programmführung auch komisch bis unpraktisch. Zum Beispiel das man eine Zahl auswählt und das gleich beim ersten Klick fest ist und man sich nicht umentscheiden kann. Diese Einschränkung ist unschön und auch gar nicht nötig. Ich hätte diese Eingabe durch den Start des Würfelns beendet.

Das es im GUI-Programm noch ein und ausgaben über die Textkonsole gibt, ist mir erst gar nicht aufgefallen. Entweder hat man ein GUI-Programm oder nicht. Dieses gemisch wird spätestens dann unbenutzbar wenn jemand das Programm nicht von einem Terminal aus startet.

Der Auszahlen-Knopf ist IMHO auch überflüssig. Nach dem Würfeln kann man den Benutzer über den Gewinn (oder nicht-Gewinn) informieren und dann sollte IMHO auch gleich das setzen eines Geldbetrags möglich sein. Der Extra-Knopf ist ein unnötiger Schritt.

In der `info()`-Methode wird ein `info`-Attribut gesetzt, was dann natürlich die `info()`-Methode überschreibt. Ausserhalb der `__init__()`-Methode sollten sowieso eigentlich keine neuen Attribute eingeführt werden. Nach dem die `__init__()`-Methode durchlaufen ist, sollte das Objekt in einem vollständig initialisierten Zustand sein. Es gibt noch drei andere Attribute die nicht in der `__init__()` erstmals gesetzt werden. Vielleicht sollten das auch gar keine Attribute sein. Das Attribut `hilfzaehler` wird zwar in der `__init__()` definiert, sollte aber eher gar kein Attribut sein. Zumindest nicht auf *dem* Objekt. Die GUI könnte man nämlich im Gegensatz zum Spiel, deutlich sinnvoller in mehr Klassen zerlegen. Dann wäre die `__init__()` wahrscheinlich auch nicht 90 Zeilen lang. Andererseits hätte ich den Zähler wahrscheinlich einfach als Argument an die `werfen()`-Methode übergeben. Denn `after()` kann man auch zusätzlich Argumente für die aufzurufene Funktion/Methode mitgeben. Warum rufst Du `after()` eigentlich auf `self.master` auf statt auf `self`?

Wenn das mit Python 2 und 3 laufen soll, dann sollte man nicht die `input()`-Funktion in Python 2 verwenden, denn da hiess die nocht `raw_input()`. Und man sollte ``print`` mit ``from __future__ import print_function`` vom Schlüsselwort zur Funktion machen.
BlackJack

@dulac: Und nochmal ich: Habe gerade mal die Regeln des Spiels nachgeschlagen und was Du auf jeden Fall konfigurierbar machen solltest, sind die Quoten, denn weder der englische noch der deutsche Wikipedia-Artikel deckt sich mit Deinen Gewinnberechnungen, dafür enthalten beide, teilweise unterschiedliche, Varianten der Quoten für 1, 2, oder 3 Übereinstimmungen. Dann könnte man im `Optionen`-Menü auch mehr als einen Untermenüpunkt unterbringen. :-)

Ich habe es mal weniger ”overengineered” umgesetzt:

Code: Alles auswählen

#!/usr/bin/env python
# encoding: utf-8
from __future__ import unicode_literals
import Tkinter as tk
from functools import partial
from random import randint
from tkMessageBox import showerror


roll_dice = partial(randint, 1, 6)


class ChuckALuck(object):

    def __init__(self, quota=(0, 1, 2, 10)):
        self.quota = quota
        self.dice_values = None
        self.number = None
        self.roll_dices()

    def roll_dices(self):
        self.dice_values = [roll_dice() for _ in xrange(len(self.quota) - 1)]

    def play(self, guess, bet):
        self.roll_dices()
        return self.quota[self.dice_values.count(guess)] * bet


class Account(object):

    def __init__(self, balance=0):
        self.balance = balance

    def withdraw(self, amount):
        if amount > self.balance:
            raise ValueError('not enough money')
        self.balance -= amount

    def deposit(self, amount):
        self.balance += amount


class DiceUI(tk.Label):

    def __init__(self, master, images):
        tk.Label.__init__(self, master)
        self.images = images
        self.set_value(1)

    def set_value(self, value):
        self['image'] = self.images[value - 1]


class ChuckALuckUI(tk.Frame):

    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.account = Account(1000)
        self.game = ChuckALuck()

        frame = tk.Frame(self)
        tk.Label(frame, text='Kontostand').pack()
        self.account_display = tk.Label(frame)
        self.account_display.pack()
        self.wager_variable = tk.IntVar(frame, 10)
        wager_entry = tk.Entry(frame, textvariable=self.wager_variable)
        wager_entry.bind('<Return>', self.do_roll)
        wager_entry.pack()
        frame.pack()

        frame = tk.Frame(self)
        self.guess_variable = tk.IntVar(frame)
        tk.Label(frame, text='Spielzahl').pack()
        guess_frame = tk.Frame(frame)
        for i in xrange(1, 7):
            row, column = divmod(i - 1, 3)
            tk.Radiobutton(
                guess_frame, text=i, value=i, variable=self.guess_variable
            ).grid(row=row, column=column)
        guess_frame.pack()
        frame.pack()

        frame = tk.Frame(self)
        tk.Label(frame, text='Würfel').pack()
        dices_frame = tk.Frame(frame)
        dice_images = [
            tk.PhotoImage(file='w{0}.gif'.format(i)) for i in xrange(1, 7)
        ]
        self.dice_uis = list()
        for value in self.game.dice_values:
            dice = DiceUI(dices_frame, dice_images)
            dice.set_value(value)
            dice.pack(side=tk.LEFT)
            self.dice_uis.append(dice)
        dices_frame.pack()

        self.roll_dices_button = tk.Button(
            frame, text='Würfel werfen', command=self.do_roll
        )
        self.roll_dices_button.pack()
        frame.pack()

        self.update()

    def update(self):
        self.account_display['text'] = self.account.balance
        for dice_ui, value in zip(self.dice_uis, self.game.dice_values):
            dice_ui.set_value(value)

    def do_roll(self, _event=None):
        wager_title = 'Einsatz'
        try:
            wager = self.wager_variable.get()
        except ValueError:
            wager = 0
        if wager <= 0:
            showerror(
                wager_title,
                'Der Einsatz muss eine positive, ganze Zahl grösser als 0 sein.'
            )
        else:
            try:
                self.account.withdraw(wager)
            except ValueError:
                showerror(
                    wager_title,
                    'Der Einsatz darf nicht grösser als der Kontostand sein.'
                )
            else:
                guess = self.guess_variable.get()
                if not guess:
                    showerror('Spielzahl', 'Bitte eine Spielzahl wählen.')
                else:
                    # TODO Dice "animation".
                    self.account.deposit(self.game.play(guess, wager))
                    self.update()


def main():
    root = tk.Tk()
    root.title('Chuck-A-Luck')
    game_ui = ChuckALuckUI(root)
    game_ui.pack()
    root.mainloop()


if __name__ == '__main__':
    main()
Die GUI setzt Konto, Spielzahlauswahl, und Würfen untereinander. Nun überleg mal wieviel oder wenig Aufwand das machen würde diese Dinge im Fenster nebeneinander anzuordnen. Und vergleiche das mal mit dem Aufwand der bei `place()` anfällt wenn Du das bei Dir von nebeneinander auf untereinander ändern möchtest.
Antworten