Seite 2 von 2

Re: Leidiges OOP

Verfasst: Freitag 3. Mai 2013, 13:34
von 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.

Re: Leidiges OOP

Verfasst: Freitag 3. Mai 2013, 13:37
von Tengel
Das sammelt ja was vom Spieler "abgelegt" wird - aber in spieler macht das glaub ich nicht viel Sinn - also eher eine Funktion ausserhalb von Klassen?

Und das soll nur auf den Tisch abgelegt werden bei unentschieden?

Re: Leidiges OOP

Verfasst: Freitag 3. Mai 2013, 15:00
von 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.

Re: Leidiges OOP

Verfasst: Freitag 3. Mai 2013, 21:58
von BlackJack
Mal als Beispiel eine Implementierung wo das Spiel komplett durch die `main()`-Funktion getrieben wird:

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()
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.

Re: Leidiges OOP

Verfasst: Freitag 3. Mai 2013, 22:06
von Tengel
WoW danke :)

if __name__ is main:
main()


Ich frag mich ehrlich gesagt schon immer warum man das so schreibt - in meinen beiden Büchern wurde das auch nicht einmal so genutzt.
wieso reicht kein einaches
main()?

Re: Leidiges OOP

Verfasst: Freitag 3. Mai 2013, 22:24
von 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.

Re: Leidiges OOP

Verfasst: Samstag 4. Mai 2013, 14:56
von Tengel

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")



Hmmm :(
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
Wieso gibt es Eichel 7 plötzlich 2x ...... wieso wird Eichel 7 4x verwendet -
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.

Re: Leidiges OOP

Verfasst: Samstag 4. Mai 2013, 15:16
von Tengel

Code: Alles auswählen

 karteSpieler1 = spieler1.aktuellerKartenWert()
 karteSpieler2 = spieler2.aktuellerKartenWert()
Hm das ist auch überflüssig da man ja einfach
if spieler1.aktuellerKartenWert() ...
nehmen kann :(

Re: Leidiges OOP

Verfasst: Samstag 4. Mai 2013, 15:20
von Sirius3
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.
Tengel hat geschrieben:Hm das ist auch überflüssig
Da Du den Kartenwert öfter benutzt, ist es sinnvoll, diesen Wert nur einmal zu berechnen.

Re: Leidiges OOP

Verfasst: Samstag 4. Mai 2013, 15:29
von BlackJack
Die Kartenverdopplung kommt daher, dass Du den Nachziehstapel nicht komplett leerst:

Code: Alles auswählen

In [19]: xs = range(10)

In [20]: del xs[0:-1]

In [21]: xs
Out[21]: [9]
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.

Code: Alles auswählen

        if len(spieler1.hand) == 0:
            spieler1.hand = spieler1.nachziehstapel
            random.shuffle(spieler1.hand)
            spieler1.nachziehstapel = list()
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.

Re: Leidiges OOP

Verfasst: Samstag 4. Mai 2013, 15:56
von Tengel
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.
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 :*(?

Re: Leidiges OOP

Verfasst: Samstag 4. Mai 2013, 16:24
von 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:

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
Das mit dem Nachziehstapel wenn die Hand leer ist, gehört in die `get_card()`-Methode