class Card(db.Model): '''Provide card object.''' # TODO join rank suit id = db.Column(db.Integer, primary_key=True) # id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid4) rank = db.Column(db.String(1), unique=False, nullable=False) suit = db.Column(db.String(1), unique=False, nullable=False) ranks: tuple = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') suits: tuple = ('C', 'D', 'H', 'S') def __init__(self, rank: str, suit: str) -> None: '''Initialize card.''' if rank in Card.ranks: self.rank = rank else: raise exceptions.InvalidRankCardException('invalid rank') if suit.lower().capitalize() in Card.suits: self.suit = suit.lower().capitalize() else: raise exceptions.InvalidSuitCardException('invalid suit') def __repr__(self) -> str: '''Return string representation of card.''' return ( f"{self.__class__.__name__}(rank={self.rank!r}, suit={self.suit!r})" ) def __hash__(self) -> int: '''Check object hash.''' # TODO: better alternative return hash((self.rank, self.suit)) def __eq__(self, other: object) -> bool: '''Check if card is equal another card.''' if other.__class__ is not self.__class__: return NotImplemented return ((self.rank, self.suit) == (other.rank, other.suit ) # type: ignore ) def __gt__(self, other: 'Card') -> bool: '''Check if card is greater than other.''' if not other: return True if self.suit == other.suit: return Card.ranks.index(self.rank) > Card.ranks.index(other.rank) elif self.suit == 'S': return True elif other.suit == 'S': return False else: raise exceptions.InvalidComparisonCardException( 'except for invalid comparison')
class Deck(db.Model): '''Provide deck object.''' id = db.Column(db.Integer, primary_key=True) cards = db.relationship('Card', secondary=cards, lazy='subquery', backref=db.backref('deck_of_cards', lazy=True)) max_size = 52 def __init__(self) -> None: '''Initialize Deck.''' for rank in Card.ranks: for suit in Card.suits: self.cards.append(Card(rank, suit)) def __iter__(self) -> 'Deck': '''Return deck itself as iterator.''' return self def __next__(self) -> Card: '''Get next card instance.''' if len(self.cards) <= 0: raise StopIteration() card = choice(self.cards) self.cards.remove(card) return card
class Play(db.Model): '''Represent player action.''' book_id = db.Column(db.Integer, db.ForeignKey('book.id'), primary_key=True) card_id = db.Column(db.Integer, db.ForeignKey('card.id'), primary_key=True) player_id = db.Column( db.Integer, db.ForeignKey('player.id'), primary_key=True ) db.relationship( 'Book', uselist=False, backref='plays', lazy='dynamic' ) db.relationship( 'Card', uselist=False, backref='plays', lazy='dynamic' ) db.relationship( 'Player', uselist=False, backref='plays', lazy='dynamic' ) db.UniqueConstraint('book_id', 'card_id', 'player_id') def __init__(self, book_id: int, player_id: int, card_id: int) -> None: self.book_id = book_id self.player_id = player_id self.card_id = card_id def __repr__(self) -> str: '''Return string representation of card.''' play = dedent(f"""\ book_id={self.book_id!r},\ player_id={self.player_id!r},\ card={self.card!r}\ """) return ( f"{self.__class__.__name__}({play})" ) @property def card(self) -> Card: return Card.query.get(self.card_id)
# -*- coding: utf-8 -*- # copyright: (c) 2020 by Jesse Johnson. # license: Apache 2.0, see LICENSE for more details. '''Provide card package.''' from secrets import choice from spades import db from spades.game.models.card import Card cards = db.Table( 'deck_of_cards', db.Column('card_id', db.Integer, db.ForeignKey('card.id'), primary_key=True), db.Column('deck_id', db.Integer, db.ForeignKey('deck.id'), primary_key=True) # db.Column('card_rank', db.String(1)), # db.Column('card_suit', db.String(1)), # db.ForeignKeyConstraint( # ['card_rank', 'card_suit'], ['card.rank', 'card.suit'] # ) ) class Deck(db.Model): '''Provide deck object.'''
class Player(db.Model): '''Provide player object.''' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(100), nullable=False, unique=True) hand = db.relationship('Hand', uselist=False, back_populates='player') def __init__(self, username: str) -> None: '''Initialize player.''' self.username = username # TODO: values are round based self.__books: Set[Book] = set() self.__bid: Optional[int] = None self.__bags: int = 0 self.__score: int = 0 @property def score(self) -> int: '''Get player score.''' return self.__score @score.setter def score(self, clean: bool = False) -> None: '''Score players hand.''' # TODO overtricks books = len(self.books) if self.bid == 0: if books == 0: score = 50 else: score = -50 elif self.bid >= books: if self.bid > books: bags = self.bid - books score = books * 10 if (self.__bags + bags) < 10: self.__bags += bags score += bags else: score -= (100 - bags) self.__bags = 0 else: score = books * 10 else: score = books * -10 if clean: self.__books = set() self.__bid = None Hand.spades_broken = False self.__score = score # @property # def hand(self) -> Optional[Hand]: # '''Get player hand.''' # return self. # @hand.setter # def hand(self, hand: Hand) -> None: # '''Set player hand.''' # self.hand = hand @property def books(self) -> Set[Book]: '''Get awarded books.''' return self.__books @books.setter def books(self, book: Book) -> None: '''Get player books won.''' self.__books.add(book) @property def bid(self) -> Optional[int]: '''Get players bid.''' return self.__bid @bid.setter def bid(self, bid: int) -> None: '''Set players bid.''' self.__bid = bid def play_card(self, rank: str, suit: str) -> Optional[Card]: '''Play card from players hand.''' if self.hand and self.hand.cards: return self.hand.pull_card(rank, suit) else: raise exceptions.EmptyHandSizeException( 'player has no cards to play')
class Game(db.Model): '''Provide game object.''' id = db.Column(db.Integer, primary_key=True)
class Hand(db.Model): '''Provide player hand object.''' # TODO: lookup stack spades_broken = False max_size = 13 id = db.Column(db.Integer, primary_key=True) player_id = db.Column(db.Integer, db.ForeignKey('player.id')) player = db.relationship('Player', back_populates='hand') cards = db.relationship('Card', secondary=cards, lazy='subquery', backref=db.backref('hand_of_cards', lazy=True)) def __repr__(self) -> str: '''Return string representation of card.''' return f"{self.__class__.__name__}(hand={self.cards!r})" def __len__(self) -> int: '''Return number of items.''' return len(self.cards) def __iter__(self) -> 'Hand': '''Return hand itself as iterator.''' self.__count = 0 return self def __next__(self) -> Card: '''Get next card instance.''' if self.__count >= len(self.cards): raise StopIteration() card: Card = self.cards[self.__count] self.__count += 1 return card @property def to_json(self): '''Get json instance.''' return (json.dumps(self.cards, cls=CardEncoder)) def list_suit(self, suit: str) -> List[str]: '''List items of a suit.''' return [c.rank for c in self.cards if c.suit == suit] def get_suit(self, suit: str) -> List[Card]: '''Get all items of a suit.''' return [c for c in self.cards if c.suit == suit] def playable(self, suit: str = None) -> List[Card]: '''Get all playable cards.''' if not suit and not Hand.spades_broken: hand = [c for c in self.cards if c.suit != 'S'] return self.cards if hand == [] else hand elif suit and len(self.get_suit(suit)) > 0: return self.get_suit(suit) else: return self.cards def add_card(self, card: Card) -> None: '''Add card to player hand.''' if len(self.cards) < Hand.max_size: if card not in self.cards: self.cards.append(card) else: exceptions.InvalidDeckException( 'duplicate card already in hand') else: raise exceptions.MaxHandSizeException('maximum hand size') def pull_card(self, rank: str, suit: str) -> Card: '''Pull card from hand to play.''' # TODO change relationship card = Card.query.filter(Card.rank == rank, Card.suit == suit).first() print('----cards----', card) if card in self.cards: if card.suit == 'S': Hand.spades_broken = True self.cards.remove(card) return card raise exceptions.IllegalPlayException('player does not hold card')