I have the following code and cant seem to realized why I get this error: Traceback (most recent call last):
card_ranks = [c.rank for c in cards[i:i+5]]
AttributeError: 'str' object has no attribute 'rank'
Here's the code:
import random
import logging
from collections import OrderedDict
from const import __version__, suits, ranks, POS_TOP, POS_BOTTOM
logger = logging.getLogger(__name__)
def f_list(lst, sep=','):
return sep.join([str(x) for x in lst])
def f_lists(lst, sep=' / '):
return f_list(map(f_list, lst), sep)
class Card(object):
"""Represents a single french-design card with it's rank and suit.
Cards can be compared and ordered by rank. A card, relative to
a card of the same rank but different suit, is compared as neither
higher, lower nor equal.
:param rank: Either the rank (one of 'A', 'K', 'Q', 'J', 'T', '9', ... '2')
or rank and suit together (e.g. 'AS', '8H', etc.)
:type rank: str
:param suit: The suit, if not given as one string with rank
(one of 'S', 'H', 'C', 'D' for spade, heart, club or diamond)
:type suit: str
:raises: ValueError
"""
def __init__(self, rank, suit=None):
if suit is None:
suit = rank[1]
rank = rank[0]
if rank not in ranks:
raise ValueError('Card(): Invalid rank')
if suit not in suits:
raise ValueError('Card(): Invalid suit')
self.rank = rank
self.suit = suit
#classmethod
def card_list(cls, *args):
"""Create a list of new cards.
Each argument should describe one card with rank and suit together.
:param args: One or more cards.
:type rank: str
:returns: List of new cards, one for each input parameter.
:rtype: list of :class:`pokercards.cards.Card` objects
:raises: ValueError
"""
lst = []
for c in args:
lst.append(cls(c))
return lst
def __str__(self):
return self.rank + self.suit
def __repr__(self):
return 'Card(%s, %s)' % (self.rank, self.suit)
def __hash__(self):
return (ord(self.rank) << 8) + ord(self.suit)
def __eq__(self, obj):
return self.rank == obj.rank and self.suit == obj.suit
def __ne__(self, obj):
return self.rank != obj.rank or self.suit != obj.suit
def __lt__(self, obj):
return ranks.index(self.rank) > ranks.index(obj.rank)
def __gt__(self, obj):
return ranks.index(self.rank) < ranks.index(obj.rank)
def __le__(self, obj):
return ranks.index(self.rank) >= ranks.index(obj.rank)
def __ge__(self, obj):
return ranks.index(self.rank) <= ranks.index(obj.rank)
class Deck(object):
"""Represents a single deck of 52 :class:`card.Card` objects.
The deck could be imagined face down on a table. All internal lists
represent the cards in order from bottom up. So dealing the top
card means poping last item from the list.
"""
def __init__(self):
self.popped = []
self.discarded = []
self.active = []
for s in suits:
for r in ranks:
self.active.append(Card(r, s))
def shuffle(self):
"""Shuffle the deck."""
random.shuffle(self.active)
def pop(self):
"""Deal the top card from the deck.
:returns: :class:`pokercards.cards.Card` instance
"""
card = self.active.pop()
self.popped.append(card)
return card
def discard(self):
card = self.active.pop()
self.discarded.append(card)
def return_cards(self, cards, pos = POS_BOTTOM):
if pos not in (POS_BOTTOM, POS_TOP):
raise Exception('Deck.return_cards(): invalid pos parameter')
for card in cards[:]:
if card in self.discarded:
self.discarded.remove(card)
elif card in self.popped:
self.popped.remove(card)
else:
raise Exception('Deck.return_cards(): card not among removed cards')
if pos == POS_BOTTOM:
self.active[0:0] = [card]
else:
self.active.append(card)
def return_discarded(self, pos = POS_BOTTOM):
self.return_cards(self.discarded, pos)
def return_popped(self, pos = POS_BOTTOM):
self.return_cards(self.popped, pos)
def return_all(self, pos = POS_BOTTOM):
self.return_popped()
self.return_discarded()
def stats(self):
return (len(self.active), len(self.popped), len(self.discarded))
def __str__(self):
return '[%s]' % ' '.join((str(card) for card in self.active))
def __repr__(self):
return 'Deck(%s)' % self.__str__()
class PokerHand(object):
"""Compute the best hand from given cards, implementing traditional
"high" poker hand ranks.
The hand object can be given more than five cards (as in Texas
Hold'em or similar variants) and the evaluation will pick the best
hand.
Evaluated :class:`pokercards.cards.PokerHand` objects are
compared and sorted by the rank of the hand.
.. attribute:: cards
List of :class:`pokercards.cards.Card` objects to make the hand
from. The :meth:`pokercards.cards.PokerHand.evaluate` method
should be called after manual update to re-evaluate the updated
hand.
Following attributes are available after evaluating the hand.
.. attribute:: hand_rank
Readonly rank of the hand (0 = high card to 8 = straight flush)
.. attribute:: hand_cards
Readonly list of cards which complete the rank.
.. attribute:: kickers
Readonly list of extra cards which can break a tie.
:param cards: List of :class:`pokercards.cards.Card` objects.
:param evaluate: Evaluate the hand when creating.
:type evaluate: bool
"""
def __init__(self, cards, evaluate=True):
cards.sort(reverse=True)
self.cards = cards
if evaluate:
self.evaluate()
def evaluate(self):
"""Evaluate the rank of the hand.
Should be called either implicitly at start by leaving
parameter ``evaluate`` True when creating the hand or
explicitly by calling this method later, e.g. after changing
the :attr:`cards` attribute manually.
"""
self._eval_hand_rank()
self._fill_kickers()
def _by_rank(self, cards=None):
if cards is None:
cards = self.cards
ranked = OrderedDict()
for card in cards:
if card.rank in ranked:
ranked[card.rank].append(card)
else:
ranked[card.rank] = [card]
return ranked
def _by_suit(self, cards=None):
if cards is None:
cards = self.cards
suited = OrderedDict()
for card in cards:
if card.suit in suited:
suited[card.suit].append(card)
else:
suited[card.suit] = [card]
return suited
def _find_flushes(self, cards=None):
if cards is None:
cards = self.cards
flushes = []
for cards in self._by_suit(cards).values():
l = len(cards)
if l >= 5:
for i in xrange(0, l - 4):
flushes.append(cards[i:i+5])
return flushes
def _find_straights(self, cards=None):
if cards is None:
cards = self.cards
straights = []
for i in xrange(0, len(cards) - 4):
card_ranks = [c.rank for c in cards[i:i+5]]
j = ranks.index(card_ranks[0])
if card_ranks == ranks[j:j+5]:
straights.append(cards[i:i+5])
return straights
def _fill_kickers(self):
hand_count = len(self.hand_cards)
kicker_count = 5 - hand_count
if kicker_count > 0:
kickers = self.cards[:]
for card in self.hand_cards:
kickers.remove(card)
self.kickers = kickers[:kicker_count]
else:
self.kickers = []
logger.debug("kickers: %s", f_list(self.kickers))
logger.debug("--- -------------- ---")
def _eval_hand_rank(self):
logger.debug("--- Evaluating %s ---", f_list(self.cards))
straights = self._find_straights()
if straights: logger.debug( "straights: %s", f_lists(straights))
flushes = self._find_flushes()
if flushes: logger.debug("flushes: %s", f_lists(flushes))
pairs = []
threes = []
fours = []
for cards in self._by_rank().values():
l = len(cards)
if l >= 4:
fours.append(cards[0:4])
elif l == 3:
threes.append(cards)
elif l == 2:
pairs.append(cards)
if pairs: logger.debug("pairs: %s", f_lists(pairs))
if threes: logger.debug("threes: %s", f_lists(threes))
if fours: logger.debug("fours: %s", f_lists(fours))
# straight flush
for cards in straights:
if cards in flushes:
self.hand_rank = 8
self.hand_cards = cards
logger.debug("* straight flush: %s", f_list(self.hand_cards))
return
# four of a kind
if len(fours) > 0:
self.hand_rank = 7
self.hand_cards = fours[0]
logger.debug("* four of a kind: %s", f_list(self.hand_cards))
return
# full house
if len(threes) > 1:
self.hand_rank = 6
self.hand_cards = threes[0] + threes[1][:2]
logger.debug("* full house: %s", f_list(self.hand_cards))
return
elif len(threes) == 1 and len(pairs) > 0:
self.hand_rank = 6
self.hand_cards = threes[0] + pairs[0]
logger.debug("* full house: %s", f_list(self.hand_cards))
return
# flush
if len(flushes) > 0:
self.hand_rank = 5
self.hand_cards = flushes[0]
logger.debug("* flush: %s", f_list(self.hand_cards))
return
# straight
if len(straights) > 0:
self.hand_rank = 4
self.hand_cards = straights[0]
logger.debug("* straight: %s", f_list(self.hand_cards))
return
# three of a kind
if len(threes) > 0:
self.hand_rank = 3
self.hand_cards = threes[0]
logger.debug("* three of a kind: %s", f_list(self.hand_cards))
return
# two pair
if len(pairs) > 1:
self.hand_rank = 2
self.hand_cards = pairs[0] + pairs[1]
logger.debug("* two pairs: %s", f_list(self.hand_cards))
return
# one pair
if len(pairs) == 1:
self.hand_rank = 1
self.hand_cards = pairs[0];
logger.debug("* two of a kind: %s", f_list(self.hand_cards))
return
# high card
self.hand_rank = 0
self.hand_cards = [self.cards[0]]
logger.debug("* high card: %s", f_list(self.hand_cards))
def __str__(self):
return '[%s]' % f_list(self.cards)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self.__str__())
def __cmp__(self, other):
if self.hand_rank > other.hand_rank:
return 1
elif self.hand_rank < other.hand_rank:
return -1
else:
# same rank
for c1, c2 in zip(self.hand_cards, other.hand_cards):
if c1 > c2:
return 1
elif c1 < c2:
return -1
else:
# same cards, check kickers
for c1, c2 in zip(self.kickers, other.kickers):
if c1 > c2:
return 1
elif c1 < c2:
return -1
# really, a tie
return 0
deck = Deck()
deck.shuffle()
player = []
computer = []
cmoney = 500
money = 500
pot = 0
for i in range(5):
card = deck.pop()
player.append(str(card))
player.sort()
for i in range(5):
card = deck.pop()
computer.append(str(card))
computer.sort()
pot += 10
money -= 5
cmoney -= 5
print "All players have anted $5."
print "Your Hand:", player
print "Your Money: $" + str(money)
print "Pot: $" + str(pot)
discard = []
ask = raw_input("Bet or check? ")
if ask.lower() == "bet":
ask2 = int(raw_input("How much? "))
if ask2 > money:
print "You don't have that much money!"
else:
money -= ask2
cmoney -= ask2
pot += ask2 * 2
print("Pot: $" + str(pot))
ui = raw_input("Do you want to discard some cards? ")
if ui.lower() == "yes":
ui2 = int(raw_input("How many? "))
for i in range(ui2):
ui3 = raw_input("Card " + str(i + 1) + ": ")
discard.append(ui3)
player.pop()
for i in range(ui2):
card = deck.pop()
player.append(str(card))
print "Your Hand:", player
print "Your Money: $" + str(money)
ask3 = raw_input("Bet or check? ")
if ask3.lower() == "bet":
ask4 = int(raw_input("How much? "))
if ask4 > money:
print "You don't have that much money!"
else:
money -= ask4
pot += ask4
pval = PokerHand(player, True)
cval = PokerHand(computer, True)
if pval > cval:
print "YOU WIN!"
money += pot
pot = 0
else:
print "YOU LOSE!"
cmoney += pot
pot = 0
print "Pot: $" + str(pot)
print "Your Hand:", player
print "Computer\'s Hand:", computer
You are constructing the PokerHand with an array of strings, not an array of cards. The code is here:
for i in range(5):
card = deck.pop()
player.append(str(card))
player.sort()
for i in range(5):
card = deck.pop()
computer.append(str(card))
computer.sort()
Instead of str(card), try just using append(card). Hopefully that should work, unless you use the string elsewhere.
Related
So I know this code is a mess. I am trying to get the base code without importing any other libraries other than random.
def deckToShuffle(name):
print(f'''{'-'*40}
SHUFFLE {name.upper()}
{'-'*40}''')
def getKey(val,dict):
for key, value in dict.items():
if val == value:
return key
return "key doesn't exist"
class JOKER:
def __init__(self, rank):
self.rank = rank
self.name = 'Joker'
def printCard(self):
print(f'{self.rank}')
class CARD:
def __init__(self, suit, rank):
self.name = rank + ' of ' + suit
self.suit = suit
self.rank = rank
def printCard(self):
print(f'{self.rank} of {self.suit}')
class DECK:
def __init__(self, name, jokers = False):
deck = []
self.name = name
suits = ['Diamonds', 'Clubs', 'Hearts', 'Spades']
ranks = ['Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten','Jack','Queen','King','Ace']
self.suits = suits
self.ranks = ranks
self.deck = deck
self.jokers = jokers
for suit in self.suits:
for rank in self.ranks:
self.deck.append(CARD(suit , rank))
if self.jokers:
self.deck.append(JOKER('Joker'))
self.deck.append(JOKER('Joker'))
def printDeck(self):
for card in self.deck:
card.printCard()
def shuffle(self):
random.shuffle(self.deck)
deckToShuffle(self.name)
def randomSplit(self,num):
splitToDeck = 1
random.shuffle(self.deck)
dct = {}
for n in range(1,num+1):
dct['pl_%s' % n] = []
while len(self.deck) != 36:
if splitToDeck == (num+1):
splitToDeck = 1
card = self.deck.pop(-1)
d = dct['pl_%s' % splitToDeck]
d.append(card)
splitToDeck += 1
self.dct = dct
def printHand(self, hand):
print('='*40)
print(f">>> Player {hand}'s Hand")
for card in self.dct['pl_%s' % hand]:
print(card.name)
def compareTopCard(self):
top = {}
winner = []
for cards in self.dct:
top[cards] = []
t = self.dct[cards]
if t[0].rank == 'Joker':
t = 100
else:
t = self.ranks.index(t[0].rank)
top[cards].append(t)
print(top)
for tops in top:
if winner == []:
winner = top[tops]
elif top[tops] == [100]:
winner = [100]
elif top[tops] > winner:
winner = top[tops]
elif top[tops] == winner:
winner = [100]
if winner == [100]:
print ("WAR")
else:
player = getKey(winner,top)
playerNum = player[3:]
print (playerNum)
warDeck = DECK('War Deck', True)
warDeck.randomSplit(10)
#warDeck.printHand(1)
#warDeck.printHand(2)
print('='*40)
warDeck.compareTopCard()
and here is the error I get
Traceback (most recent call last):
File "main.py", line 99, in <module>
warDeck.compareTopCard()
File "main.py", line 83, in compareTopCard
elif top[tops] > winner:
TypeError: '>' not supported between instances of 'list' and 'int'
I thought I didn't have any list/int instances that would cause this error.
elif top[tops] > winner:
TypeError: '>' not supported between instances of 'list' and 'int'
So, what the Traceback is saying is that top[tops] is a list, while winner is an int.
I'm trying to make a simplified version of the card game War. In this game, there are two players. Each starts with half of a deck. The players each deal the top card from their decks and whoever has the higher card wins the other player's cards and adds them to the bottom of his deck. If there is a tie, the two cards are eliminated from play. The game ends when one player runs out of cards.
However, I'm having an issue with the pop argument, in that it gives me an error with "pop from empty list".
How can I fix that?
import random
class Card:
def __init__(self, value, suit):
self.value = value
self.suit = suit
def __str__(self):
names = ['Jack', 'Queen', 'King', 'Ace']
if self.value <= 10:
return '{} of {}'.format(self.value, self.suit)
else:
return '{} of {}'.format(names[self.value-11], self.suit)
class CardGroup:
def __init__(self, cards = []):
self.cards = cards
def shuffle(self):
random.shuffle(self.cards)
class StandardDeck(CardGroup):
def __init__(self):
self.cards = []
for s in ['Hearts', 'Diamonds', 'Clubs', 'Spades']:
for v in range(2,15):
self.cards.append(Card(v, s))
def deal_out(self, num_cards, num_players):
deal = [[0 for x in range(num_cards)] for y in range(num_players)]
for i in range(num_cards):
for k in range(num_players):
deal[k][i] = self.cards.pop()
self.deal = deal
deck = StandardDeck()
deck.shuffle()
print("\n===== shuffled deck =====\n")
player1_list = []
player2_list = []
for i in range(26):
p1_temp = deck.deal_out(26, 2)
player1_list.append(p1_temp)
p2_temp = deck.deal_out(26, 2)
if (p2_temp.__init__() == 1):
player1_list.append(p2_temp)
player2_list.append(player1_list.pop(0))
else:
player2_list.append(p2_temp)
# Card dealt to Player #1
player1_card = player1_list.pop(0)
print("===== player #1 =====")
print("Card dealt to player #1: \n", player1_card)
print(player1_list)
#Card dealt to Player #2
player2_card = player2_list.pop(0)
print("\n===== player #2 =====")
print("Card dealt to player #2: \n", player2_card)
print(player2_list)
# Compare the two cards using overloaded operators
if player1_card == player2_card:
print("Tie: ", player1_card, "and", player2_card,\
"are of equal rank")
elif player1_card > player2_card:
print("Player #1 wins: ", player1_card, \
"is of higher rank than", player2_card)
else:
print("Player #2 wins: ", player2_card, \
"is of higher rank than", player1_card)
print()
Rather than self.deal = deal in deal_out() you likely want to return the deal.
If you do, some code related to for i in range(26): can be removed as your player hands get populated from deal_out(). You also have some issues of comparing cards when you want to compare card values.
Here are some slight modifications to get you going again:
import random
class Card:
def __init__(self, value, suit):
self.value = value
self.suit = suit
def __str__(self):
names = ['Jack', 'Queen', 'King', 'Ace']
if self.value <= 10:
return '{} of {}'.format(self.value, self.suit)
else:
return '{} of {}'.format(names[self.value-11], self.suit)
class CardGroup:
def __init__(self, cards = []):
self.cards = cards
def shuffle(self):
random.shuffle(self.cards)
class StandardDeck(CardGroup):
def __init__(self):
self.cards = []
for s in ['Hearts', 'Diamonds', 'Clubs', 'Spades']:
for v in range(2,15):
self.cards.append(Card(v, s))
def deal_out(self, num_cards, num_players):
deal = [[0 for x in range(num_cards)] for y in range(num_players)]
for i in range(num_cards):
for k in range(num_players):
deal[k][i] = self.cards.pop()
## self.deal = deal
return deal
deck = StandardDeck()
deck.shuffle()
print("\n===== shuffled deck =====\n")
'''
player1_list = []
player2_list = []
for i in range(26):
p1_temp = deck.deal_out(26, 2)
player1_list.append(p1_temp)
p2_temp = deck.deal_out(26, 2)
if (p2_temp.__init__() == 1):
player1_list.append(p2_temp)
player2_list.append(player1_list.pop(0))
else:
player2_list.append(p2_temp)
'''
player1_list, player2_list = deck.deal_out(26, 2)
# Card dealt to Player #1
player1_card = player1_list.pop(0)
print("===== player #1 =====")
print("Card dealt to player #1: \n", player1_card)
#print(player1_list)
#Card dealt to Player #2
player2_card = player2_list.pop(0)
print("\n===== player #2 =====")
print("Card dealt to player #2: \n", player2_card)
#print(player2_list)
# Compare the two cards using overloaded operators
if player1_card.value == player2_card.value:
print("Tie: ", player1_card, "and", player2_card,\
"are of equal rank")
elif player1_card.value > player2_card.value:
print("Player #1 wins: ", player1_card, \
"is of higher rank than", player2_card)
else:
print("Player #2 wins: ", player2_card, \
"is of higher rank than", player1_card)
print()
I am in need of help with an assignment I have been given.
I have been asked to modify the section below from a blackjack game so that a dealer deals a card to each player and the highest card wins unless there is a draw.
I am unable to get the code right for this.
This is what i have in place:
for player in self.still_playing:
if player.total > self.players.total:
player.win()
elif player.total < self.players.total:
player.lose()
else:
player.push()
here is the rest of the code:
import Cards, Games
class BJ_Card(Cards.Card):
# Defines a Blackjack card
ACE_VALUE = 1
#property
def value(self):
if self.is_face_up:
val = BJ_Card.CARDS.index(self.card) + 1
if val > 10:
val = 10
else:
val = None
return val
# This object returns a number between 1 and 10,
# representing the value of a Blackjack card
class BJ_Deck(Cards.Deck):
# Defines a Blackjack deck
def populate(self):
for suit in BJ_Card.SUITS:
for card in BJ_Card.CARDS:
self.cards.append(BJ_Card(card, suit))
class BJ_Hand(Cards.Hand):
# Defines a Blackjack hand
def __init__(self, name):
super(BJ_Hand, self).__init__()
self.name = name
def __str__(self):
rep = self.name + ":\t" + super(BJ_Hand, self).__str__()
if self.total:
rep += "(" + str(self.total) + ")"
return rep
#property
def total(self):
# If a card has the value None, then total is None
for card in self.cards:
if not card.value:
return None
# Add card values
t = 0
for card in self.cards:
t += card.value
# Check if hand contains an Ace
contains_ace = False
for card in self.cards:
if card.value == BJ_Card.ACE_VALUE:
contains_ace = True
# treat Ace as 1
contains_ace = 1
return t
def is_busted(self):
return self.total > 21
class BJ_Player(BJ_Hand):
# Defines a Blackjack player
def is_hitting(self):
response = Games.askYesNo("\n" + self.name + ", do you want another
card? (Y/N): ")
return response == "y"
def bust(self):
print(self.name, "busts.")
self.lose()
def lose(self):
print(self.name, "loses.")
def win(self):
print(self.name, "wins.")
def push(self):
print(self.name, "draws.")
class BJ_Dealer(BJ_Hand):
# Defines a Blackjack dealer
def is_hitting(self):
return self.total < 17
def bust(self):
print(self.name, "busts.")
def flip_first_card(self):
first_card = self.cards[0]
first_card.flip()
class BJ_Game(object):
# Defines a Blackjack game
def __init__(self, names):
self.players = []
for name in names:
player = BJ_Player(name)
self.players.append(player)
self.dealer = BJ_Dealer("Dealer")
self.deck = BJ_Deck()
self.deck.populate()
self.deck.shuffle()
#property
def still_playing(self):
sp = []
for player in self.players:
if not player.is_busted():
sp.append(player)
return sp
def __additional_cards(self, player):
while not player.is_busted() and player.is_hitting():
self.deck.deal([player])
print(player)
if player.is_busted():
player.bust()
def play(self):
# Deal initial 1 card to all players
self.deck.deal(self.players, per_hand = 1)
for player in self.players:
print(player)
for player in self.still_playing:
if player.total > self.players.total:
player.win()
elif player.total < self.players.total:
player.lose()
else:
player.push()
# Remove everyone's cards
for player in self.players:
player.clear()
def main():
print("\nWelcome to the Python Blackjack game.\n")
names = []
number = Games.askForNumber("How many players? (2-7): ", low = 2, high =
8)
print()
i = 1
for i in range(number):
name = input("Enter player name: ")
if name == "":
names.append("Anon")
print()
i += 1
else:
names.append(name)
print()
i += 1
game = BJ_Game(names)
again = "Y"
while again == "y" or again == "Y":
game.play()
again = Games.askYesNo("\nDo you want to play again?: ")
main()
self.players
is a list of BJ_Player classes / objects. You're calling self.players.total i.e trying to get the property total of a python list which does not exist as this is not a property of a list. I assume you're trying to do something more like;
for player in self.players:
print(player.total)
this way you would be accessing each players total. However, the class BJ_Player does not appear to have a total property either, so you will need to add this to the class and then use a loop like the one given above.
I am trying to create a simple card game to better understand OOP and classes.
The game is as follows: Two cards are dealt from a deck. Then a third card is dealt. If the third card is between the first two cards, then the player wins. If the third card is equal to either of the first two cards, or is outside of the set, then the player loses.
This is what I have so far:
class Deck(object):
def __init__(self):
self.deck = []
def PopulateDeck(self):
suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
for suit in suits:
for rank in range(2, 15):
if rank == 11:
value = "Jack"
elif rank == 12:
value = "Queen"
elif rank == 13:
value = "King"
elif rank == 14:
value = "Ace"
self.deck.append(str(value) + " of " + suit)
class Card(object):
def __init__(self, rank, value):
self.rank = rank
self.value = value
self.card = self.rank + self.value
I am having a difficult time with classes and OOP, and I'm not sure if this is a good start, or where I should go next. Much of this was created by reading other sources and examples. Can anyone please provide advice on what other classes I may want to make to run my game, and how those classes may interact with/inherit from each other? Thank you.
This is more of a code/approach review. A card game is a case for composition, not inheritance; the Deck contains Cards, but isn't in itself a type of Card, and vice versa.
I think you are duplicating information in the Card. Just store suit and rank, and use __str__ to create 'x of y'. You can also implement the rich comparison methods:
class Card(object):
FACES = {11: 'Jack', 12: 'Queen', 13: 'King', 14: 'Ace'}
def __init__(self, rank, suit):
self.suit = suit
self.rank = rank
def __str__(self):
value = self.FACES.get(self.rank, self.rank)
return "{0} of {1}".format(value, self.suit)
def __lt__(self, other):
return self.rank < other.rank
Now e.g. str(Card(13, 'Clubs')) == "King of Clubs". This way you don't duplicate the rank and value in card.
Next, I think the Deck should incorporate the population generation in __init__; you can provide optional arguments for a non-standard deck. I have included two implementations; the commented-out version is a list comprehension using itertools to do the same job in one line. I also provide a function to pick n different random cards from self.deck.
from itertools import product
import random
class Deck(object):
def __init__(self, ranks=None, suits=None):
if ranks is None:
ranks = range(2, 15)
if suits is None:
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
## self.deck = [Card(r, s) for r, s in product(ranks, suits)]
self.deck = []
for r in ranks:
for s in suits:
self.deck.append(Card(r, s))
def deal(self, n):
return random.sample(self.deck, n)
Now the game is simple; you can deal three cards per hand, and compare the cards naturally (using e.g. <) because of the comparison methods.
deck = Deck()
hand = deck.deal(3)
print(" - ".join(map(str, hand)))
if min(hand[0], hand[1]) < hand[2] < max(hand[0], hand[1]):
print("Winner!")
else:
print("Loser.")
As #tobias_k have pointed out in the comments + some of my thoughts
class Deck(object):
def __init__(self):
self.deck = []
self.dealt = [] #Prevents from dealing the same card
def PopulateDeck(self):
suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
for suit in suits:
for rank in range(2, 15):
if rank == 11:
value = "Jack"
elif rank == 12:
value = "Queen"
elif rank == 13:
value = "King"
elif rank == 14:
value = "Ace"
else:
value = str(rank)
self.deck.append(Card(value, suit))
def deal(self):
#Randomly select card
remaining_cards = [card for card in self.deck if card not in self.dealt]
card_index = random.randrange(0, len(remaining_cards)-1)
card = remaining_cards[card_index]
self.dealt.append(card)
return card
def shuffle(self):
self.dealt = []
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
self.card = str(self.rank) + " " + str(self.suit)
def __eq__(self, other):
return self.rank == other.rank and self.suit == other.suit
def play():
deck = Deck()
card1 = deck.deal()
card2 = deck.deal()
card3 = deck.deal()
#And here you will compare the cards to see if the player wins or not. Not sure
#what exact criterion you're using.
deck.shuffle() #And leave your deck nicely shuffled for next game
play()
Random documentation
I have not run this code, and it likely contains errors. But it is an illustration of what you can do.
I am also learning opps using card games as i find that is most easy way to understand the concepts. I have come accross this code sometime back, but not sure about the link. I am pesting this code with few usefull comments. This may be helpful.
from random import shuffle
class Card:
suits = ['spades', 'hearts', 'diamonds', 'clubs']
values = [None, None, '2','3','4','5','6','7','8','9','10','Jack', 'Queen', 'King', 'Ace']
def __init__(self, v, s):
'''suits + values are ints'''
self.value = v
self.suit = s
def __lt__(self, c2): # allows to compare two objects cards in this case
if self.value < c2.value:
return True
if self.value == c2.value: # value is what user is putting while creating object of class
if self.suit < c2.suit: # c2 is with what we are comapring
return True
else:
return False
return False
def __gt__(self, c2): # allows to compare two objects cards in this case,
if self.value > c2.value: # c2 is with what we are comapring
return True
if self.value == c2.value:
if self.suit > c2.suit: # suits comes into picture if values are same suit number is given importance
return True
else:
return False
return False
def __repr__(self):
v = self.values[self.value] + ' of ' + self.suits[self.suit]
return v
# defining the class which represent the deck of card
class Deck:
def __init__(self):
self.cards = []
for i in range(2,15):
for j in range(4):
self.cards.append(Card(i,j))
shuffle(self.cards)
def rm_card(self):
if len(self.cards) == 0:
return # if block it return to None object when condition is satisfied
return self.cards.pop()
class Player:
def __init__(self, name):
self.name = name # name of the player
self.card = None # current card player holding
self.wins = 0
class Game:
def __init__(self):
name1 = input('p1 name ')
name2 = input('p2 name ')
self.deck = Deck()
self.p1 = Player(name1)
self.p2 = Player(name2)
def wins(self, winner):
w = "{} wins this round"
w = w.format(winner)
print(w)
def draw(self, p1n, p1c, p2n, p2c):
d = "{} drew {} {} drew {}"
d = d.format(p1n, p1c, p2n, p2c)
print(d)
def play_game(self):
cards = self.deck.cards # self.deck = Deck() and cards = self.deck.cards is list of card
print('Beginning War!!!')
while len(cards) > 2:
m = 'q to quit. Any' + ' key to play'
response = input(m)
if response == 'q':
break
p1c = self.deck.rm_card() # removing first card from deck by player 1
p2c = self.deck.rm_card() # removing second card from deck by player 2
p1n = self.p1.name
p2n = self.p2.name
self.draw(p1n, p1c, p2n, p2c)
if p1c > p2c:
self.p1.wins +=1
self.wins(self.p1.name)
else:
self.p2.wins += 1
self.wins(self.p2.name)
win = self.winner(self.p1, self.p2)
print("War is over. {} wins".format(win))
def winner(self, p1, p2):
if p1.wins > p2.wins:
return p1.name
if p1.wins < p2.wins:
return p2.name
return 'Its Tie!'
game = Game()
game.play_game()
Source: https://thecleverprogrammer.com/2020/10/04/card-game-with-python/
Below is part of my code. Basically what this does is when run it creates credit cards with a random number and a given currency code and credit card limit. The Credit Card is a class and the money it stores is also a class (Which I have not included here for brevity and because I think they are not relevant to my question.) What happens in my code, however, is that my cancel statement works out fine but if I have two credit cards in a list and I try to cancel the second one it will again print out NO_SUCH_CARD. The card gets deleted anyway if its in the list. My guess is that his happens because the for loop iterates through the list and it first detects a card whose number is different than the given one, which is why it prints no such card, but I have no idea how to fix this. Help would be appreciated.
PATH = 'saved_cards.txt'
creditcard_list = []
import decimal
import ast
import collections
import os
import random
def currency_dictionary():
'''Initialized at the start of the program. It finds and reads a currency.txt
file and stores those in a dictionary'''
final_dict = collections.defaultdict(list)
with open("currency.txt", 'r') as f:
currency_lines = f.readlines()
for item in currency_lines:
m = item.split(' ')
final_dict[m[0]] = [int(m[1]), decimal.Decimal(m[2])]
return final_dict
class Money():
def __init__(self, money_amount: decimal, currency_code: str):
self.m = decimal.Decimal(money_amount)
self.c = str(currency_code)
self.d = currency_dictionary()
def __repr__(self):
return 'Money({}, {})'.format(self.m, self.c)
def __eq__(self, other):
if type(other) != Money:
return False
elif self.c == other.c:
return self.m == other.m
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor = dictionary_key2[1]
y = other.m / conversion_factor
return x == y
def __ne__(self, other):
if type(other) != Money:
return True
elif self.c == other.c:
return self.m != other.m
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor = dictionary_key2[1]
y = other.m / conversion_factor
return x != y
def __add__(self, other):
if self.c == other.c:
return Money((self.m + other.m), self.c)
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor2 = dictionary_key2[1]
y = other.m / conversion_factor2
total = x + y
return Money((total * conversion_factor1), self.c)
def __sub__(self, other):
if self.c == other.c:
return Money((self.m - other.m), self.c)
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor2 = dictionary_key2[1]
y = other.m / conversion_factor2
total = x - y
return Money((total * conversion_factor1), self.c)
class Credit_Card():
def __init__(self, card_number, money_amount: Money, card_limit: int):
if type(money_amount) != Money or type(card_limit) != int:
raise TypeError('one of the types of the parameters entered is not valid')
self.number = card_number
self.amount = money_amount
self.limit = card_limit
def __repr__(self):
return 'Card#{}({}, {})'.format(self.number, self.amount, self.limit)
def user_interface():
boolean = True
while boolean:
temp_list = []
command = input()
if command.split()[0] == 'ISSUE':
if len(command.split()) == 3:
x = "%0.5d" % random.randint(0,99999)
currency_code = command.split()[1]
card_limit = int(command.split()[2])
if card_limit < 0:
print("NEGATIVE_LIMIT")
elif not currency_dictionary()[currency_code]:
print("NO_SUCH_CURRENCY")
else:
for card in creditcard_list:
temp_list.append(card.number)
if x not in temp_list and currency_dictionary()[currency_code]:
creditcard_list.append(Credit_Card(x, Money(0, currency_code), card_limit))
print('ISSUED', x)
print(creditcard_list)
else:
print("INVALID_ISSUE_COMMAND")
elif command.split()[0] == 'CANCEL':
templist2 = []
if len(command.split()) == 2:
card_number = command.split()[1]
for card in creditcard_list:
templist2.append(card)
for i, card in enumerate(templist2):
if card_number not in templist2[i].number:
print('NO_SUCH_CARD')
elif templist2[i].amount.m != 0:
print('NONZERO_BALANCE')
elif templist2[i].number == command.split()[1] and card.amount.m == 0:
del creditcard_list[i]
print('CANCELLED', card_number)
print(creditcard_list)
elif command.split()[0] == 'PURCHASE':
if len(command.split()) == 4:
card_number = command.split()[1]
currency_code = command.split()[2]
amount = int(command.split()[3])
if currency_code not in currency_dictionary().keys():
print('NO_SUCH_CURRENCY')
elif amount < 0:
print('NONPOSITIVE_AMOUNT')
else:
for i, card in enumerate(creditcard_list):
if card.number == card_number and 0 <= amount <= card.limit :
x = card.amount + Money(amount, currency_code)
creditcard_list[i] = Credit_Card(card.number, x, card.limit)
elif creditcard_list[i].number != card_number:
print('NO_SUCH_CARD')
elif amount > creditcard_list[i].limit:
print('OVER_LIMIT')
elif command.split(0) == 'PAYMENT':
print(creditcard_list)
if __name__ == '__main__':
user_interface()
My output for the cancel command basically looks like this, and I'm pretty sure once I figure this out I'll be able to deal with the rest. Bold is input, non-bold is output.
**ISSUE USD 5000**
ISSUED 50695
[Card#50695(Money(0, USD), 5000)]
**ISSUE RON 3000**
ISSUED 25282
[Card#50695(Money(0, USD), 5000), Card#25282(Money(0, RON), 3000)]
**CANCEL 25282**
[Card#50695(Money(0, USD), 5000), Card#25282(Money(0, RON), 3000)]
*NO_SUCH_CARD*
CANCELLED 25282
[Card#50695(Money(0, USD), 5000)]
Note that the lists are only printed out for me to keep track of what cards are currently in the main list, and I will eventually remove those print statements. I have italicized the output I'm having a problem with.
The problem appears to be that you're modifying creditcard_list at the same time you are iterating through it. You should first create a temporary copy of the list to iterate through while later removing items from the actual list.
Edit: Ahh, it's printing "NO_SUCH_CARD" on the first card in the list! Not on the second. So in your example, you loop through a list of two; it first visits card #50695, which is not equal to 2582, so it prints "NO_SUCH_CARD". Then it visits 25282, deletes it, and prints "CANCELED". It's doing exactly what you wrote it to do. Instead, just loop through, deleting the card if you find it and silently skipping any card that doesn't match. In the end, if the card is not found print "NO_SUCH_CARD"
Edit2: Here's an example:
found = False
for card in templist2:
if card_number == card.number:
if card.amount.m != 0:
print('NONZERO_BALANCE')
else:
del creditcard_list[i]
print('CANCELLED', card_number)
found = True
break
if not found:
print('NO_SUCH_CARD')