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!!!
Problem mit einfachem Kartenspiel Programm
-
- 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__
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.
__str__ ruft man nicht direkt auf, sondern über str(karte). Konstanten schreibt man per Konvention groß: FARBEN, WERTE, SYMBOLE.
- __blackjack__
- User
- Beiträge: 13100
- 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:
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):
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()
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