I'm learning Python 2 and I'm trying to create a blackjack game using OOP. Can someone please let me know why I can't do hand.hit() twice after running the code below? It only works once. Why?
Also, can someone please let me know how I can calculate the actual value of a player's hand?
Thanks!
import random
rank = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace']
suit = ['Diamonds', 'Clubs', 'Hearts', 'Spade']
card_val = {'2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, '10':10, 'Jack':10, 'Queen':10, 'King':10, 'Ace':1}
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __str__(self):
return str(self.rank) + ' of ' + str(self.suit)
def grab_suit(self):
return self.suit
def grab_rank(self):
return self.rank
def draw(self):
print(self.suit + self.rank)
class Deck(object):
def __init__(self):
self.cards = []
for i in rank:
for j in suit:
self.cards.append(Card(i,j))
def __str__(self):
return str([str(card) for card in self.cards])
def shuffle(self):
random.shuffle(self.cards)
def deal(self):
single_card = self.cards.pop()
return single_card
deck = Deck()
class PlayerHand(object):
def __init__(self):
self.value = []
def __str__(self):
return str([str(card) for card in self.value])
def hit(self):
self.hit = self.value.append(deck.deal())
return self.value
hand = PlayerHand()
Well the answer to the first question is pretty easy: You are overwriting your method with "None" (since self.value.append does not return anything).
So you need to change the line:
self.hit = self.value.append(deck.deal())
To
self.value.append(deck.deal())
Calculating the actual value of the hand has been asked already on stackoverflow, therefore I will only answer the first part of the question.
Related
This question already has answers here:
What is the purpose of the return statement? How is it different from printing?
(15 answers)
How to print instances of a class using print()?
(12 answers)
Closed 6 months ago.
I copied a card game code online to Python and I'm trying to recreate the popular card game of War.
import random
class Card(object):
def __init__(self, suit, val):
self.suit = suit
self.value = val
def show(self):
print("{} of {}".format(self.value, self.suit))
class Deck:
def __init__(self):
self.cards = []
self.build()
def build(self):
for s in ["Spades", "Clubs", "Diamonds", "Hearts"]:
for v in {'2' : 0, '3' : 1, '4' : 2, '5' : 3, '6' : 4, '7' : 5, '8' : 6, '9' : 7 , '10' : 8, 'Jack' : 9, 'Queen' : 10, 'King' : 11, 'Ace' : 12}:
self.cards.append(Card(s,v))
def show(self):
for c in self.cards:
c.show()
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()
class Player:
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:
print(self.name, "drew a ", card.show() , "!")
deck = Deck()
deck.shuffle()
peter = Player('Peter')
peter.draw(deck)
jessica = Player('Jessica')
jessica.draw(deck)
peter.showHand()
jessica.showHand()
In the Player.show_hand() method I want to print (name object) draws a (value of suit). However, instead, it prints the suit of value first, and then it says Player draws a None !. It kind of goes like this:
9 of Clubs
Peter drew a None!
It's mostly a simple error, but I want it so that the suit of value gets printed alongside the player, like this:
Peter drew a 9 of Clubs!
This is the method that results in a runtime error:
def showHand(self):
for card in self.hand:
print(self.name, "drew a ", card.show() , "!")
Your function Card.show(), prints and returns None.
I would implement a string dunder for the Card class as shown here. Then I would simply format print the Card class down below in the Player.showHand() function which will call the Card.__str__() dunder automatically.
import random
class Card(object):
def __init__(self, suit, val):
self.suit = suit
self.value = val
# this decides what string to return when using this class as a string.
def __str__(self):
return "{} of {}".format(self.value, self.suit)
def show(self):
print("{} of {}".format(self.value, self.suit))
class Deck:
def __init__(self):
self.cards = []
self.build()
def build(self):
for s in ["Spades", "Clubs", "Diamonds", "Hearts"]:
for v in {'2' : 0, '3' : 1, '4' : 2, '5' : 3, '6' : 4, '7' : 5, '8' : 6, '9' : 7 , '10' : 8, 'Jack' : 9, 'Queen' : 10, 'King' : 11, 'Ace' : 12}:
self.cards.append(Card(s,v))
def show(self):
for c in self.cards:
c.show()
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()
class Player:
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:
# This line was changed:
print(f"{self.name} drew a {card}!")
deck = Deck()
deck.shuffle()
peter = Player('Peter')
peter.draw(deck)
jessica = Player('Jessica')
jessica.draw(deck)
peter.showHand()
jessica.showHand()
Execution:
[ttucker#zim stackoverflow]$ python war.py
Peter drew a 10 of Clubs!
Jessica drew a 10 of Hearts
Okay so i'm having issues with comparing a user inputted value with that already printed into the command line.
The printing to the command line is controlled by the classes that are called and i believe that it's causing issues with the process!
fairly sure i need to use def but i'm new to all of this so i don't know where to start!
Thanks for any help ahead of time! Not sure if this is the best way to go about creating a card game as it's pretty convoluted let me know your thoughts!
Originally tried to convert the hand of cards (bob.showHand) into a string but because it's being generated by a class it doesnt work? It's not much code so i've dumped it all below hope that's fine!
print('Running')
import random
class Turn:
def __init__(self, start):
self.start = start
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
def show(self):
print('{} of {}'.format(self.value, self.suit))
class Deck:
def __init__(self):
self.cards = []
self.build()
def build(self):
for s in ['Spades', 'Clubs', 'Diamonds', 'Hearts']:
for v in range(1,14):
self.cards.append(Card(s, v))
def show(self):
for c in self.cards:
c.show()
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()
def draw(self, deck):
self.hand.append(deck,drawCard())
return self
class Player1:
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:
card.show()
class Player2:
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:
card.show()
print('generating deck')
deck = Deck()
print('shuffling deck')
deck.shuffle()
bob = Player1('bob')
ryan = Player2('ryan')
print('dealing deck')
for x in range(7):
bob.draw(deck)
for y in range(7):
ryan.draw(deck)
Turn.start = 0
if Turn.start == 0:
print('bob to act')
print("bob's hand below")
print(bob.showHand())
cardplay = input('Type the card to play it')
if cardplay == bob.showHand():
print('playing card!')
I'd expect the print of playing card to appear. However no matter how much i try i can't seem to get it too work!
Your showHand method does not create a string; it causes print to be used to display text in the terminal. input reads a string from the user, and stores it. The == comparison will never compare equal, because on one side you have a string, and on the other side you have the special value None.
You should not have the methods of any of your classes do any print calls. Instead, have them return the appropriate strings, which are then printed from outside.
Also, by using a special name for these methods - __str__ - we can make Python use it automatically when printing a class instance, or when converting to string using str. Consider the example:
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
def __str__(self):
return f'{self.value} of {self.suit}'
class Hand:
def __init__(self, cards):
self.cards = cards
def __str__(self):
return ' | '.join(str(card) for card in self.cards)
my_hand = Hand([Card('Spades', 'Ace'), Card('Diamonds', 'King')]) # blackjack!
print(my_hand) # we see: `Ace of Spades | King of Diamonds`
Your problem is that if cardplay == bob.showHand(): does not actually do what you think. showHand() returns None and that will never equal any input Card. You need to convert what the user inputs to a card and then compare that card to the cards the Player is currently holding.
To solve these issues and make your code do what you are trying to do, change your Card Class to the following:
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
def show(self):
print('{} of {}'.format(self.value, self.suit))
def __eq__(self, other):
if (other.suit == self.suit and other.value == self.value):
return True
else:
return False
and the last couple of lines to this:
Turn.start = 0
if Turn.start == 0:
print('bob to act')
print("bob's hand below")
bob.showHand()
cardplay = input('Type the card to play it')
picked = Card(cardplay.split()[2], int(cardplay.split()[0]))
for card in bob.hand:
if picked == card:
print('playing card!')
Then, you can enter any Card, exactly as it appears, on the input line and it will print
'playing card!'
I am currently learning about class inheritance in python. In the example below, the goal is to remove card pairs(any 2 cards with the same value and color, e.g 3 of hearts and 3 of diamonds) in a single hand containing 13 distinct, random cards.
It seems the error is at line 127: if match in self.cards: . This statement always returns true even if match is not in self.cards . The error is ValueError: list.remove(x): x not in list. Anyone has any idea how to achieve the intended goal? Thanks.
p.s learning through: How to Think Like a Computer Scientist.
class Card:
"""represents a card found in a standard deck with 52 cards."""
suits = ["Clubs", "Diamonds", "Hearts", "Spades"]
ranks = ["narf", "Ace", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "Jack", "Queen", "King"]
def __init__(self, suit=0, rank=0):
self.suit = suit
self.rank = rank
def __str__(self):
return (self.ranks[self.rank] + ' of ' + self.suits[self.suit])
def cmp(self, other):
if self.suit > other.suit:
return 1
if self.suit < other.suit:
return -1
if self.rank < other.rank:
return 1
if self.rank < other.rank:
return -1
return 0
def __eq__(self, other):
return self.cmp(other) == 0
def __le__(self, other):
return self.cmp(other) <= 0
def __lt__(self, other):
return self.cmp(other) < 0
def __ge__(self, other):
return self.cmp(other) >= 0
def __gt__(self, other):
return self.cmp(other) > 0
def __ne__(self, other):
return self.cmp(other) != 0
class Deck:
"""Contains a list of cards as attribute and generates the 52 cards"""
def __init__(self):
self.cards = []
for suit in range(4):
for rank in range(1, 14):
self.cards.append(Card(suit, rank))
def __str__(self):
s = ""
for i in range(len(self.cards)):
s = s + " " * i + str(self.cards[i]) + "\n"
return s
def print_deck(self):
for card in self.cards:
print(card)
def shuffle(self):
"""shuffles the deck of cards randomly."""
import random
rng = random.Random()
rng.shuffle(self.cards)
def remove(self, card):
"""Removes a single specified card from the deck."""
if card in self.cards:
self.cards.remove(card)
return True
else:
return False
def pop(self):
"""Select a single card from the deck"""
return self.cards.pop()
def is_empty(self):
if self.cards == []:
return True
return False
def deal(self, hands, num_cards=52):
"""Deals cards from the deck to a list of hands.
If number of card to deal is not specified, the entire deck is dealt."""
for i in range(num_cards):
if self.cards == []:
break
card = self.pop()
hand = hands[i % len(hands)]
hand.add(card)
class Hand(Deck):
def __init__(self, name=''):
Deck.__init__(self)
self.cards = []
self.name = name
def __str__(self):
string = "The Hand " + self.name
if self.is_empty():
string += 'is empty.\n'
else:
string += ' contains:\n'
return string + Deck.__str__(self)
def add(self, card):
self.cards.append(card)
class CardGame:
def __init__(self):
self.deck = Deck()
self.deck.shuffle()
class OldMaidHand(Hand):
"""Creates a hand for a single player to play a game of Old Maid."""
def remove_matches(self):
cards = self.cards[:]
for card in cards:
match = Card(3 - card.suit, card.rank)
if match in self.cards:
self.cards.remove(card)
self.cards.remove(match)
print('Hand {0} removed {1} and {2}.'
.format(self.name, card, match))
game = CardGame()
player1 = OldMaidHand('test')
game.deck.deal([player1], 13)
player1.print_deck()
player1.remove_matches()
I want to use self.numbers and self.suits from the Cards class in the Deck class. The only way i know to do this is what is shown below. Pass class.card as an argument in the class Deck's __init__ method. Here is the problem. In order to then use the Deck class, I have to pass an argument as well and i do not need or want to do that. Hopefully, I'm explaining myself well. I have the same problem trying to pass variables from the Card class and the Deck class in the Player class.
Here is the code:
import random
class Card:
def __init__(self):
self.numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'jack', 'queen', 'king', 'ace']
self.suits = ['hearts', 'diamonds', 'clubs', 'spades']
# print the current card
def __str__(self):
print(f'{self.numbers} of {self.suits}')
class Deck(Card):
def __init__(self, class_card):
super().__init__()
self.deck = []
self.numbers = class_card.numbers
self.suits = class_card.suits
# create a new deck of cards
def create_deck(self):
for suit in self.suits:
for number in self.numbers:
self.deck.append([number, suit])
return self.deck
# shuffle the deck of cards before the start of the game
def shuffle_deck(self, deck):
random.shuffle(deck)
class Player(Deck):
def __init__(self, class_deck):
super().__init__()
self.hand = []
self.count_cards = 0
self.score = 0
# draw a card from the deck
def draw_card(self):
drawn_card = self.hand.append(self.deck[-1])
return drawn_card
# count the cards in the players hand
def count_hand(self):
self.count_cards = len(self.deck)
return self.count_cards
# calculate the score each time the player wins
def calculate_score(self):
pass
When you run super().__init__() the deck will inherit the numbers and suits attribute from card. See the follows:
import random
class Card:
def __init__(self):
self.numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'jack', 'queen', 'king', 'ace']
self.suits = ['hearts', 'diamonds', 'clubs', 'spades']
# print the current card
def __str__(self):
print(f'{self.numbers} of {self.suits}')
class Deck(Card):
def __init__(self):
super().__init__()
self.deck = []
# create a new deck of cards
def create_deck(self):
for suit in self.suits:
for number in self.numbers:
self.deck.append([number, suit])
return self
# shuffle the deck of cards before the start of the game
def shuffle_deck(self):
random.shuffle(self.deck)
def __len__(self):
return len(self.deck)
def pop(self):
return self.deck.pop()
class People:
def __init__(self, name):
self.name = name
class Player(People):
def __init__(self, name, deck):
super().__init__(name)
self.hand = []
self.count_cards = 0
self.score = 0
self.deck = deck
# draw a card from the deck
def draw_card(self):
drawn_card = self.hand.append(self.deck.pop())
return drawn_card
# count the cards in the players hand
def count_hand(self):
self.count_cards = len(self.hand)
return self.count_cards
# calculate the score each time the player wins
def calculate_score(self):
pass
def main():
deck = Deck().create_deck()
players = [Player(name, deck) for name in ('Jim', 'Ben', 'Paul', 'Wang')]
deck.shuffle_deck()
i = 0
while len(deck):
players[i].draw_card()
i = (i + 1) % len(players)
for p in players:
print(p.name, p.count_hand(), p.calculate_score())
if __name__ == '__main__':
main()
Output:
Jim 14 None
Ben 14 None
Paul 14 None
Wang 14 None
However, my recommend solution is like this:
import random
class Card:
numbers = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'jack', 'queen', 'king', 'ace']
suits = ['hearts', 'diamonds', 'clubs', 'spades']
def __init__(self, number, suit):
self.number = number
self.suit = suit
self.score = self.numbers.index(number) * 10 + self.suits.index(suit)
# print the current card
def __str__(self):
print(f'{self.number} of {self.suit}')
def __eq__(self, other):
return self.score == other.score
def __lt__(self, other):
return self.score < other.score
class Deck:
def __init__(self):
self.deck = [
Card(num, suit) for num in Card.numbers for suit in Card.suits
]
# shuffle the deck of cards before the start of the game
def shuffle(self):
random.shuffle(self.deck)
return self
def __len__(self):
return len(self.deck)
def pop(self):
return self.deck.pop()
class People:
def __init__(self, name):
self.name = name
class Player(People):
def __init__(self, name, deck):
super().__init__(name)
self.hand = []
self.count_cards = 0
self.score = 0
self.deck = deck
# draw a card from the deck
def draw_card(self):
drawn_card = self.hand.append(self.deck.pop())
return drawn_card
# count the cards in the players hand
def count_hand(self):
self.count_cards = len(self.hand)
return self.count_cards
# calculate the score each time the player wins
def calculate_score(self):
return sum(card.score for card in self.hand)
def main():
deck = Deck().shuffle()
players = [Player(name, deck) for name in ('Jim', 'Ben', 'Paul', 'Wang')]
i = 0
while len(deck):
players[i].draw_card()
i = (i + 1) % len(players)
for p in players:
print(p.name, p.count_hand(), p.calculate_score())
if __name__ == '__main__':
main()
Im working through a blackjack game as an assignment for a python course I purchased on udemy and I believe I've found an issue with the class's provided solution. I made a change to the code myself to make it work, but I'd like to ask the stackoverflow community 2 questions: 1) Will someone please explain exactly why the solution doesn't work? I will make a few guesses below but as a beginner I'd like to understand exactly whats wrong for my own benefit. 2) Is the way I fixed the problem a good solution? If not, what are some other ways I could code this?
This is the given solution (I'm only showing the 3 classes of: Card, Hand and Deck - problem is within Deck, specifically Deck's str function)
# Let's start with defining our suits, ranks and values
suits = ('H','D','C','S')
ranking = ('A','2','3','4','5','6','7','8','9','10','J','Q','K')
card_val = {'A':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'10':10,'J':10,'Q':10,'K':10}
class Card(object):
# card needs a suit and rank
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
def __str__(self):
return self.suit + self.rank
def get_suit(self):
return self.suit
def get_rank(self):
return self.rank
def draw(self):
print (self.suit + self.rank)
class Hand(object):
# A hand needs 2 cards and a total value
def __init__(self):
self.hand = []
self.value = 0
self.ace = False
def __str__(self):
hand_comp = ''
for card in self.hand:
card_name = card.__str__
hand_comp += " " + card_name
return 'The hand has %s' %hand_comp
def card_add(self, card):
self.hand.append(card)
# Check for Aces
if card.rank == 'A':
self.ace = True
self.value += card_val[card.rank]
def calc_val(self):
# calculate the value of the hand, make aces an 11 if they don't bust
if(self.ace == True and self.value < 12):
return self.value + 10
else:
return self.value
def draw(self, hidden):
if hidden == True and playing == True:
#Don't show first hidden card
starting_card = 1
else:
starting_card = 0
for x in range(starting_card, len(self.hand)):
self.hand[x].draw()
class Deck(object):
def __init__(self):
self.deck = []
for s in suits:
for r in ranking:
self.deck.append(Card(s,r))
def shuffle(self):
random.shuffle(self.deck)
def deal(self):
single_card = self.deck.pop()
return single_card
def __str__(self):
deck_comp = ''
for card in self.hand:
deck_comp += " " + deck_comp.__str__()
return "The deck has " + deck_comp
My problem is with the def str function under class Deck. The solution asks the program to run a for loop over... self.hand (which is under class Hand). I don't see why this would work at all. self.hand doesn't have anything to do with getting a full deck returned to me. Secondly, is deck_comp += " " + deck_comp.str() good code? I find my program freezes up if I only changed the loop to self.deck. I created a new variable called cards_name and assign it to cards.str() to get each card's suit and rank instead.
this is what I corrected the code to:
def __str__(self):
deck_comp = ''
for card in self.deck:
cards_name = card.__str__()
deck_comp += " " + cards_name
return "The deck has " + deck_comp