In the BlackJack game, I am trying to catch the summation of the cards' values in hand and print the same. I want to know about the mistake in my code keeping the same logic that I am working with.
I am returning a Tuple from the init_deal() method of the Deck class and calling the same in the next class i.e Hand. Inside add() function of Hand class, I am trying to sum up the values of the Cards to calculate the total value in hand.
import random
suits = ('Hearts', 'Diamonds', 'Spades', 'Clubs')
ranks = ('Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine',
'Ten', 'Jack', 'Queen', 'King', 'Ace')
values = {'Two':2, 'Three':3, 'Four':4, 'Five':5, 'Six':6, 'Seven':7,
'Eight':8, 'Nine':9, 'Ten':10, 'Jack':10, 'Queen':10, 'King':10, 'Ace':11}
class Card:
def __init__(self,suit,rank):
self.suit= suit
self.rank= rank
def __str__(self):
return (f'{self.rank} of {self.suit}')
class Deck:
def __init__(self):
self.deck=[]
self.computer=[]
self.player=[]
for suit in suits:
for rank in ranks:
self.deck.append(Card(suit,rank))
def init_deal(self):
count=1
while count<5:
if count%2==0:
comp=self.deck.pop()
self.computer.append(comp)
count+=1
elif count%2!=0:
playr=self.deck.pop()
self.player.append(playr)
count+=1
else:
break
return (self.computer,self.player)
class Hand(Deck):
def __init__(self):
self.hand=Deck().init_deal()
self.hand_com=self.hand[0]
self.hand_playr=self.hand[1]
self.value_comp = 0
self.value_player = 0
self.val_comp=''
self.val_player=''
self.aces = 0
def add(self):
self.val_comp=' '.join(val.split()[0] for val in self.hand_com)
self.val_player=' '.join(val.split()[0] for val in self.hand_playr)
self.val_comp_lst=self.val_comp.split()
self.val_player_lst=self.val_player.split()
print(self.val_comp_lst)
print(self.val_player_lst)
for val in self.val_comp_lst:
self.value_comp += values[val]
for val in self.val_player_lst:
self.value_player += values[val]
return (self.val_comp,self.val_player)
print(self.value_comp)
print(self.value_player)
def __str__():
pass #Some logic here
x=Deck()
x.init_deal()
y=Hand(x)
y.add()
Lets assume
Deck().init_deal()
returns
(['Two of Hearts','Eight of Diamonds'],['Five of Spades','Six of Diamonds'])
So as per my code's expected result:
self.hand_com=['Two of Hearts','Eight of Diamonds']
self.hand_playr=['Five of Spades','Six of Diamonds']
val_comp_lst=['Two','Eight']
val_player_lst=['Five','Six']
So the ultimate expected result (which actually I am unable to print):
self.value_comp = 10
self.value_player = 11
But now if I run the code I am getting an error as "AttributeError: 'Card' object has no attribute 'split'"
Please help me to understand the mistake I am doing in this code
You could have a dictionary that you use to map the word to the numeric value and then use a list comp and sum to sum up those values:
val_comp_lst=['Two','Eight']
nums = {'One': 1, 'Two': 2 ...}
sum([nums[x] for x in val_comp_lst])
It's probably easiest to add a value property to Card.
class Card:
_values = {1: 11, 11: 10, 12: 10, 13: 10}
def __init__(self, suit, rank):
self.suit= suit
self.rank= rank
def __str__(self):
return (f'{self.rank} of {self.suit}')
#property
def value(self):
# returns the value from _values, or the rank of the card
return self._values.get(self.rank, self.rank)
Then sum the value of the cards in the hand. However be aware that aces are counted as either 11 or 1, so you might have to fudge with those numbers afterwards!
An aside: I'm confused on your class structure. You write class Hand(Deck), but that implies that a Hand is a Deck, which seems unlikely. Are you sure this is what you want?
Related
So I have been stuck on this for a few days I have the deck of cards created and it works but I don't know how I would make each card have a value without using 52 if/elif statements for a BlackJack game. How would i make 10 of spades = 10, 9 of clubs = 9 etc.
import random
#card class to take suit and value self
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
def show(self):
print (f"{self.value} of {self.suit}")
#creates the deck class
class Deck:
def __init__(self):
self.cards = []
self.build()
#appends each suit and each val of the card to give us 52 cards (4 * 13 = 52)
def build(self):
for s in ["Spades", "Clubs", "Diamonds", "Hearts"]:
for v in range (1,14):
if v == 1:
self.cards.append(Card(s,"Ace")) #code that turns 1s into aces
elif v == 11:
self.cards.append(Card(s,"Queen")) #code that turns 11 into queens
elif v == 12:
self.cards.append(Card(s, "King")) #code that turns 12 into kings
elif v == 13:
self.cards.append(Card(s,"Jack")) #code that turns 13 into Jacks
else:
self.cards.append(Card(s,v))
def show(self):
for c in self.cards:
c.show()
#shuffle method using the Fisher Yates Shuffle Algorithm
def shuffle(self):
for i in range(len(self.cards) - 1, 0, -1):
r = random.randint(0, i)
self.cards[i], self.cards[r] = self.cards[r], self.cards[i]
def drawCard(self):
return self.cards.pop()
#dealer class
class Dealer:
global currentCard
def __init__(self, name):
self.name = name
self.hand = []
def draw(self,deck):
self.hand.append(deck.drawCard())
return self
def showHand(self):
for card in self.hand:
currentCard = card.show()
deck = Deck()
deck.shuffle()
dealer = Dealer("Dealer")
dealer.draw(deck)
dealer.showHand()
In term of the value you should first figure out where you want to handle that functionality. In terms of generally naming the cards you could assume them to be ordered in a pattern so that they are defined by their number and use modulo to get the suit and value. For example:
for i in range(0,52)
Card(suit=i//13,val=i%13)
The modulo operator % just gives you "the rest" of a division, so if you operate with whole numbers (no decimal fractions) then 10//3 is 3 and a rest of 1 because 3*3+1 = 10. So if you use modulo 13 (cards in a suit), that should give you a number between 0 and 12 (or 1 and 13 if you add 1) while i//13 rounds down to the nearest whole number which would give you a number between 0 and 3 (or 1 and 4) which can be used for the suits.
And once you have that you can use a dictionary or just a list with suits and card names that translate the numbers to names. Now in terms of values you could either derive them from the card names in a similar fashion but I'd rather deal with them on the level of the game logic as for example and ace could be both a 1 and an 11 (right?) so dealing with that when it's necessary would make more sense than to assign a value at initialization.
Every card needs two attributes - a suit and a value. The value will depend on the type of card game you're "playing". A common approach would be to assign values in the range 2->14
Consider this and note the absence of cumbersome if/else constructs:
import random
class CARD:
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
names = {2: 'Two',
3: 'Three',
4: 'Four',
5: 'Five',
6: 'Six',
7: 'Seven',
8: 'Eight',
9: 'Nine',
10: 'Ten',
11: 'Jack',
12: 'Queen',
13: 'King',
14: 'Ace'}
def __init__(self, suit, value):
self.suit = suit
self.value = value
def __str__(self):
return f'{CARD.names[self.value]} of {self.suit} has a value of {self.value}'
class DECK:
def __init__(self):
self.cards = None
def get_cards(self):
if self.cards is None:
self.cards = [CARD(CARD.suits[n % 4], n % 13 + 2) for n in range(52)]
return self.cards
def get_random_card(self):
return random.choice(self.get_cards())
D = DECK()
# pick some cards at random
for _ in range(5):
print(D.get_random_card())
Output (similar to):
Queen of Spades has a value of 12
Five of Diamonds has a value of 5
Nine of Diamonds has a value of 9
Four of Clubs has a value of 4
Three of Clubs has a value of 3
Problem
I am learning Python on Udemy and wanted to print out the card deck created in the following script.
Reprex
import random
suits = ('Hearts', 'Diamonds', 'Spades', 'Clubs')
ranks = ('Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King', 'Ace')
values = {'Two':2, 'Three':3, 'Four':4, 'Five':5, 'Six':6, 'Seven':7, 'Eight':8,
'Nine':9, 'Ten':10, 'Jack':11, 'Queen':12, 'King':13, 'Ace':14}
class Card:
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
self.value = values[rank]
def __str__(self):
return self.rank + ' of ' + self.suit
class Deck:
def __init__(self):
# Note this only happens once upon creation of a new Deck
self.all_cards = []
for suit in suits:
for rank in ranks:
# This assumes the Card class has already been defined!
self.all_cards.append(Card(suit,rank))
def shuffle(self):
# Note this doesn't return anything
random.shuffle(self.all_cards)
def deal_one(self):
# Note we remove one card from the list of all_cards
return self.all_cards.pop()
Works
The above works when I want to pick out a single card.
print(Deck().all_cards[0])
#Two of Hearts
Error
Yet, it does not work when I try to print out the whole deck (or just enter it).
print(Deck().all_cards)
#[<__main__.Card object at 0x0000028F128D1700>, <__main__.Card object at 0x0000028F128D1280>, #<__main__.Card object at 0x0000028F128D1730>, <__main__.Card object at 0x0000028F128D1D60>,
#...
#...
#<__main__.Card object at 0x0000028F128D9C40>, <__main__.Card object at 0x0000028F128D9C70>]
Attempts
I tried to add a print line to the script, but that didn't work. Any help would be appreciated.
Add __repr__ to your Card class:
class Card:
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
self.value = values[rank]
def __str__(self):
return self.rank + ' of ' + self.suit
def __repr__(self):
return str(self) # return the same thing as `__str__`
I have an assignment to create a deck of cards through the use of a class with methods that deal, shuffle, fan, order and checks if the deck is in order but I am having trouble creating the last one. The deck must be ordered by their values in each suit, and the suits ordered as Clubs, Diamonds, Hearts, and then Spades. My code up to this point is found below:
import random
class Card():
def __init__(self, value, suit):
self.value = value
self.suit = suit
def show_card(self):
print(str(self.value)+ " of " + str(self.suit))
class Deck():
deck_of_cards = []
cards_in_play = []
draw_card = None
def __init__(self):
self.make_deck()
def make_deck(self):
for suit in ['Clubs', 'Diamonds', 'Hearts', 'Spades']:
for value in [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']:
self.deck_of_cards.append(Card(value,suit))
self.cards_in_play.append(Card(value, suit))
def shuffle(self):
self.shuffled_deck = random.shuffle(self.cards_in_play)
def fan(self):
for card in self.cards_in_play:
card.show_card()
def deal(self):
draw_card=self.cards_in_play.pop()
draw_card.show_card()
def order(self):
for suit in ['Club', 'Diamonds', 'Hearts', 'Spades']:
for value in [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']:
self.deck_of_cards.append(Card(value, suit))
self.cards_in_play.append(Card(value, suit))
Some code fixes before we go on:
Universalize the suits and values list so that it can be used by all classes if needed.
Move deck_of_cards and cards_in_play inside the __init__ function. If you do not, this is called a "class attribute" and will make it so every class has that value if not initialized (likely something you do not intend).
class Test:
a = 10
t1 = Test
t2 = Test
t1.a = 11
print(t2.a) # >>> 11
random.shuffle() is a function that runs in place. In other words, it returns None, but modifies the list given to it.
import random
l = ["a", "b", "c"]
print(random.shuffle(l)) # >>> None
print(l) # >>> [b, c, a]
Don't print things -- return them. It will make your code not only clearer, but also more useful. If you want to print something that a function returns, just simply print the return.
def deal(self):
draw_card = self.cards_in_play.pop()
return draw_card
Familiarize yourself with sort and the __lt__ (detail below).
import random
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']
class Card:
def __init__(self, value, suit):
self.suit = suit
self.value = value
def __lt__(self, other):
if suits.index(self.suit) > suits.index(other.suit):
return False
if values.index(self.value) > values.index(other.value):
return False
return True
def __repr__(self):
return f"{self.value} of {self.suit}"
class Deck:
def __init__(self):
self.deck_of_cards = []
self.cards_in_play = []
self.make_deck()
def make_deck(self):
for suit in suits:
for value in values:
self.deck_of_cards.append(Card(value,suit))
self.cards_in_play.append(Card(value, suit))
def shuffle(self):
random.shuffle(self.cards_in_play)
def fan(self):
for card in self.cards_in_play:
print(card)
def deal(self):
draw_card = self.cards_in_play.pop()
return draw_card
def order(self):
self.cards_in_play.sort()
def __repr__(self):
return repr(self.cards_in_play)
Utilizing the magic method __lt__ (less than), we can use the function sort to automatically sort a class. To make the point more clear, notice the following:
# Your new `order` function.
def order(self):
self.cards_in_play.sort()
# Implementing __lt__ now allows us to do the following comparison (which is what sort will use to sort your list of Card objects):
print(Card("Clubs", 2) > Card("Clubs", 3)) # True
# If you notice on docs, __ge__ (greater than), __eq__ (equal than), etc. can also be implemented to give you full flexibility.
Notice I also added the __repr__ function to both Deck and Card so that you can instead more simply do:
card = Card("Clubs", 2)
print(card) # >>> 2 of Clubs
Edit: #Discussion below.
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']
class Card:
def __init__(self, value, suit):
self.suit = suit
self.value = value
def __lt__(self, other):
if suits.index(self.suit) > suits.index(other.suit):
return False
if values.index(self.value) > values.index(other.value):
return False
return True
def __eq__(self, other):
if self.suit == other.suit and self.value == other.value:
return True
else:
return False
def __repr__(self):
return f"{self.value} of {self.suit}"
With the new __eq__ method, we can use the == sign between classes.
c1 = Card(2, "Clubs")
c2 = Card(2, "Diamonds")
c3 = Card(2, "Diamonds")
print(c1 == c2) # False
print(c1 > c2) # False
print(c1 < c2) # True
print(c2 == c3) # True
This allows us to compare the Cards with ease.
You can initialize your Card class as follows:
values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
class Card:
def __init__(self, value, suit):
self.suit = suit
self.value = value
#if value order matters in first place
self.rank = 4*values.index(self.value) + suits.index(self.suit)
#else
self.rank = 13*suits.index(self.suit) + values.index(self.value)
and create the check function as follows:
class Deck:
def check(self):
rank_before = self.cards_in_play[0].rank
for card in self.cards_in_play[1:]:
rank = card.rank
if rank > rank_before:
rank_before = rank
else:
return False
return True
The canonical way to handle this is to realize that you have two identities for the card: its play value and its display value. You need a simple cognate to the __repr__ function.
Very simply, value value with a simple rank integer, card_rank, 0-12. The display value, what you print any time a human refers to it, is handled with a trivial translation list:
print_value = (
'2', '3', '4', '5', '6', '7', '8', '9', '10',
'Jack', 'Queen', 'King', 'Ace'
)
Whenever you need to output a value, you simply use print_value[card_rank]. Put this into the __repr__ function of your Card class.
With this partitioning of functionality, sorting by card_rank solves your problem without further ado.
I want to know what exaclty is the following line of code doing,i would like to receive help to fully understand what is happening in the upper class when i initiate that for loops
import random
suits = ('Hearts', 'Diamonds', 'Spades', 'Clubs')
ranks = ('Two', 'Three', 'Four', 'Five', 'Six', 'Seven','Eight','Nine', 'Ten', 'Jack', 'Queen', 'King', 'Ace')
values = {'Two':2, 'Three':3, 'Four':4, 'Five':5, 'Six':6, 'Seven':7, 'Eight':8, 'Nine':9, 'Ten':10, 'Jack':10,
'Queen':10, 'King':10, 'Ace':11}
playing = True
class Card():
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
def __str__(self):
return "{} of {}".format(self.rank,self.suit)
card = Card(suits,ranks)
class Deck():
def __init__(self):
self.deck = []
for suit in suits:
for rank in ranks:
self.deck.append(Card(suit,rank)) <======= I want to know what is this performing?
def __str__(self):
deck_comp = ""
for card in self.deck:
deck_comp += "\n" + card.__str__()
return "The card is" + deck_comp
def shuffle(self):
random.shuffle(self.deck)
def deal(self):
single_card = self.deck.pop()
return single_card
deck = Deck
You are adding an instance of a Card to a list. This is something you should read about yourself and then ask a more specific question. Explaining it is difficult since we don't know exactly how much you know and the subject is broad. Relevant reading here:
Data Structures
Classes
Several books (or tutorials) define a card and a deck in the following fashion:
import random
class Card(object):
""" A card object with a suit and rank."""
RANKS = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
SUITS = ('Spades', 'Diamonds', 'Hearts', 'Clubs')
def __init__(self, rank, suit):
"""Creates a card with the given rank and suit."""
self.rank = rank
self.suit = suit
def __str__(self):
"""Returns the string representation of a card."""
if self.rank == 1:
rank = 'Ace'
elif self.rank == 11:
rank = 'Jack'
elif self.rank == 12:
rank = 'Queen'
elif self.rank == 13:
rank = 'King'
else:
rank = self.rank
return str(rank) + ' of ' + self.suit
import random
class Deck(object):
""" A deck containing 52 cards."""
def __init__(self):
"""Creates a full deck of cards."""
self._cards = []
for suit in Card.SUITS:
for rank in Card.RANKS:
c = Card(rank, suit)
self._cards.append(c)
def shuffle(self):
"""Shuffles the cards."""
random.shuffle(self._cards)
def deal(self):
"""Removes and returns the top card or None
if the deck is empty."""
if len(self) == 0:
return None
else:
return self._cards.pop(0)
def __len__(self):
"""Returns the number of cards left in the deck."""
return len(self._cards)
def __str__(self):
"""Returns the string representation of a deck."""
result = ''
for c in self._cards:
result = self.result + str(c) + '\n'
return result
A recent book I am reading defines it as:
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
If nothing else, this version “seems” less verbose. (But it is not my concern for this question. As they are, it is wrong to compare the length of the codes.)
For this example, and perhaps in general, what are the pros and cons of defining a card as namedtuple versus class?
If the answer is simply one is mutable and other is not, what are my reasons to care about that?
Is one version more Pythonic than the other?
The named tuple really is only less verbose in that you don't need the boilerplate __init__ method that the class has.
OK, so the implementation you show doesn't have a lengthy __str__ function either, but then again its representation as a string doesn't have the features required of the class version, so it's not reasonable to compare the amounts of code.
The important difference between the two is that namedtuple gives you immutable objects, whereas the class shown above is mutable (and would require extra code to make it immutable).
Extra functions (as khelwood mentions in a comment) can for example be dealt with by combining the two:
class Card(collections.namedtuple('CardBase', ['rank', 'suit'])):
def __str__(self):
# code to say "Ace of spades" goes here
The result still has read-only .rank and .suit attributes, although it does now has its own dictionary for other mutable attributes so it's not really an immutable type any more. If you're intending to mix read-only with read-write attributes then you're probably better off using #property than using namedtuple, but if you just want to stick some convenience functions on something that's otherwise a good fit for namedtuple, then this works.
A final possible disadvantage of using namedtuple is that the result is a tuple. That is to say, it can be accessed using [0] and [1], card objects can be added together using + with meaningless results, and everything else tuples do. It isn't usually actively harmful to have nonsense/irrelevant operations on your objects, but it's not good either because it can bloat your auto-generated documentation, make mistakes harder to find, and other such annoyances. It's also harder to change a published interface with at lot of guff in it, because once you publish it someone might use it.