Seite 1 von 2
Leidiges OOP
Verfasst: Donnerstag 2. Mai 2013, 18:29
von Tengel
Ja...diese sch... OOP steht im Raum wie ein riesiger Berg ....es gibt kein vorwärts und kein drumm herum -.-. ..... und es nimmt immer mehr die Motivation.
http://pastebin.com/Qvim3nUz
So - mir ist klar - das die Zuweisungen nicht schön sind - die brauch ich nur um zu testen ob die einzelnen Schritte bisher richtig laufen - am Ende werden die in eine extra Klasse gepackt.
Wie schaffe ich es das von Spieler1 und Spieler2 die "oberste" Karte genommen und mit der des anderen verglichen wird?
Re: Leidiges OOP
Verfasst: Donnerstag 2. Mai 2013, 19:02
von Tengel
Re: Leidiges OOP
Verfasst: Donnerstag 2. Mai 2013, 19:14
von BlackJack
@Tengel: Du solltest einen Schritt zurück gehen und `Spielvorbereitung` und `Start` weglassen. Das sind keine Klassen, denn sie haben keine Methoden. Also syntaktisch sind das natürlich schon Klassen und Methoden, aber sie machen keinen Sinn, weil die Methoden den Umstand das sie auf einer Klasse definiert sind, überhaupt nicht benutzen oder benötigen. Man hätte die auch ganz normal als Funktionen schreiben können, ohne den Code in den ”Methoden” ändern zu müssen. Also stellt sich die Frage warum Du die unnötigerweise in Klassen steckst.
`other` ist ein schlechter Name für die Karten solange `self` nicht auch von dem gleichen (Duck) Type ist.
Du greifst auch zu viel auf die Interna von Spielern und Karten zu. Das sollte mit *Methoden* auf diesen Typen passieren. Sonst sind die Daten nicht gekapselt, was ja ein Hauptgrund ist, Klassen zu verwenden. Wenn Du zum Beispiel von aussen immer auf das `karten`-Attribut vom `Stapel`-Objekt zugreifst, dann hättest Du stattdessen gleich eine Liste nehmen können. Die Funktion zum geben der Karten, könnte beispielsweise so aussehen:
Code: Alles auswählen
def gib_handkarten(stapel, spieler1, spieler2):
for _ in range(len(stapel) // 2):
spieler1.add(stapel.ziehe_karte())
spieler2.add(stapel.ziehe_karte())
Diese Funktion geht es weder etwas an wie der Stapel intern die Karten verwaltet, noch wie der Spieler intern seine Hand verwaltet, darum passiert das über Methoden auf diesen Datentypen. Der Stapel braucht also eine `__len__()`- und eine `ziehe_karte()`-Methode und der Spieler eine `add()`-Methode.
Man könnte die `gib_handkarten()` auch als Methode auf `Stapel` definieren.
Edit: Auf den `Stapel` würde ich auch das `mischen()` verlegen.
Re: Leidiges OOP
Verfasst: Donnerstag 2. Mai 2013, 19:26
von Tengel
Code: Alles auswählen
Du greifst auch zu viel auf die Interna von Spielern und Karten zu. Das sollte mit *Methoden* auf diesen Typen passieren. Sonst sind die Daten nicht gekapselt, was ja ein Hauptgrund ist, Klassen zu verwenden. Wenn Du zum Beispiel von aussen immer auf das `karten`-Attribut vom `Stapel`-Objekt zugreifst, dann hättest Du stattdessen gleich eine Liste nehmen können.
Sry - das versteh ich irgendwie nicht so ganz - ich greif auf den Stapel doch nur 1x zu beim Handkarten geben?
Oder bezieht sich das quasi nochmal darauf - das mischen und Startkarten geben eigentlich in Stapel gehört?
Re: Leidiges OOP
Verfasst: Donnerstag 2. Mai 2013, 19:41
von BlackJack
@Tengel: Du greifst dort nicht auf den Stapel zu sondern auf die `karten`-Liste *direkt*. Das heisst Code ausserhalb der `Stapel`-Methoden weiss, dass die Karten dort in einer Liste gehalten werden. Wenn man das jemals ändern möchte, dann muss man mehr als nur die Methoden des `Stapel`-Datentyps ändern. Genauso beim `Spieler` und seiner Hand. Vergleich doch mal den Inhalt der Schleife für das verteilen der Karten bei Dir und bei meinem Vorschlag: ``spieler.hand.append(stapel.karten.pop())`` vs. ``spieler.add(karten.ziehe_karte())``. Beim ersten wird auf `hand` und `karten` zugegriffen und das müssen dann auch wirklich Objekte sein, die `append()`- und `pop()`-Methoden haben, und beim zweiten müssen nur auf `spieler` und `karten` die entsprechenden Methoden existieren — völlig egal wie die Karten *innerhalb* der beiden Objekte verwaltet werden.
Re: Leidiges OOP
Verfasst: Donnerstag 2. Mai 2013, 23:24
von Tengel
Ich nehm mal an das ist falsch....
Das mit __add__ versteh ich nicht - ich will doch nicht das was addiert wird - sondern das er die Karte hinzufügt?
Unabhängig davon das meine Shell gefühlte 400 Fehler zeigt -...weil ja das Spieler Objekt kein add hat .....
Ich hab
Code: Alles auswählen
def __ad__(self, stapel):
self.hand = self.hand+ stapel
etc. etc.....
Es ist aussichtslos - ich scheine wirklich zu dumm für OOP zu sein.
Re: Leidiges OOP
Verfasst: Donnerstag 2. Mai 2013, 23:33
von Sirius3
Tengel hat geschrieben:
Ich nehm mal an das ist falsch....
Falsch! Das ist genau richtig!
Zum »add«: Du kannst natürlich auch den +-Operator definieren, verständlicher finde ich aber eine Methode »add«. Was soll die machen? Keinen »stapel« zu den Handkarten hinzufügen, sondern nur eine einzelne Karte:
Damit muß niemand außer die Klasse »Spieler« etwas darüber wissen, wie ein Spieler seine Karten speichert.
Das sieht doch alles schon ganz vielversprechend aus

Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 07:02
von snafu
@Tengel: Vielleicht kennzeichnest du das `karten`- bzw `hand`-Attribut einfach als "privat" (`self._karten` / `self._hand`) und zwingst dich dazu, dass niemand von außen direkt darauf zugreifen kann - auch wenn es durch die Freiheiten, die Python einem gibt, eben doch ginge. Und mit dieser Denkweise kommst du dann automatisch dazu, eine spezielle Methode für den Zugriff implementieren zu müssen.
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 10:06
von Tengel
Achso -....also gar nicht das __add__ nehmen -.-" kein wunder das es nicht ging.
Bzw. gehen tut es ja - aber da ich ja von BlackJack
Code: Alles auswählen
spieler1.add(stapel.ziehe_karte())
spieler2.add(stapel.ziehe_karte())
genommen hab ist es klar das er ein Fehler auswirft da __add__ nicht als Methode add herhalten kann.
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 11:30
von Tengel
So ich habe den Spieler mal um 3 Methoden erweitert.
Code: Alles auswählen
def gewinneKarte(self, KarteGegner):
print(self.hand[0],"von",self.name," gewinnt\n")
self.nachziehstapel.append(KarteGegner)
self.nachziehstapel.append(self.verliereKarte())
def verliereKarte(self):
return self.hand.pop(0)
def aktuellerKartenWert(self):
return self.hand[0].rang
Momentan bin ich bei
Code: Alles auswählen
def vergleicheWert(self, spieler1, spieler2):
while len(spieler1.hand) !=0:
print("{0:13} gegen {1:>13}".format(spieler1.hand[0],spieler2.hand[0]))
if spieler1.aktuellerKartenWert() > spieler2.aktuellerKartenWert():
spieler1.gewinneKarte(spieler2.verliereKarte())
elif spieler1.aktuellerKartenWert() == spieler2.aktuellerKartenWert():
print("unentschieden")
else:
spieler2.gewinneKarte(spieler1.verliereKarte())
Das steht momentan als Methode im Stapel ...ich bin mir aber nicht sicher ob das nicht eher in Spieler gehört?
Das würde auch das Problem beheben das ich ja in Stapel schon wieder auf - z. B. len(spieler1.hand) zugreife....
Unentschieden führt noch logischerweise noch zu einer Endlosschleife.
Sollte kein Unentschieden kommen - läuft es "soweit" richtig - was bedeutet die 3 neuen Spieler Methoden funktionieren.
Jetzt stellt sich die Frage wie ich am sinnvollsten hinbekomme das er wenn die .hand leer ist - versucht die Karten vom Nachziehstapel auf die Hand zu nehmen.
Momentan denke ich an
Code: Alles auswählen
while len(spieler1.hand) != 0:
while len(spieler1.hand) !=0:
## vergleiche bis Hand leer, dann Sprint in die äußere Schleife
try:
spieler1.hand.append(spieler1.nachziehstapel)
except:
print("Keine Karten mehr - leider verloren")
Oder mit einem if len(spieler1.hand)==0
Was ja wahr sein muss da er aus der inneren Schleife gesprungen ist
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 11:40
von /me
Tengel hat geschrieben:Jetzt stellt sich die Frage wie ich am sinnvollsten hinbekomme das er wenn die .hand leer ist - versucht die Karten vom Nachziehstapel auf die Hand zu nehmen.
Momentan denke ich an
Code: Alles auswählen
while len(spieler1.hand) != 0:
while len(spieler1.hand) !=0:
## vergleiche bis Hand leer, dann Sprint in die äußere Schleife
try:
spieler1.hand.append(spieler1.nachziehstapel)
except:
print("Keine Karten mehr - leider verloren")
Es ist nicht klar, was du genau zu erreichen versuchst, aber die zwei Schleifen sehen extrem komisch aus (mal abgesehen davon, dass nicht klar ist ob du den Inhalt der zweiten Schleife weggelassen oder die Einrückung vergessen hast). Was möchtest du genau? Sollen alle Karten vom Nachziehstapel gezogen werden wenn die Kartenhand des Spielers leer ist oder nur eine?
Das leere
except ist ein No-Go. Du solltest immer explizit angeben welche Art von Fehler du erwartest.
Statt
spieler1.hand.append() zu verwenden würde ich dem Spieler-Objekt eine
draw_card-Methode geben.
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 11:43
von Tengel
Wenn die Hand leer ist sollen - falls vorhanden - alle Karten vom Nachziehstapel nachgezogen werden.
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 11:52
von /me
Tengel hat geschrieben:Wenn die Hand leer ist sollen - falls vorhanden - alle Karten vom Nachziehstapel nachgezogen werden.
Also so etwas:
Code: Alles auswählen
if len(spieler1.hand) == 0:
while spieler1.nachziehstapel:
spieler1.hand.extend(spieler1.nachziehstapel)
if len(spieler1.hand) == 0:
print("Keine Karten mehr - leider verloren")
Das ist jetzt nur ein grober Entwurf (der in eine Endlosschleife und dann zu einem MemoryError führen wird), aber es ist ein Anfang.
Besser wäre meiner Meinung nach
Code: Alles auswählen
while spieler1.nachziehstapel:
spieler1.draw_card(spieler1.nachziehstapel.get_card())
Damit ist das Wissen über den genauen Aufbau der jeweiligen Kartensätze nicht mehr erforderlich. Zudem kannst du bei get_card auch dafür sorgen, dass die Karte wirklich vom Nachziehstapel entfernt wird. In der ersten Variante ist das noch gar nicht berücksichtigt (und daraus resultiert auch die angesprochene Endlosschleife).
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 11:59
von BlackJack
@Tengel: Das finde ich jetzt immer noch nicht klärend genug. Kannst Du vielleicht mal das Spiel an sich, ohne den Quelltext erklären? Also das grosse Ganze, das eigentliche Ziel was dort umgesetzt werden soll? Ich habe so ein bisschen den Verdacht, dass die Unterscheidung zwischen `hand` und `nachziehstapel` nicht nötig ist, und Du eigentlich nur *eine* Datenstruktur, und zwar eine `Queue.Queue` für die Karten beim Spieler haben möchtest. Das ``pop(0)`` ist von der Laufzeit her ungünstig. Da könnte man auch schon eine `Queue` verwenden, oder man nimmt die Karte nicht vom Anfang der Liste sondern vom Ende.
So etwas wie `spieler1.aktuellerKartenWert()` und dann der Vergleich mit dem Ergebnis vom `spieler2` der gleichen Methode ist IMHO auf den falschen Objekten gelöst. Wenn der Kartenwert ausschliesslich von den Daten einer Karte, ohne zusätzliche Regeln von aussen abhängt, also zum Beispiel das eine vom Spielverlauf abhängige Farbe alle anderen schlägt, dann würde ich die Karten selbst mit einer Implementierung von `Karte.__cmp__()` vergleichbar machen. Das hatte ich Dir in einem anderen Thema ja schon einmal als Quelltext gezeigt.
Was soll bei unentschieden passieren? Falls da die beiden Karten abgelegt werden und der nächste Spielzug entscheidet wer alle vier Karten bekommt, oder allgemeiner alle Karten bis ein Zug nicht mehr unentschieden ist, dann kann man das nicht mehr sinnvoll in `Spieler` lösen, dann braucht man etwas ausserhalb, Funktion(en) oder Klasse, um den Spielverlauf voran zu treiben.
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 12:08
von Tengel
Es handelt sich um ein sehr stupides Kartenspiel ....aus meiner Kindheit - woh erfunden von irgendwelchen Verwandten die uns ruhig stellen wollten :>
Sie nannten das "Maxl fangen"- hab dazu aber bei google nix gefunden.
Die Karten werden komplett ausgeteilt.
Jeder Spieler legt die oberste Karte offen auf den Tisch.
Die höchste Karte gewinnt.
Treffen Karten mit dem selben Wert aufeinander legt jeder Spieler eine Karte verdeckt auf die bereits offene Karte - gefolgt von einer weiteren offenen(und das halt im zweifel so lange bis einer gewinnt - der dann alle Karten bekommt - so kann man z. B. wenn man kein Ass mehr hat bei einem 6 vs 6 ein Ass bekommen wenn das verdeckt gelegt wird.
Hat man keine Karten mehr wird der Nachziehstapel gemischt und verwendet.
Es gewinnt der wo am Ende alle Karten hat -
Wie gesagt - sehr stupide - man kann es eigentlich nicht beeinflussen - außer evtl .man bescheißt beim mischen/austeilen.
Deshalb dachte ich auch das wär geeignet um OOP zu üben - da man ja nur das Gerüst/den Ablauf festlegen msus und dann von "außen" keinen Input mehr braucht.
Nachziehstapel -haben wir halt immer genutzt - auch um immer wieder mal zu mischen - weswegen ich ihn einbauen wollte - würde ich Dinge die mir Probleme bereiten weg lassen -.....wäre das wohl nicht Sinn u Zweck der Übung ...und es würd nicht viel übrig bleiben :>
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 12:09
von /me
BlackJack hat geschrieben:@Tengel: Das finde ich jetzt immer noch nicht klärend genug. Kannst Du vielleicht mal das Spiel an sich, ohne den Quelltext erklären? Also das grosse Ganze, das eigentliche Ziel was dort umgesetzt werden soll? Ich habe so ein bisschen den Verdacht, dass die Unterscheidung zwischen `hand` und `nachziehstapel` nicht nötig ist, und Du eigentlich nur *eine* Datenstruktur, und zwar eine `Queue.Queue` für die Karten beim Spieler haben möchtest.
Der Meinung kann ich mich nur anschließen. Ich arbeite gerade an einem Spiel in dem es Karten für Spieler gibt, einen offenen und einen verdeckten Nachzugstapel und den Ablagestapel. Alle diese Karten werden bei mir jeweils in einer Instanz des selbst definierten Typs
CardStack abgelegt. Dieser Kartenstapel kann alles was nötig ist um mit den Karten umzugehen.
Hier ist ein kurzer Überblick über einige der Methoden:
Code: Alles auswählen
class CardStack(list):
def __init__(self, description='cardstack'):
def __str__(self):
def append_card(self, card):
def append_card_from(self, other_cardstack):
def count_cards_by_type(self):
def get_card(self, card_type=None):
def get_random_card(self):
def shuffle(self):
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 12:34
von BlackJack
@Tengel: Klingt nach einer Variante von „Leben und Tod”, ak.a. „Battaille Royale”, a.k.a. „Battle”, a.k.a. „War”.
Wenn der Nachziehstapel gemischt werden soll, dann macht es in der Tat Sinn den extra zu verwalten. Einen mischbaren Kartenstapel solltest Du mit der `Stapel`-Klasse ja schon haben. Ich weiss nicht wie Dein aktueller Quelltext aussieht, aber zu dem was Du gezeigt hattest, sagte ich ja schon mal, dass das erzeugen eines kompletten Blattes dort in der `__init__()` ungünstig ist. Denn hier musst Du ja mit einem leeren Stapel anfangen, zu dem dann immer die gewonnenen Karten hinzugefügt werden. Und die Hand ist im Grunde *auch* ein `Stapel` und keine Liste, denn Du willst ja genau wie beim Stapel beim Verteilen der Karten auf die Spieler immer eine Karte vom Stapel ziehen. Das heisst so eine Methode muss es da schon geben.
Wie schon gesagt: Wenn ein Spielzug unentschieden ist, dann müssen Karten auf „dem Tisch” abgelegt werden, bis es einen Zug mit einem Gewinner gibt, also muss das ausserhalb der Spieler-Objekte passieren, denn man muss sich die Karten irgendwo merken.
Das mit dem Nachziehstapel übernehmen wenn die Hand leer ist, scheint mir wieder an der falschen Stelle zu passieren. Das ist etwas Internes beim Spieler. Der braucht eine Methode mit der man eine Karte von ihm anfordern kann. Die nimmt er von der Hand, oder wenn die leer ist, wird der Nachziehstapel gemischt und zur Hand und der Nachziestapel wird mit einem leeren Stapel initialisiert. Der Test am Anfang der Methode gemacht werden, um Quelltextwiederholung zu vermeiden. Wenn beides leer ist, könnte man eine Ausnahme auslösen. Beziehungsweise den `Stapel` eine auslösen lassen, wenn versucht wird von einem leeren Stapel eine Karte zu ziehen.
Diese beiden ``while``-Schleifen können so nicht funktionieren. Denn die äussere Bedingung bezieht sich ja nur auf `spieler1`. Aber es kann ja auch passieren, dass `spieler2` keine Karten mehr auf der Hand und/oder dem Nachziehstapel hat. Und das innere ``while`` verstehe ich nicht‽
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 12:41
von Tengel
http://pastebin.com/T7xv6rG2
Wie gesagt - das unter den Definitionen dient momentan nur zur Überprüfung und wird am Ende noch geändert.
vergleicheWert ist noch unfertig
ich frag mich auch ob es nicht zu unübersichtlich ist - und ob vergleicheWert nicht einfach je nachdem -1, 0, oder 1 zurückliefern sollte und dann in einer anderen Methode weiter behandelt werden.
Ansich müsste man auch die add Methode so ändern/nutzen können das man damit Karten vom Stapel, Nachziehstapel oder anderen Spieler "ziehen"-> holen könnte.
Wobei ich mir dann nicht sicher bin wie ich die "Verwaltung" hinbekomme wen man eine Karte gewinnt.
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 13:16
von BlackJack
@Tengel: Die `vergleicheWert()` gehört nicht auf den `Stapel`-Typ. In der Form nicht einmal in eine andere Klasse, denn es ist semantisch eine Funktion und keine Methode. Immer wenn das `self`-Argument überhaupt nicht benötigt wird, oder nur benötigt wird um andere Methoden aufzurufen die das `self` aus eben dem selben Grund benötigen, dann muss man sich ernsthaft fragen warum man das als Methode in eine Klasse gesteckt hat.
In `Spieler` wird in `gewinneKarte()` etwas gelöst was IMHO aussehalb gemacht werden sollte. Eine Funktion (oder Methode auf einem `Battle`-Typ zum Beispiel) die von den Spielern Karten abfragt die der jeweilige Spiele ausspielt, die „auf dem Tisch” ablegt, den Gewinner ermittelt und dann alle Karten auf dem Tisch dem Gewinner hinzufügt, oder nichts tut.
Die Logik mit dem mischen und verwenden des Nachziehstapels bei Bedarf kann man in die Methode packen, welche die ausgepielte Karte abfragt. Und dann kann man die `Spieler.add()`-Methode auch die Karten dem Nachziehstapel hinzufügen lassen. Das die beim ersten Ausspielen einer Karte dann vom Nachziehstapel auf die Hand wandern, ergibt sich ja automatisch.
Re: Leidiges OOP
Verfasst: Freitag 3. Mai 2013, 13:30
von Tengel
Und der "Tisch" hat dann welche Struktur?
Ich muss dort Listenelemente ablegen können - also auch eine Liste?