Add some code

This commit is contained in:
IamTheFij 2021-12-01 14:27:40 -08:00
parent e33c23473a
commit a7a3c5bed2
10 changed files with 425 additions and 3 deletions

View File

@ -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.

15
Makefile Normal file
View File

@ -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
$(VENV):
$(PYTHON) -m venv $(VENV)
$(VENV)/bin/pip install -r requirements-dev.txt -r requirements.txt

0
cardy/__init__.py Normal file
View File

12
cardy/__main__.py Normal file
View File

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

178
cardy/deck.py Normal file
View File

@ -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):
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}]"
)

137
cardy/phase_10.py Normal file
View File

@ -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):
pass
class Skip(Phase10Card):
pass
class Wild(WildCard, Phase10Card):
pass
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]))
super().__init__(cards)
class PhaseSet(OpenCardSet):
pass
class Phase10Player(Player):
_table: List[FaceUpPile]
def __init__(self, name):
self._table = []
super().__init__(name)
def take_turn(self) -> Any:
pass
def __str__(self):
table = " no sets played"
if self._table:
table = "\n".join((" "+str(s) for s in self._table))
return "\n".join((
f"{self.name}: {len(self._hand)} in hand",
"On table:",
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)
@dataclass
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.draw_pile.shuffle()
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)]
p.draw_cards(self.draw_pile)
def take_turn(self):
end_round = self.next_player.take_turn()
self.turn += 1
if end_round:
self._reset_round()
@property
def next_player(self) -> Player:
return self.players[self.turn % len(self.players)]
@property
def dealer(self) -> Player:
return self.players[self._dealer_index]
def _reset_round(self):
self.draw_pile = Deck()
self.draw_pile.shuffle()
self.dealer += 1
self.deal()
def __str__(self) -> str:
return "\n".join((
"Game",
f"Draw: {str(self.draw_pile)}",
f"Discard: {str(self.discard_pile)}",
"",
"\n\n".join(
str(p) for p in self.players
),
))

20
cardy/player.py Normal file
View File

@ -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):
self.name = 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={self.name}>"

60
cardy/playing_cards.py Normal file
View File

@ -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
SUITS = (
("diamonds", "red"),
("hearts", "red"),
("clubs", "black"),
("spades", "black"),
)
# FACES maps special values to their face names
FACES = {
11: "Jack",
12: "Queen",
13: "King",
14: "Ace",
-1: "Joker",
}
class PlayingCard(Card):
@property
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()} "
f"({self.color.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):
cards.append(
PlayingCard(
value=c[0],
suit=c[1][0],
color=c[1][1],
)
)
for _ in range(0, num_jokers):
cards.append(Joker())
super().__init__(cards2)

0
requirements-dev.txt Normal file
View File

0
requirements.txt Normal file
View File