Add some code

IamTheFij 1 year ago
parent e33c23473a
commit a7a3c5bed2

@ -208,8 +208,8 @@ If you develop a new program, and you want it to be of the greatest possible use
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
Cardy, a Python package for building card games
Copyright (C) 2021 Ian Fijolek
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
Cardy Copyright (C) 2021 Ian Fijolek
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

@ -0,0 +1,15 @@
.PHONY: test clean all
VENV := venv
PYTHON := python3
.PHONY: default
default: run
.PHONY: run
run: $(VENV)
$(VENV)/bin/python -m cardy
$(PYTHON) -m venv $(VENV)
$(VENV)/bin/pip install -r requirements-dev.txt -r requirements.txt

@ -0,0 +1,12 @@
from cardy.phase_10 import Game
if __name__ == "__main__":
g = Game(["Ian", "Jessica"])
print(f"Deal it out {}")
print(f"Next player is {}")

@ -0,0 +1,178 @@
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):
class Card(object):
_color: Optional[str]
_face: Optional[str]
_suit: Optional[str]
_value: int
def __init__(
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
def color(self) -> Optional[str]:
return self._color
def face(self) -> Optional[str]:
return self._face
def suit(self) -> Optional[str]:
return self._suit
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
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):
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]]"
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]]"
return "[[empty pile]]"
class OpenCardSet(Pile):
def __str__(self) -> str:
cards = ", ".join((
str(card) for card in self._pile
return (

@ -0,0 +1,137 @@
from dataclasses import dataclass
from itertools import product
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from cardy.deck import Card
from cardy.deck import FaceUpPile
from cardy.deck import OpenCardSet
from cardy.deck import Pile
from cardy.deck import WildCard
from cardy.player import Player
class Phase10Card(Card):
class Skip(Phase10Card):
class Wild(WildCard, Phase10Card):
class Deck(Pile):
def __init__(self):
cards: List[Phase10Card] = []
for c in product(range(1, 13), ("red", "blue", "green", "yellow")):
cards.append(Phase10Card(c[0], color=c[1]))
class PhaseSet(OpenCardSet):
class Phase10Player(Player):
_table: List[FaceUpPile]
def __init__(self, name):
self._table = []
def take_turn(self) -> Any:
def __str__(self):
table = " no sets played"
if self._table:
table = "\n".join((" "+str(s) for s in self._table))
return "\n".join((
f"{}: {len(self._hand)} in hand",
"On table:",
def json(self, is_owner: bool=False) -> Dict[str, Any]:
state = {
"table": [pile.json() for pile in self._table],
if is_owner:
state["hand"] = [card.json() for card in self._hand]
if is_owner:
state["hand"] = len(self._hand)
class Phase10Turn:
player: Player
draw_card: bool
discard_card: Optional[Card]
play_sets: Optional[List[PhaseSet]]
play_on_sets: Optional[Dict[str, List[Card]]]
class Game(object):
_dealer_index: int
draw_pile: Deck
discard_pile: FaceUpPile
turn: int
game_round: int
players: List[Phase10Player]
sets: List[OpenCardSet]
def __init__(self, player_names: List[str]):
self._dealer_index = 0
self.turn = 1
self.game_round = 1
self.players = [Phase10Player(name) for name in player_names]
self.draw_pile = Deck()
self.discard_pile = FaceUpPile()
self.sets = []
def deal(self):
# each player draws 10
for _ in range(0, 10):
for seat in range(0, len(self.players)):
draw_seat = seat + self._dealer_index + 1
p = self.players[draw_seat % len(self.players)]
def take_turn(self):
end_round = self.next_player.take_turn()
self.turn += 1
if end_round:
def next_player(self) -> Player:
return self.players[self.turn % len(self.players)]
def dealer(self) -> Player:
return self.players[self._dealer_index]
def _reset_round(self):
self.draw_pile = Deck()
self.draw_pile.shuffle() += 1
def __str__(self) -> str:
return "\n".join((
f"Draw: {str(self.draw_pile)}",
f"Discard: {str(self.discard_pile)}",
str(p) for p in self.players

@ -0,0 +1,20 @@
from typing import Any
from typing import List
from cardy.deck import Card
from cardy.deck import Pile
class Player(object):
def __init__(self, name: str): = name
self._hand: List[Card] = []
def draw_cards(self, pile: Pile, num_cards: int = 1):
self._hand += pile.draw_cards(num_cards)
def take_turn(self) -> Any:
raise NotImplementedError()
def __repr__(self) -> str:
return f"<Player name={}>"

@ -0,0 +1,60 @@
from itertools import product
from typing import List
from cardy.deck import Card
from cardy.deck import Pile
from cardy.deck import WildCard
# SUITES is a collection of tuples with suites and their colors
("diamonds", "red"),
("hearts", "red"),
("clubs", "black"),
("spades", "black"),
# FACES maps special values to their face names
11: "Jack",
12: "Queen",
13: "King",
14: "Ace",
-1: "Joker",
class PlayingCard(Card):
def face(self) -> str:
return FACES.get(self.value, str(self.value))
def __str__(self) -> str:
return (
f"[{self.face.capitalize()} of {self.suit.capitalize()} "
class Joker(WildCard, PlayingCard):
def __str__(self) -> str:
return "[Joker]"
class Deck(Pile):
def __init__(self, num_jokers: int = 0, aces_high: bool = False):
cards: List[PlayingCard] = []
card_values = range(2, 15) if aces_high else range(1, 14)
for c in product(card_values, SUITS):
for _ in range(0, num_jokers):