Implement playing cards

This commit is contained in:
Mark Veidemanis 2022-03-11 20:16:04 +00:00
parent 857b26f4a3
commit 74bd1c4415
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
1 changed files with 234 additions and 84 deletions

View File

@ -4,6 +4,12 @@ import random
class NoMoreCards(Exception): class NoMoreCards(Exception):
pass pass
class InvalidCard(Exception):
pass
class InvalidSuit(Exception):
pass
class Game(object): class Game(object):
def __init__(self, deck, rules): def __init__(self, deck, rules):
self.players = [] self.players = []
@ -13,50 +19,86 @@ class Game(object):
# Which index player's turn is it? # Which index player's turn is it?
self.player_index = 0 self.player_index = 0
print("Created game.") self.last_player = None
@property
def current_player(self):
return self.players[self.player_index]
def next_turn(self): def next_turn(self):
current_player = self.players[self.current_player] current_player = self.players[self.player_index]
print(f"Current player is {current_player}") print(f"Current player is {current_player}")
if self.player_index == len(self.players): if self.player_index == len(self.players):
print(f"Reached the end of players: {self.players}, {self.current_player}") print(f"Reached the end of players: {self.players}, {current_player}")
self.current_player = 0 self.current_player = 0
else: else:
self.current_player += 1 self.current_player += 1
print(f"Incrementing current_player: {self.players}, {self.current_player}") print(f"Incrementing current_player: {self.players}, {current_player}")
def determine_start(self, first_card):
"""Determine who starts the game."""
card_map = {}
for player in self.players:
player_min = player.hand.min_of_suit_or_none(first_card.suit)
if player_min is None:
card_map[None] = player
else:
card_map[str(player_min)] = player
if len(card_map) == 1 and None in card_map:
return card_map[None]
else:
without_none = {k:v for k,v in card_map.items() if k}
min_card = min(without_none.keys())
min_player = without_none[min_card]
return min_player
def add_players(self, *players): def add_players(self, *players):
for player in players: for player in players:
print(f"Adding player: {player}") print(f"Adding player: {player}")
self.players.append(player) self.players.append(player)
def flipcard(self):
if not len(self.playdeck.cards) == 0:
return False
first_card = self.drawdeck.draw()
print(f"First card is: {first_card}")
self.playdeck.put(first_card)
starter = self.determine_start(first_card)
print(f"{starter} to start!")
def deal(self): def deal(self):
for player in self.players: for player in self.players:
num_cards = self.rules.initial_cards num_cards = self.rules.initial_cards
cards = list(self.drawdeck.draw_num(num_cards)) cards = list(self.drawdeck.draw_num(num_cards))
print("Cards", cards) player.hand.put_cards(*cards)
player.deck.put_cards(*cards)
if self.rules.flipcard: if self.rules.flipcard:
first_card = self.drawdeck.draw() self.flipcard()
print(f"First card is: {first_card}")
self.playdeck.put(first_card)
def play_card(self, player, card): def play_card(self, player, card, special=True):
print(f"Player {player} attempting to play {card}") print(f"Player {player} attempting to play {card}")
last_card = self.playdeck.last_card last_card = self.playdeck.last_card
print(f"Last card {last_card}") print(f"Last card {last_card}")
is_playable = self.rules.is_playable_on(last_card, card) if player == self.last_player:
special = False
is_playable = self.rules.is_playable_on(last_card, card, special=special)
print("Card is playable", is_playable) print("Card is playable", is_playable)
#current_card = self.d if is_playable:
actual_card = player.hand.draw_card(card)
self.playdeck.put(actual_card)
self.last_player = player
return is_playable
#def play_combination(self, player, *cards):
# print(f"Player {player} attempting to play combination {cards}")
class Player(object): class Player(object):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
self.deck = Hand() self.hand = Hand()
print(f"Created player {name}")
def __str__(self): def __str__(self):
return self.name return f"@{self.name}"
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
@ -66,11 +108,9 @@ class SetOfCards(object):
self.cards = [] self.cards = []
if cards: if cards:
self.cards = [*cards] self.cards = [*cards]
print(f"Created deck with cards: {self.cards}")
def draw(self): def draw(self):
card = self.cards.pop() card = self.cards.pop()
print(f"Drawing {card} from deck")
return card return card
def draw_num(self, num): def draw_num(self, num):
@ -78,17 +118,28 @@ class SetOfCards(object):
yield self.cards.pop() yield self.cards.pop()
def put(self, card): def put(self, card):
print("Putting card on deck:", card)
self.cards.append(card) self.cards.append(card)
def put_cards(self, *cards): def put_cards(self, *cards):
for card in cards: for card in cards:
self.put(card) self.put(card)
def draw_card(self, card):
for mycard in self.cards:
if card == mycard:
self.cards.remove(mycard)
print(f"Returned {mycard} and amended cards: {self.cards}")
return mycard
def __str__(self):
return str(self.cards)
def __repr__(self):
return self.__str__()
class Deck(SetOfCards): class Deck(SetOfCards):
def shuffle(self): def shuffle(self):
random.shuffle(self.cards) random.shuffle(self.cards)
print(f"Shuffled deck: {self.cards}")
@property @property
def last_card(self): def last_card(self):
@ -97,44 +148,70 @@ class Deck(SetOfCards):
else: else:
raise NoMoreCards raise NoMoreCards
@property
def last_cards(self):
if self.cards:
return self.cards[:5]
def fill(self):
for suit_str in "SHCD":
values_str_10 = [str(x) for x in range(2, 11)]
for val_str in [*values_str_10, "J", "Q", "K", "A"]:
card_str = f"{suit_str}{val_str}"
print("card_str", card_str)
card = Card(card_str)
self.cards.append(card)
print("cards after fill", self.cards)
print("len after fill", len(self.cards))
class Hand(SetOfCards): class Hand(SetOfCards):
pass def have_suit(self, suit):
"""Have we got this suit?"""
for card in self.cards:
if card.suit == suit:
return True
return False
def min_of_suit_or_none(self, suit):
if not self.have_suit(suit):
return None
suit_cards = []
for card in self.cards:
if card.suit == suit:
suit_cards.append(card)
return min(suit_cards)
class Rules(object): class Rules(object):
def __init__(self): def __init__(self):
self.initial_cards = 2 # Number of cards to start with self.initial_cards = 7 # Number of cards to start with
self.flipcard = True # Initially flip a card self.flipcard = True # Initially flip a card
self.jacks_on_twos = False # Can we play Jacks on 2s? self.jacks_on_twos = False # Can we play Jacks on 2s?
self.jacks_on_eights = False # Can we play Jacks on 8s? self.jacks_on_eights = False # Can we play Jacks on 8s?
self.set_power_cards() self.set_power_cards()
print("Created rules.")
def set_power_cards(self): def set_power_cards(self):
# Next player has to pick up two cards if they don't have a # Next player has to pick up two cards if they don't have a
# card of the same value # card of the same value
self.pick_up_two = Value(2) self.pick_up_two = Value("2")
# Next player has to skip a go if they don't have another # Next player has to skip a go if they don't have another
self.skip_a_turn = Value(8) self.skip_a_turn = Value("8")
# How many cards to pick up # How many cards to pick up
self.multiple_number = 6 self.multiple_number = 6
pick_up_multiple_suits = [Suit("Spades"), Suit("Clubs")] pick_up_multiple_suits = [Suit("S"), Suit("C")]
# Create a BlackJack filter # Create a BlackJack filter
self.pick_up_multiple = Filter(pick_up_multiple_suits, Value("Jack")) self.pick_up_multiple = Filter(pick_up_multiple_suits, Value("J"))
self.change_suit = Value("Ace") self.change_suit = Value("A")
self.specials_all = [self.pick_up_two, self.specials_all = [self.pick_up_two,
self.skip_a_turn, self.skip_a_turn,
self.pick_up_multiple, self.pick_up_multiple,
self.change_suit] self.change_suit]
self.specials_power = self.specials_all[:-1] self.specials_power = self.specials_all[:-1]
print("Specials all", self.specials_all)
print("Power specials", self.specials_power)
def can_play_on_special(bottom_card, card): def can_play_on_special_power(self, bottom_card, card):
# If the bottom card is a two # If the bottom card is a two
if bottom_card == self.pick_up_two: if bottom_card == self.pick_up_two:
# If the proposed card is not also the same # If the proposed card is not also the same
@ -162,41 +239,67 @@ class Rules(object):
if card == self.pick_up_multiple: if card == self.pick_up_multiple:
return True return True
return False return False
print("Reached final return for can play on special")
return False return False
def is_playable_on(bottom_card, card): def can_play_special_on(self, bottom_card, card):
if bottom_card in self.rules.specials_all: print("CARD IS ", card)
return self.can_play_on_special(bottom_card, card) print("CHANGE SUIT IS", self.change_suit)
if card == self.change_suit:
print("Card is change suit")
if bottom_card not in self.specials_power:
return True
return False
def is_playable_on(self, bottom_card, card, special):
print(f"Is playable on {bottom_card} {card}")
if special:
if bottom_card in self.specials_power:
print("Bottom card in specials all")
return self.can_play_on_special_power(bottom_card, card)
if card.suit == bottom_card.suit: if card.suit == bottom_card.suit:
print("Suit is bottom card suit")
return True return True
if card.val == buttom_card.val: if card.val == bottom_card.val:
print("Value is bottom card value")
return True return True
if special:
if card in self.specials_all:
print("Card is in specials_all")
return self.can_play_special_on(bottom_card, card)
class Suit(object): class Suit(object):
def __init__(self, suit): def __init__(self, suit):
if suit not in "SHDC":
raise InvalidSuit
self.suit = suit self.suit = suit
def __str__(self): def __str__(self):
return f"#{self.suit}" return f"{self.suit}"
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
def __eq__(self, other): def __eq__(self, other):
return other.suit == self.suit if isinstance(other, Suit):
return self.suit == other.suit
elif isinstance(other, Card):
return self.suit == other.suit.suit
class Value(object): class Value(object):
def __init__(self, val): def __init__(self, val):
self.hash_map = {"J": 11, "Q": 12, "K": 13, "A": 14}
string_values = [str(x) for x in range(2, 11)]
if val not in self.hash_map.keys() and val not in string_values:
raise InvalidCard
self.val = val self.val = val
self.numeric_value = self.get_numeric_value(val) self.numeric_value = self.get_numeric_value(val)
def get_numeric_value(self, val): def get_numeric_value(self, val):
hash_map = {"Jack": 11, "Queen": 12, "King": 13, "Ace": 14} if val in self.hash_map:
if val in hash_map: return self.hash_map[val]
return hash_map[val]
else: else:
return val return int(val)
def __gt__(self, other): def __gt__(self, other):
return self.numeric_value > other.numeric_value return self.numeric_value > other.numeric_value
@ -207,9 +310,14 @@ class Value(object):
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, Value): if isinstance(other, Value):
return self.numeric_value == other.numeric_value return self.numeric_value == other.numeric_value
elif isinstance(other, Card):
return self.numeric_value == other.val.numeric_value
else:
print(f"ERROR! Cannot compare {self} and {other}")
return False
def __str__(self): def __str__(self):
return f".{str(self.val)}" return f"{str(self.val)}"
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
@ -225,37 +333,54 @@ class Filter(object):
self.val = val self.val = val
def __str__(self): def __str__(self):
return f"|{self.val} of {self.suits}" return f"|{self.val} of {self.suits}|"
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
def __eq__(self, other): def __eq__(self, other):
print("Filter EQ called")
print("other suit", other.suit)
print("our suits", self.suits)
print("other val", other.val)
print("our val", self.val)
if other.suit in self.suits and other.val == self.val: if other.suit in self.suits and other.val == self.val:
return True return True
else: else:
return False return False
class Card(object): class Card(object):
def __init__(self, suit=None, val=None): def __init__(self, identifier):
self.suit = Suit(suit) self.suit, self.val = Card.identifier_to_card(identifier)
self.val = Value(val)
print(f"Created card with suit {suit} and value {self.val}")
def playable_on(self, card): def playable_on(self, card):
pass pass
def __str__(self): def __str__(self):
return f"+({self.val} of {self.suit})" return f"{self.suit}{self.val}"
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
def __gt__(self, other):
return self.val.numeric_value > other.val.numeric_value
def __lt__(self, other):
return self.val.numeric_value < other.val.numeric_value
def __eq__(self, other):
if isinstance(other, Card):
if self.val.numeric_value == other.val.numeric_value:
if self.suit.suit == other.suit.suit:
return True
elif isinstance(other, Value):
return self.val.numeric_value == other.numeric_value
elif isinstance(other, Suit):
return self.suit.suit == other.suit
return False
@staticmethod
def identifier_to_card(identifier):
suit_str = identifier[0]
val_str = identifier[1:]
suit = Suit(suit_str)
val = Value(val_str)
return suit, val
@staticmethod @staticmethod
def multiple_suit(val, suits): def multiple_suit(val, suits):
@ -265,18 +390,12 @@ class Card(object):
card.val = val card.val = val
yield card yield card
mark = Player("Mark") mark = Player("Mark")
john = Player("John") john = Player("John")
card1 = Card("Spades", "Ace") deck = Deck()
card2 = Card("Diamonds", "Jack") deck.fill()
card4 = Card("Clubs", 3) #deck.shuffle()
card5 = Card("Diamonds", 3)
card6 = Card("Hearts", 4)
deck = Deck(card1, card2, card4, card5, card6)
deck.shuffle()
rules = Rules() rules = Rules()
@ -285,26 +404,57 @@ game.add_players(mark, john)
game.deal() game.deal()
print("Mark's cards:", mark.deck.cards) print("Mark's cards:", mark.hand.cards)
print("John's cards:", john.hand.cards)
print("John's cards:", john.deck.cards) print("--- Beginning game ---")
while 1:
print("=====")
current_player = game.current_player
print(f"Player {current_player}'s go!")
player_cards = current_player.hand.cards
last_player = game.last_player
if last_player:
print(f"Last player: {last_player}")
print(f"Last card is: {game.playdeck.last_card}")
print(f"Last 5 cards: {game.playdeck.last_cards}")
print(f"Your cards: {player_cards}")
text = input("~> ")
parsed_cards = text.split(",")
print("Parsed", parsed_cards)
cards_to_play = []
for card in parsed_cards:
if len(card) == 0:
print("Invalid card")
continue
print("Parsed card iter", card)
try:
card_to_play = Card(card)
print("Card to play", card_to_play)
except InvalidCard:
print("Invalid card:", card)
continue
if card_to_play not in current_player.hand.cards:
print(f"Card is not in your hand: {card_to_play}")
continue
print(f"Attempting to play {card_to_play}")
played = game.play_card(current_player, card_to_play)
if not played:
print("Illegal move!")
continue
if card_to_play == game.rules.change_suit:
suit = None
while not suit:
new_suit = input("Enter a new suit: #> ")
try:
suit = Suit(new_suit)
except InvalidSuit:
print(f"Suit {new_suit} is not valid, please select S, H, D or C")
continue
print(f"Changing last card of {game.playdeck.cards[-1]} to {suit}")
game.playdeck.cards[-1].suit = suit
val1 = Value("Queen") if len(current_player.hand.cards) == 0:
val2 = Value(2) print(f"Player {current_player} wins!")
val3 = Value("Queen") print("Thanks for playing!")
assert val1 > val2 exit()
assert val2 < val1
assert val3 == val1
filter_blackjack = Filter([Suit("Spades"), Suit("Clubs")], Value("Jack"))
blackjack1 = Card("Spades", "Jack")
blackjack2 = Card("Clubs", "Jack")
redjack1 = Card("Diamonds", "Jack")
redjack2 = Card("Hearts", "Jack")
assert blackjack1 == filter_blackjack
assert blackjack2 == filter_blackjack
assert redjack1 != filter_blackjack
assert redjack2 != filter_blackjack