cardy/cardy/deck.py

179 lines
3.9 KiB
Python
Raw Normal View History

2021-12-01 22:27:40 +00:00
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}]"
)