Problem mit einfachem Kartenspiel Programm

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
MalteBogner
User
Beiträge: 1
Registriert: Freitag 11. Januar 2019, 16:39

Hey,

ich bin ein Neuling mit Python und versuche gerade ein Kartenspiel nachzuprogrammieren. An einer Stelle komme ich gerade nicht weiter:

Ich habe zwei Klassen, eine für die Karten und eine für das Deck:

---------------------------------
import random

farben = ("Herz", "Karo", "Pik", "Kreuz")
symbole = ("Zwei", "Drei", "Vier", "Fünf", "Sechs", "Sieben", "Acht", "Neun", "Zehn", "Bube", "Dame", "König", "Ass")
werte= {"Zwei":2, "Drei":3, "Vier":4, "Fünf":5, "Sechs":6, "Sieben":7, "Acht":8, "Neun":9, "Zehn":10, "Bube":10, "Dame":10, "König":10, "Ass":11}


class Karte:

def __init__(self, farbe, symbol):
self.farbe = farbe
self.symbol = symbol

def __str__(self):
return self.farbe + "-" + self.symbol


class Deck:

def __init__(self):
self.deck = []
for farbe in farben:
for symbol in symbole:
self.deck.append(Karte(farbe,symbol))

def shuffle(self):
random.shuffle(self.deck)

def dealen(self):
karte = self.deck.pop()
return karte

def __str__(self):
deck = ""
for karte in self.deck:
deck += "\n" + karte.__str__()
return deck

-------------------------------------------------

Jetzt würde ich später gerne überprüfen, ob sich eine bestimmte Karte in self.deck befindet. Etwa in dieser Form:
------------------------------------------------
neues_deck = Deck()
def kartenwahl():
neue_karte = input("Welche Karte wählst du?")
if neue_karte in neues_deck.deck:
print("Diese Karte gibt es")
else:
print("Diese Karte gibt es NICHT")

kartenwahl()

--------------------------------------------
Leider funktioniert das einfach nicht und es wird immer das else statement gedruckt...

Kann mir jemand helfen?

Danke!!!
ArtooDetoo
User
Beiträge: 60
Registriert: Dienstag 4. Dezember 2018, 16:57

Am schönsten wäre es vermutlich, wenn du für die Deck-Klasse den Membership-Operator implementierst: https://docs.python.org/3/reference/dat ... __contains__
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Die magische Funktion für den in-Operator ist __contains__. zudem ist neue_karte eine String, zum Vergleich brauchst Du aber ein Karten-Objekt. Dieses braucht auch noch die magische Funktion __eq__ für den Gleichheitsvergleich und vielleicht auch noch eine from_string-Klassenmethode, um ein entsprechendes Objekt aus einem String zu erzeugen.

__str__ ruft man nicht direkt auf, sondern über str(karte). Konstanten schreibt man per Konvention groß: FARBEN, WERTE, SYMBOLE.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wenn man `__eq__()` implementiert sollte man auch gleich `__ne__()` und `__hash__()` implementieren. Entweder so dass die `__hash__()`-Methode eine `TypeError`-Ausnahme auslöst oder eben sinnvoll. Da Kartenobjekte eher unveränderlich sein dürften, macht es sinn die richtig zu implementieren. Dann kann man Karten auch als Schlüssel in Wörterbüchern oder als Elemente in Mengen verwenden.

Bei dem `Deck` muss man nicht zwingend `__contains__()` implementieren – das entsprechende Verhalten bekommt man auch frei Haus wenn das `Deck` iterierbar wäre, was allgemein für diese Typ Sinn machen würde. Ich würde da also nicht `__contains__()` implementieren, sondern `__iter__()`.

`FARBEN` und `SYMBOLE` sind eher Listen als Tupel. Bei Tupeln haben in der Regel die einzelnen Positionen eine Bedeutung, bei Listen hat jede Position die gleiche Bedeutung.

