cardy/cardy/deck.py

179 lines
3.9 KiB
Python

from random import shuffle
from typing import Any
from typing import Dict
from typing import List
from typing import Iterable
from typing import Optional
class EmptyPileError(IndexError):
pass
class Card(object):
_color: Optional[str]
_face: Optional[str]
_suit: Optional[str]
_value: int
def __init__(
self,
value: int,
suit: Optional[str] = None,
color: Optional[str] = None,
face: Optional[str] = None,
):
self._color = color
self._face = face
self._suit = suit
self._value = value
@property
def color(self) -> Optional[str]:
return self._color
@property
def face(self) -> Optional[str]:
return self._face
@property
def suit(self) -> Optional[str]:
return self._suit
@property
def value(self) -> int:
return self._value
def __lt__(self, other) -> bool:
return self.value < other.value
def __le__(self, other) -> bool:
return self.value <= other.value
def __gt__(self, other) -> bool:
return self.value > other.value
def __ge__(self, other) -> bool:
return self.value >= other.value
def __eq__(self, other) -> bool:
return (
self.value == other.value and
self.face == other.face and
self.suit == other.suit and
self.color == other.color
)
def __repr__(self) -> str:
return f"<Card value={self.value} suit={self.suit} color={self.color} face={self.face}>"
def json(self) -> Dict[str, Any]:
return {
"value": self.value,
"suit": self.suit,
"color": self.color,
"face": self.face,
}
class FaceDownCard(Card):
def json(self) -> Dict[str, Any]:
return {
"value": "FACE_DOWN",
}
class WildCard(Card):
def __init__(self):
super().__init__(-1, face="Wild", suit="Wild", color="Wild")
def __lt__(self, other) -> bool:
return True
def __le__(self, other) -> bool:
return True
def __gt__(self, other) -> bool:
return True
def __ge__(self, other) -> bool:
return True
def __eq__(self, other) -> bool:
return True
class Pile(object):
# Pile of cards where 0 is the top and -1 is the bottom
_pile: List[Card]
def __init__(self, cards: Optional[List[Card]] = None):
if cards is not None:
self._pile = cards
else:
self._pile = []
def add_top(self, card: Card):
self._pile.insert(0, card)
def add_bottom(self, card: Card):
self._pile.insert(-1, card)
def peek_top(self) -> Optional[Card]:
if self._pile:
return self._pile[0]
return None
def draw_card(self) -> Card:
if not self._pile:
raise EmptyPileError("Cannot draw from pile because it's empty")
return self._pile.pop(0)
def draw_cards(self, num_cards: int = 1) -> Iterable[Card]:
return [self.draw_card() for _ in range(0, num_cards)]
def shuffle(self):
shuffle(self._pile)
def __getitem__(self, i):
return self._cards[i]
def __len__(self) -> int:
return len(self._pile)
def __str__(self) -> str:
if self:
return (
f"[[{len(self)-1} cards]]"
)
else:
return "[[empty pile]]"
def json(self) -> List[Dict[str, Any]]:
return [c.json() for c in self._pile]
class FaceUpPile(Pile):
def __str__(self) -> str:
top_card = self.peek_top()
if top_card:
return (
f"[[{str(top_card)} +{len(self)-1} cards]]"
)
else:
return "[[empty pile]]"
class OpenCardSet(Pile):
def __str__(self) -> str:
cards = ", ".join((
str(card) for card in self._pile
))
return (
f"[{cards}]"
)