Skip to content

Commit

Permalink
adding cards and decks, and writing tests for everythuing
Browse files Browse the repository at this point in the history
  • Loading branch information
jpetrucciani committed Sep 1, 2019
1 parent becb024 commit 8262e6c
Show file tree
Hide file tree
Showing 11 changed files with 380 additions and 23 deletions.
69 changes: 64 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
gamble: a collection of gambling classes/tools
gamble
==============================================

.. image:: https://travis-ci.org/jpetrucciani/gamble.svg?branch=master
Expand All @@ -20,14 +20,14 @@ gamble: a collection of gambling classes/tools
:alt: Python 3.6+ supported


**gamble**
**gamble** is a simple library that implements a collection of some common gambling-related classes


Features
--------

- dice
- (coming soon) cards
- die, dice
- cards, decks

Usage
-----
Expand All @@ -40,8 +40,67 @@ Installation
pip install gamble
Basic Usage
^^^^^^^^^^^
-----------

Dice
^^^^

.. code-block:: python
import gamble
# create dice, defaults to 2 6-sided dice
dice = gamble.Dice()
# roll
dice.roll()
>>> 6
dice.rolls
>>> 1
# max, min
dice.max
>>> 12
dice.min
>>> 2
# d-notation for dice constructor
dice = gamble.Dice('d20+8')
# max, min
dice.max
>>> 28
dice.min
>>> 9
# parts
dice.parts
>>> [<d20 Die>, 8]
Cards
^^^^^

.. code-block:: python
import gamble
# create a deck, defaults to the standard 52 card deck, no jokers
# the deck will be shuffled by default, unless you pass shuffle=False
deck = gamble.Deck()
deck.cards_left
>>> 52
deck.top
>>> <Card:7>
deck.bottom
>>> <Card:9>
deck.shuffle() # you can also pass times=(int) to shuffle more than once
card = deck.draw() # you can also pass times=(int) to draw a list of cards
>>> <Card:A♠>
# the unicode cards icons are implemented as well!
card.unicode
>>> "🂡"
2 changes: 1 addition & 1 deletion gamble/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""
gamble module
"""
from gamble.models import Die, Dice # noqa
from gamble.models import * # noqa
6 changes: 0 additions & 6 deletions gamble/globals.py

This file was deleted.

1 change: 1 addition & 0 deletions gamble/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
models submodule for gamble
"""
from gamble.models.dice import Die, Dice # noqa
from gamble.models.cards import Card, Deck, EuchreDeck # noqa
202 changes: 202 additions & 0 deletions gamble/models/cards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
"""
standard deck of cards submodule
"""
import random
from types import SimpleNamespace
from typing import Any, List, Union


class Suit(SimpleNamespace):
"""suit namespace class"""


class Value(SimpleNamespace):
"""value namespace class"""


class Card:
"""playing card model"""

BLACK = 0
RED = 1

class Suits:
"""card suit enum"""

SPADES = Suit(name="spades", char="♠", value=0, color=0, unicode=127136)
CLUBS = Suit(name="clubs", char="♣", value=1, color=0, unicode=127184)
DIAMONDS = Suit(name="diamonds", char="♦", value=2, color=1, unicode=127168)
HEARTS = Suit(name="hearts", char="♥", value=3, color=1, unicode=127152)

@classmethod
def all(cls) -> List[Suit]:
"""get all suits"""
return sorted(
[
cls.__dict__[x]
for x in dir(cls)
if not x.startswith("_") and isinstance(cls.__dict__[x], Suit)
],
key=lambda x: x.value,
)

class Values:
"""card value enum"""

ACE = Value(char="A", name="ace", value=1)
TWO = Value(char="2", name="two", value=2)
THREE = Value(char="3", name="three", value=3)
FOUR = Value(char="4", name="four", value=4)
FIVE = Value(char="5", name="five", value=5)
SIX = Value(char="6", name="six", value=6)
SEVEN = Value(char="7", name="seven", value=7)
EIGHT = Value(char="8", name="eight", value=8)
NINE = Value(char="9", name="nine", value=9)
TEN = Value(char="T", name="ten", value=10)
JACK = Value(char="J", name="jack", value=11)
QUEEN = Value(char="Q", name="queen", value=12)
KING = Value(char="K", name="king", value=13)

@classmethod
def all(cls) -> List[Value]:
"""get all suits"""
return sorted(
[
cls.__dict__[x]
for x in dir(cls)
if not x.startswith("_") and isinstance(cls.__dict__[x], Value)
],
key=lambda x: x.value,
)

def __init__(self, value: Value = Values.ACE, suit: Suit = Suits.SPADES) -> None:
"""card constructor"""
self.value = value
self.suit = suit

@property
def color(self) -> int:
"""returns the color of the card"""
return self.suit.color

@property
def full_name(self) -> str:
"""returns the full name for this card"""
return "{} of {}".format(self.value.name, self.suit.name)

@property
def is_black(self) -> bool:
"""is_black property"""
return self.color == Card.BLACK

@property
def unicode(self) -> str:
"""get the fun little unicode card for this card"""
# we need to skip the 'knight' card if we're a queen or king
hack = int(self.value.value >= 12)
return chr(self.suit.unicode + self.value.value + hack)

@property
def is_red(self) -> bool:
"""is_red property"""
return self.color == Card.RED

def __str__(self) -> str:
"""string representation of this card"""
return "{}{}".format(self.value.char, self.suit.char)

def __repr__(self) -> str:
"""representation of this card"""
return "<Card:{}>".format(str(self))

def __lt__(self, other: "Card") -> bool:
"""less than dunder method"""
return self.value.value < other.value.value

def __eq__(self, other: object) -> bool:
"""equal to dunder method"""
if not isinstance(other, Card):
return False
return self.suit == other.suit and self.value == other.value


class Deck:
"""playing card deck model"""

def __init__(self, cards: List[Card] = None, shuffle: bool = True) -> None:
"""deck constructor"""
if cards:
self.cards = cards
else:
# lets start with a default deck of 52
self.cards = []
for suit in Card.Suits.all():
for value in Card.Values.all():
self.cards.append(Card(value=value, suit=suit))
self.cards.reverse()
self.shuffles = 0
self.draws = 0
if shuffle:
self.shuffle()

def __contains__(self, item: object) -> bool:
"""dunder contains method"""
if not isinstance(item, Card):
return False
return item in self.cards

def __str__(self) -> str:
"""string representation of a deck"""
return "<Deck[{}]>".format(self.cards_left)

def __repr__(self) -> str:
"""term representation of a deck"""
return str(self)

@property
def top(self) -> Card:
"""the top card of the deck"""
return self.cards[-1]

@property
def bottom(self) -> Card:
"""the bottom card of the deck"""
return self.cards[0]

@property
def cards_left(self) -> int:
"""number of cards left in the deck"""
return len(self.cards)

def draw(self, times: int = 1) -> Union[Card, List[Card]]:
"""draws the given number of cards from the deck"""
if times == 1:
self.draws += 1
return self.cards.pop()
cards = []
for _ in range(times):
self.draws += 1
cards.append(self.cards.pop())
return cards

def shuffle(self, times: int = 1) -> None:
"""shuffle the deck"""
for _ in range(times):
self.shuffles += 1
random.shuffle(self.cards)


class EuchreDeck(Deck):
"""deck specifically for euchre"""

def __init__(self, **kwargs: Any) -> None:
"""euchre deck constructor"""
cards: List[Card] = []

# euchre uses 9, 10, J, Q, K, A of all suits
values = [x for x in Card.Values.all() if x.value >= 9 or x.value == 1]
for suit in Card.Suits.all():
for value in values:
cards.append(Card(value=value, suit=suit))
cards.reverse()
super().__init__(cards=cards)
10 changes: 5 additions & 5 deletions gamble/models/dice.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ def __repr__(self) -> str:
"""repr"""
return self.__str__()

def __lt__(self, other) -> bool:
def __lt__(self, other: "Die") -> bool:
"""less than dunder method"""
return self.net_sides < other.net_sides

@property
def net_sides(self) -> int:
""""""
"""the raw max sides * multiplier"""
return self.sides * self.multiplier

@property
Expand All @@ -54,7 +54,7 @@ def roll(self) -> int:
class Dice:
"""a group of die objects"""

def __init__(self, init_string: str) -> None:
def __init__(self, init_string: str = "2d6") -> None:
"""create a new d notation group of dice"""
self.__d_string = init_string.strip().lower().replace("-", "+-")
self.d_strings = [x.strip() for x in self.__d_string.split("+")]
Expand Down Expand Up @@ -89,12 +89,12 @@ def parts(self) -> List[Union[Die, int]]:
return [*self.dice, *self.bonuses]

@property
def max(self):
def max(self) -> int:
"""returns the max value these dice + bonuses could return"""
return sum([*[x.max for x in self.dice], *self.bonuses])

@property
def min(self):
def min(self) -> int:
"""returns the min value these dice + bonuses could return"""
return sum([*[x.min for x in self.dice], *self.bonuses])

Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[mypy]
python_version = 3.6
disallow_untyped_defs = False
disallow_untyped_defs = True
ignore_missing_imports = True

[flake8]
Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
pip setup file
"""
from setuptools import setup, find_packages
from gamble.globals import __library__, __version__


__library__ = "gamble"
__version__ = "0.0.5"

with open("README.rst") as readme:
LONG_DESCRIPTION = readme.read()

Expand Down
Loading

0 comments on commit 8262e6c

Please sign in to comment.