from itertools import chain from . import Card class PlayingCard(Card): # internal class attributes for a playing card including valid suits and ranks suits = {"SPADES": "BLACK", "CLUBS": "BLACK", "HEARTS": "RED", "DIAMONDS": "RED"} ranks = {rank[0].upper() if not rank.isnumeric() else rank: rank.upper() for rank in ["ace"]+[str(i) for i in range(2, 11)] + ["jack", "queen", "king"]} # assign an absolute numeric value to each rank for easy compairison. Ace is 0 to make it high/low _values = {rank: value for value, rank in enumerate(ranks.keys())} def __init__(self, rank, suit): # do some validation of rank and suit if not isinstance(rank, str): try: if str(rank).isnumeric(): rank = str(rank) else: raise ValueError except ValueError: raise ValueError(f"Rank should be a string") if not isinstance(suit, str): raise TypeError("Suit should be a string.") if not suit.lower().strip() in self.suits.keys(): raise ValueError( f"'{suit.capitalize()}' is not a valid suit in a standard playing card deck") if not rank.upper() in chain(self.ranks.keys(), self.ranks.values()): raise IndexError( f"'{rank.capitalize()}' is out of the range of valid playing card ranks") # initialize the class and store its color super().__init__(rank.upper(), suit.upper()) self._color = self.suits[suit] # define __lt__ and __eq__ (< and =) as required by abstract Card class to take advantage of total ordering of Card def __eq__(self, other): if not isinstance(other, PlayingCard) or issubclass(other, PlayingCard): raise NotImplementedError(f"Comparison of {type(self).__name__} to {type(other).__name__} is not supported") return self._values[self.rank] == self._values[other.rank] def __lt__(self, other): if not isinstance(other, PlayingCard) or issubclass(other, PlayingCard): raise NotImplementedError(f"Comparison of {type(self).__name__} to {type(other).__name__} is not supported") # since Ace is 0, it will always pass one of these two conditions so it is always less than return (self._values[self.rank] < self._values[other.rank]) or (-1 * self._values[self.rank] > -1 * self._values[other.rank]) # the total ordering of Card won't get __gt__ (>) right because it's not equivalent to not __lt__ because of Ace, so explicitly define it def __gt__(self, other): if not isinstance(other, PlayingCard) or issubclass(other, PlayingCard): raise NotImplementedError(f"Comparison of {type(self).__name__} to {type(other).__name__} is not supported") # since Ace is 0, it will always pass one of these two conditions so it is always greater than return (self._values[self.rank] > self._values[other.rank]) or (-1 * self._values[self.rank] < -1 * self._values[other.rank]) @property def color(self): return self._color