implemented abstract Card class
implemented concrete PlayingCard class
This commit is contained in:
0
BearOnline/__init__.py
Normal file
0
BearOnline/__init__.py
Normal file
0
BearOnline/cards/__init__.py
Normal file
0
BearOnline/cards/__init__.py
Normal file
25
BearOnline/cards/card.py
Normal file
25
BearOnline/cards/card.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import abc
|
||||||
|
from abc import ABC
|
||||||
|
from functools import total_ordering
|
||||||
|
|
||||||
|
@total_ordering
|
||||||
|
class Card(ABC):
|
||||||
|
def __init__(self, rank, suit):
|
||||||
|
self._rank = rank
|
||||||
|
self._suit = suit
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __lt__(self, other):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __eq__(self, other):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rank(self):
|
||||||
|
return self._rank
|
||||||
|
|
||||||
|
@property
|
||||||
|
def suit(self):
|
||||||
|
return self._suit
|
||||||
65
BearOnline/cards/playingcard.py
Normal file
65
BearOnline/cards/playingcard.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
from .card 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
|
||||||
Reference in New Issue
Block a user