Die Mischung von Deutsch und Englisch ist unschön und ich sehe auch nicht nach welchem Muster das getrennt wurde. Es macht sinn bei Englisch zu bleiben, weil sowohl die Programmiersprache selbst, als auch so ziemlich alle Bibliotheken Englisch verwenden.

Die Reihenfolge `neues_deck` zuweisen, Funktion definieren, Funktion benutzen wird sehr schnell unübersichtlich. Das Hauptprogramm sollte nicht auf Modulebene stehen und schon gar nicht durch Funktions- oder Klassendefinitionen unterbrochen werden. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Dann hat man das ”Problem” das `kartenwahl()` auf `neues_deck` zugreift, das aber nicht als globaler Name auf Modulebene existiert wenn das Hauptprogramm in einer Funktion steckt. ”Problem” in Anführungsstrichen, weil eine Funktion so etwas sowieso nicht machen sollte: Alles was eine Funktion oder Methode ausser Konstanten benötigt, sollte sie als Argument(e) übergeben bekommen.

`neues_deck` ist unnötig spezifisch. Solange es nicht auch `altes_deck` oder so etwas gibt, ist der Zusatz `neues_` überflüssig.

Der Attributname `deck` ist falsch. Das ist ja Kompisition an der Stelle und ein Deck besteht nicht aus einem Deck, ein Deck besteht aus Karten.

Zum erstellen würde sich eine „list comprehension“ anbieten.

Zeichenketten wiederholt in einer Schleife mit ``+``/``+=`` erweitern ist ineffizient. Dafür gibt es die `join()`-Methode auf Zeichenketten. Die `Deck.__str__()` schrumpft damit auf einen Einzeiler zusammen.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import random

FARBEN = ['Herz', 'Karo', 'Pik', 'Kreuz']
SYMBOLE = [
    'Zwei', 'Drei', 'Vier', 'Fünf', 'Sechs', 'Sieben',
    'Acht', 'Neun', 'Zehn', 'Bube', 'Dame', 'König', 'Ass',
]
WERTE = {
    'Zwei': 2,
    'Drei': 3,
    'Vier': 4,
    'Fünf': 5,
    'Sechs': 6,
    'Sieben': 7,
    'Acht': 8,
    'Neun': 9,
    'Zehn': 10,
    'Bube': 10,
    'Dame': 10,
    'König': 10,
    'Ass': 11,
}
assert WERTE.keys() == set(SYMBOLE)


class Karte:

    def __init__(self, farbe, symbol):
        self.farbe = farbe
        self.symbol = symbol

    def __eq__(self, other):
        return self.as_tuple() == other.as_tuple()
    
    def __ne__(self, other):
        return not self == other
    
    def __hash__(self):
        return hash(self.as_tuple())

    def __str__(self):
        return f'{self.farbe}-{self.symbol}'

    def as_tuple(self):
        return (self.farbe, self.symbol)


class Deck:

    def __init__(self):
        self.karten = [
            Karte(farbe, symbol) for symbol in SYMBOLE for farbe in FARBEN
        ]

    def __iter__(self):
        return iter(self.karten)

    def __str__(self):
        return '\n'.join(map(str, self))

    def shuffle(self):
        random.shuffle(self.karten)

    def dealen(self):
        return self.karten.pop()


def kartenwahl(deck):
    karten_name = input('Welche Karte wählst du? ')
    if karten_name in map(str, deck):
        print('Diese Karte gibt es.')
    else:
        print('Diese Karte gibt es NICHT!')


def main():
    deck = Deck()
    kartenwahl(deck)


if __name__ == '__main__':
    main()
Für Farben und Symbole würde ich noch einen Blick in das `enum`-Modul werfen, statt da einfach Zeichenkettenkonstanten zu verwenden.

Und das externe `attr`-Modul mag ich sehr gerne. Damit schrumpft die Kartenklasse deutlich zusammen (und eine gute `repr()`-Darstellung gibt's auch noch dazu):

Code: Alles auswählen

@attrs(frozen=True)
class Karte:
    
    farbe = attrib()
    symbol = attrib()
    
    def __str__(self):
        return f'{self.farbe}-{self.symbol}'
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten