Leidiges OOP
-
BlackJack
@Tengel: Du musst dort *Karten* ablegen können. Ob die vorher mal Elemente einer Liste, oder einer anderen Datenstruktur waren, ist doch egal. An der Stelle macht eine Liste Sinn, weil man soweit ich das sehe keine zusätzliche Funktionalität benötigt.
-
BlackJack
@Tengel: Richtig das würde nicht in `Spieler` gehören.
Man kann die Karten auch generell „auf den Tisch legen”, aber beim unentschieden ist es zwingend nötig, weil man sich die Karten dann über einen oder sogar mehrere Spielzüge hinweg merken muss. Ob das dann in einer Funktion, oder mit einer Klasse für das Spiel gemacht wird, hängt davon ab ob die Spielzüge als einzelne Schritte ausgeführt werden können, oder ob der gesamte Spielverlauf nur als Ganzes in einer Funktion abgewickelt wird. Schritte sind flexibler, insbesondere wenn man da später mal eine GUI drauf setzen möchte. Denn dann hat man die Hauptschleife, die so ein Spiel antreibt, in der Regel nicht mehr im eigenen Code, sondern muss sich von der GUI-Hauptschleife in einzelnen Schritten aufrufen lassen.
Man kann die Karten auch generell „auf den Tisch legen”, aber beim unentschieden ist es zwingend nötig, weil man sich die Karten dann über einen oder sogar mehrere Spielzüge hinweg merken muss. Ob das dann in einer Funktion, oder mit einer Klasse für das Spiel gemacht wird, hängt davon ab ob die Spielzüge als einzelne Schritte ausgeführt werden können, oder ob der gesamte Spielverlauf nur als Ganzes in einer Funktion abgewickelt wird. Schritte sind flexibler, insbesondere wenn man da später mal eine GUI drauf setzen möchte. Denn dann hat man die Hauptschleife, die so ein Spiel antreibt, in der Regel nicht mehr im eigenen Code, sondern muss sich von der GUI-Hauptschleife in einzelnen Schritten aufrufen lassen.
-
BlackJack
Mal als Beispiel eine Implementierung wo das Spiel komplett durch die `main()`-Funktion getrieben wird:
Das Spiel funktioniert für zwei oder mehr Spieler. Die `main()` ist ziemlich umfangreich, da könnte man sicher ansetzen und die auf mehr Funktionen aufteilen, oder wie gesagt, eine Klasse für das Spiel insgesamt einführen.
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from random import shuffle
class Deck(object):
def __init__(self, cards=()):
self.cards = list(cards)
def __len__(self):
return len(self.cards)
def __getitem__(self, index):
return self.cards[index]
def __delitem__(self, index):
del self.cards[index]
def add_many(self, cards):
self.cards.extend(cards)
def shuffle(self):
shuffle(self.cards)
def draw_card(self):
return self.cards.pop()
def deal_cards(self, players):
count = len(self) // len(players)
if not count:
raise ValueError('more players than cards')
for player in players:
player.add_many(self[-count:])
del self[-count:]
class Card(object):
RANK_NAMES = map(str, xrange(1, 11)) + ['Jack', 'Queen', 'King', 'Ace']
SUITE_NAMES = ['diamonds', 'hearts', 'spades', 'clubs']
def __init__(self, rank, suite):
if not (
0 <= rank < len(self.RANK_NAMES)
and 0 <= suite < len(self.SUITE_NAMES)
):
raise ValueError('suite or rank out of bounds')
self.rank = rank
self.suite = suite
def __str__(self):
return '{0} of {1}'.format(
self.RANK_NAMES[self.rank], self.SUITE_NAMES[self.suite]
)
def __cmp__(self, other):
return cmp(self.rank, other.rank)
def __hash__(self):
raise TypeError('unhashable type: {0}'.format(type(self)))
@classmethod
def create_deck(cls):
return Deck(
cls(r, s)
for r in xrange(len(cls.RANK_NAMES))
for s in xrange(len(cls.SUITE_NAMES))
)
class Player(object):
def __init__(self, name):
self.name = name
self.hand = Deck()
self.received = Deck()
def __str__(self):
return '{0} ({1} cards)'.format(self.name, len(self))
def __len__(self):
return len(self.hand) + len(self.received)
def has_cards(self):
return bool(self)
def add_many(self, cards):
self.received.add_many(cards)
def get_card(self):
if not self.hand:
self.received.shuffle()
self.hand, self.received = self.received, Deck()
return self.hand.draw_card()
def main():
players = map(Player, ['Mary', 'Paul'])
deck = Card.create_deck()
deck.shuffle()
deck.deal_cards(players)
cards_on_table = list()
while len(players) > 1:
print
ranking = sorted(
((player.get_card(), player) for player in players), reverse=True
)
for card, player in ranking:
print '{0} is playing {1}.'.format(player, card)
cards_on_table.extend(card for card, _ in ranking)
best_card, best_player = ranking[0]
second_best_card, _ = ranking[1]
if best_card != second_best_card:
print '{0} is winning.'.format(best_player)
best_player.add_many(cards_on_table)
cards_on_table = list()
else:
print 'No one is winning.'
players = [player for player in players if player.has_cards()]
print '-' * 30
print 'Winner:', players[0]
if __name__ == '__main__':
main()-
BlackJack
@Tengel: Der Name `__name__` ist der Name des Moduls, *ausser* wenn das Modul nicht importiert, sondern als Programm gestartet wurd, dann ist `__name__` an den Wert '__main__' gebunden. Man kann Module mit diesem ``if`` also entweder importieren, dann passiert nichts weiter, und man kann die Datentypen in dem Modul verwenden und die Funktionen aufrufen, oder man startet das Modul als Programm, dann wird die `main()`-Funktion ausgeführt.
Das kann beispielsweise nützlich sein wenn man bei einem Programm die Klassen und Funktionen von einem anderen Modul aus verwenden möchte, oder um bei Modulen die selbst keine Programme sind, eine paar ausführbare Tests unterzubringen, die ausgeführt werden wenn man das Modul als Programm ausführt.
Das kann beispielsweise nützlich sein wenn man bei einem Programm die Klassen und Funktionen von einem anderen Modul aus verwenden möchte, oder um bei Modulen die selbst keine Programme sind, eine paar ausführbare Tests unterzubringen, die ausgeführt werden wenn man das Modul als Programm ausführt.
Code: Alles auswählen
def Spielfeld():
spielfeld = []
karteSpieler1 = spieler1.aktuellerKartenWert()
karteSpieler2 = spieler2.aktuellerKartenWert()
if karteSpieler1 > karteSpieler2:
spieler1.gewinneKarte(spieler2.verliereKarte())
elif karteSpieler1 < karteSpieler2:
spieler2.gewinneKarte(spieler1.verliereKarte())
else:
spielfeld.append(spieler1.verliereKarte())
spielfeld.append(spieler2.verliereKarte())
if len(spieler1.hand) == 0:
try:
spieler1.hand.extend(spieler1.nachziehstapel)
random.shuffle(spieler1.hand)
del spieler1.nachziehstapel[0:-1]
except:
print("Keine Karten mehr - ",spieler2," gewinnt")
if len(spieler2.hand) == 0:
try:
spieler2.hand.extend(spieler2.nachziehstapel)
random.shuffle(spieler2.hand)
del spieler2.nachziehstapel[0:-1]
except:
print("Keine Karten mehr - ",spieler1," gewinnt")
ewig lang - ewig verschachtelt und funktioniert nicht(unabhängig davon das momentan bei gleichem Wert die Karten einfach "verschwinden")
Aufgerufen durch
Code: Alles auswählen
while len(spieler1.hand) != 0 or len(spieler2.hand) != 0:
Aber die Ausgaben ergeben überhaupt keinen Sinn?!
Code: Alles auswählen
Eichel-7 gegen Eichel-7
Eichel-7 gegen Eichel-7
Schellen-Ober gegen Eichel-7
Schellen-Ober von Heinz gewinnt
Schellen-10 gegen Eichel-7
Schellen-10 von Heinz gewinnt
Eichel-7 gegen Eichel-7
Eichel-7 gegen Eichel-7
Eichel-7 gegen Eichel-7
Eichel-7 gegen Eichel-7
Blatt-Unter gegen Eichel-7
Blatt-Unter von Heinz gewinnt
Eichel-7 gegen Eichel-7
Eichel-7 gegen Eichel-7
Eichel-7 gegen Eichel-7
Schellen-9 gegen Eichel-7
Schellen-9 von Heinz gewinnt
unabhängig davon das Eichel 7 vs Eichel 7 nicht möglich sein sollte
sollte nach 7 gegen 7 das ja im "Spielfeld" "verschinden" - da kein Rückgabewert...
Ich weiß...BlackJack du hast dir ewig mühe gegeben - und ich schäme mich auch...ich kriegs nicht hin -.-"....
Aber einfach copy und paste auf einen Code ist auch fail
Achja - das
try - except ist auch Schwachsinn - ..da es ja kein Fehler gibt wenn man eine leere Liste an eine leere Liste hängen will.
Code: Alles auswählen
karteSpieler1 = spieler1.aktuellerKartenWert()
karteSpieler2 = spieler2.aktuellerKartenWert()if spieler1.aktuellerKartenWert() ...
nehmen kann
Schau Dir mal an, was passiert, wenn ein Spieler Karten von seinem Nachziehstapel holt.
Vor allem, wie der Nachziehstapel danach aussieht
Dieses Nachziehen sollte übrigens eine Methode der Spieler-Klasse sein.
Vor allem, wie der Nachziehstapel danach aussieht
Dieses Nachziehen sollte übrigens eine Methode der Spieler-Klasse sein.
Da Du den Kartenwert öfter benutzt, ist es sinnvoll, diesen Wert nur einmal zu berechnen.Tengel hat geschrieben:Hm das ist auch überflüssig
-
BlackJack
Die Kartenverdopplung kommt daher, dass Du den Nachziehstapel nicht komplett leerst:
Da die letzte Karte sowohl zur Hand hinzugefügt, aber immer noch im Nachziehstapel steckt, gibt es sie nun mehrfach. Das passiert jedes mal wenn die Hand leer ist, aber der Nachziehstapel nicht. Und da der, sowie er mindestens eine Karte enthält niemals leer wird, geht diese Kartenverdoppelung munter weiter.
Ich würde mich an der Stelle von der Idee lösen für die beiden Attribute unbedingt die Objekte zu verändern. Also statt alle Elemente aus dem Nachziehstapen in die auf jeden Fall leere Hand zu kopieren, und dann alle Elemente aus dem Nachziehstapel zu entfernen, könnte man auch einfach den Nachziehstapel an die Hand binden und dann den Nachziehstapel an eine leere Liste.
Falls das Programm doch abbrechen sollte, liegt das an der falschen ``while``-Bedingung. Die Abbruchbedingung des Spiels ist ja nicht ob keiner mehr etwas auf der Hand hat, sondern dass ein Spieler gar keine Karten mehr hat — weder auf der Hand, noch auf dem Nachziehstapel.
Die Funktion löst das unentschieden-Problem nicht, weil `spielfeld` eine lokale Liste ist, die nach Abarbeitung der Funktion verschwindet. Das ist ja aber etwas was man sich über mehrere Spielzüge hinweg merken muss. Also wenn man es als Funktion lösen möchte, müsste man es als Argument übergeben und am Ende auch wieder an den Aufrufer zurück geben, damit eventuell dort abgelegte Karten im nächsten Zug auch jemandem zugesprochen werden können.
Der Funktionsname ist eher ein Name für eine Klasse. Gegen PEP8 verstossen finde ich an sich ja schon nicht schön, aber Funktionsnamen mit Grossbuchstaben anfangen zu lassen, ist wirklich keine gute Idee. Insbesondere wenn der Name dann auch noch von der Bedeutung nach einem Klassennamen aussieht.
Ich würde die ausgespielten Karten grundsätzlich an diesen Namen binden und `Spieler.aktuellerKartenWert()` und `Spieler.gewinneKarte()` weg lassen. Dann bekommt der Gewinner sofern kein Unentschieden ist die Karten vom Tisch. Damit braucht man an der Stelle keine Sonderbehandlung mehr ob es sich um den aktuellen Stich oder Karten von vorherigen Unentschieden-Zügen handelt.
Code: Alles auswählen
In [19]: xs = range(10)
In [20]: del xs[0:-1]
In [21]: xs
Out[21]: [9]Ich würde mich an der Stelle von der Idee lösen für die beiden Attribute unbedingt die Objekte zu verändern. Also statt alle Elemente aus dem Nachziehstapen in die auf jeden Fall leere Hand zu kopieren, und dann alle Elemente aus dem Nachziehstapel zu entfernen, könnte man auch einfach den Nachziehstapel an die Hand binden und dann den Nachziehstapel an eine leere Liste.
Code: Alles auswählen
if len(spieler1.hand) == 0:
spieler1.hand = spieler1.nachziehstapel
random.shuffle(spieler1.hand)
spieler1.nachziehstapel = list()Die Funktion löst das unentschieden-Problem nicht, weil `spielfeld` eine lokale Liste ist, die nach Abarbeitung der Funktion verschwindet. Das ist ja aber etwas was man sich über mehrere Spielzüge hinweg merken muss. Also wenn man es als Funktion lösen möchte, müsste man es als Argument übergeben und am Ende auch wieder an den Aufrufer zurück geben, damit eventuell dort abgelegte Karten im nächsten Zug auch jemandem zugesprochen werden können.
Der Funktionsname ist eher ein Name für eine Klasse. Gegen PEP8 verstossen finde ich an sich ja schon nicht schön, aber Funktionsnamen mit Grossbuchstaben anfangen zu lassen, ist wirklich keine gute Idee. Insbesondere wenn der Name dann auch noch von der Bedeutung nach einem Klassennamen aussieht.
Ich würde die ausgespielten Karten grundsätzlich an diesen Namen binden und `Spieler.aktuellerKartenWert()` und `Spieler.gewinneKarte()` weg lassen. Dann bekommt der Gewinner sofern kein Unentschieden ist die Karten vom Tisch. Damit braucht man an der Stelle keine Sonderbehandlung mehr ob es sich um den aktuellen Stich oder Karten von vorherigen Unentschieden-Zügen handelt.
Also im Sinne von jede Runde legen beide Spieler die Karten auf den Tisch - der Gewinner bekommt dann was am Tisch liegt.Ich würde die ausgespielten Karten grundsätzlich an diesen Namen binden und `Spieler.aktuellerKartenWert()` und `Spieler.gewinneKarte()` weg lassen. Dann bekommt der Gewinner sofern kein Unentschieden ist die Karten vom Tisch. Damit braucht man an der Stelle keine Sonderbehandlung mehr ob es sich um den aktuellen Stich oder Karten von vorherigen Unentschieden-Zügen handelt.
Dh. ich nutze je Spieler verliereKarte um die Karten auf den "Tisch" zu bekommen.
Aber wie komm ich dann an den Kartenwert?
Desweitern - wie verhält sich das in einer Schleife wenn ein Stechen ist?
Rekursiver Aufruf?
Der erst eine leere tisch Liste übergeben bekommt und wenn er sich selbst aufruft die aktuelle tisch Liste?
Hm ...an diese Stelle ..auch wenns nicht zum Thema gehört ...muss ich mal dumm fragen...
Ihr habt ja sicher schon mehrere "quereinsteiger Anfänger" hier gehabt ...kann es sein das ich mich da verhältnissmäßig ...doch recht anstell, ich mein ...ich bin da jetzt seit 1,5 Monaten dran :*(?
-
BlackJack
@Tengel: Du kannst die beiden Karten doch an Namen binden, wie bisher, *und* zu der Liste für den Tisch hinzufügen. Dann vergleichst Du die Karten und der Gewinner bekommt den Tischinhalt.
Das mit dem Stechen hatte ich doch schon erklärt: Die Funktion muss nur den Tisch als Argument entgegennehmen und auch als Rückgabewert zurückgeben. Und schon kann man sich über Aufrufe hinweg den Inhalt des Tischs merken. Rekursion sollte man sich für tatsächlich rekursive Problemstellungen aufheben.
Dabei fällt mir gerade auf: Woher kennt `Spielzug()` eigentlich `spieler1` und `spieler2`? Die sollten auch als Argumente in die Funktion kommen. Die Funktion für einen Spielzug könnte so aussehen:
Das mit dem Nachziehstapel wenn die Hand leer ist, gehört in die `get_card()`-Methode
Das mit dem Stechen hatte ich doch schon erklärt: Die Funktion muss nur den Tisch als Argument entgegennehmen und auch als Rückgabewert zurückgeben. Und schon kann man sich über Aufrufe hinweg den Inhalt des Tischs merken. Rekursion sollte man sich für tatsächlich rekursive Problemstellungen aufheben.
Dabei fällt mir gerade auf: Woher kennt `Spielzug()` eigentlich `spieler1` und `spieler2`? Die sollten auch als Argumente in die Funktion kommen. Die Funktion für einen Spielzug könnte so aussehen:
Code: Alles auswählen
def make_move(cards_on_table, player_a, player_b):
card_a = player_a.get_card()
card_b = player_b.get_card()
cards_on_table.extend([card_a, card_b])
if card_a > card_b:
winner = player_a
elif card_a < card_b:
winner = player_b
else:
winner = None
if winner is not None:
winner.add_many(cards_on_table)
cards_on_table = list()
return cards_on_table