Question: I am attempting to make a blackjack game. I am at the part where I am trying to make a player class with a hand size which contains an empty list. My intuition tells me that I need to be targeting the deck where the cards are and to use the append and the pop methods. I have already written a function in another class called the deal cards method. Am I able to use functions from other classes inside a class? Or do I have to come up with some way of my class targeting the deck and getting the cards?
# Work on the player class give them the ability to have a hand and to deal the
# cards into that hand
from random import shuffle
class Card:
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __str__(self):
return str(self.rank) + " of " + self.suit
class Deck:
deck = []
def __init__(self):
suits = "Hearts", "Diamonds", "Clubs", "Spades"
ranks = 2,3,4,5,6,7,8,9,10,"J","Q","K","A"
for suit in suits:
for rank in ranks:
card= Card(rank,suit)
self.deck.append(card)
# need to be able to deal cards
def deal_card(self):
dealt_card = self.deck.pop()
return dealt_card # return gives back the value to whomever called it.
# needs to be able to shuffle
def shuffle(self):
shuffle(self.deck)
# display the deck
def display(self):
for card in self.deck:
print(card)
class Player:
def __init__(self, name, isdealer):
self.name = name
self.hand = []
self.isdealer = isdealer
def draw_cards(self):
player_cards = dealt_card();
#scratchpad thinking area
# self.hand is an empty list. I want to get cards from the deck and put them in that list! SO I need to target
# the deck somehow.
# maybe with pop?
# I would use append to add the result to my hand.
# Work on the player class give them the ability to have a hand and to deal the
# cards into that hand
def main(): # main can be called blackjack or gameplay
# Welcome the player and explain the rules
print("""Welcome to Blackjack! Here are the Rules
Try to get as close to 21 without going over.
Kings, Queens, and Jacks are worth 10 points.
Aces are worth 1 or 11 points.
Cards 2 through 10 are worth their face value.
(H)it to take another card.
(S)tand to stop taking cards.
The dealer stops hitting at 17""")
# Run a game of blackjack
# create a deck of cards outside of the main.
deck = Deck()
deck.shuffle()
deck.display()
# Make player 1 and the dealer
# while True:
# return cards to the deck
# Shuffle the deck of cards close to the start to start a new game.
# Deal 2 cards to the players
# Loop: display hands and scores
# Ask them to hit or stand.
# Determine Winner
# If the program is run (instead of imported), run the game:
if __name__ == '__main__':
main()
I have tried using the function from the deck class but I am unsure I can use methods from other classes. I know that append can be used to add to strings. I know I need to target the deck somehow but it is in another class.
Am I able to use functions from other classes inside a class? Or do I have to come up with some way of my class targeting the deck and getting the cards?
There are several ways to approach this dilemma:
You could have a method in the Deck
class that takes a player instance as argument. It then has access to both the deck and the player's hand
You could have a method in the Player
class that takes the deck instance as argument. This is similar, but approaching it from the opposite side
You could create yet another class, like BlackJack
that would have attributes for the deck and the players, and then its methods can access both deck and player without issue. This looks like a nicer solution, but it seems less compatible with the main
function you have. It would mean that all of the logic in main
would move into that new class.
In below code I have taken the first approach in the deal
method.
I also took some other decisions:
Make Deck
a subclass of list, because a deck is mostly like a list with maybe just one or two extra methods. This means a deck instance no longer needs a deck
attribute, but can apply pop
, append
, ..etc directly to self
.
Make Player
a subclass of Deck
, because a player is mostly like a deck (a "hand") with a name and a score. This means there is no more hand
attribute, as self
is that hand.
Add a value
attribute to the Card
class, which would calculate the value of the card (1 for Ace, ...etc) at construction.
Add a score
method to the Player
class which sums up the card values, adding 10 if the hand includes at least one ace and that would not bust the score.
I also implemented the game loop with hitting/standing/quitting, and reporting the winner
Maybe some of this might not be acceptable for the code challenge you might be taking, but I thought these were the right thing to do:
from random import shuffle
class Card:
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
# Add a value to use in the blackjack scoring
self.value = rank if isinstance(rank, int) else (1 if rank == "A" else 10)
def __str__(self):
return f"{self.rank} of {self.suit}"
class Deck(list): # Inherit from list as a deck is mostly a list
def __init__(self):
suits = "Hearts", "Diamonds", "Clubs", "Spades"
ranks = 2,3,4,5,6,7,8,9,10,"J","Q","K","A"
for suit in suits:
for rank in ranks:
card = Card(rank, suit)
self.append(card)
def deal(self, player): # Pass player as argument so to act on deck & player
player.append(self.pop())
return player[-1]
def collect(self, player):
self.extend(player)
player.clear()
def __str__(self):
return ", ".join(map(str, self))
class Player(Deck): # Inherit from Deck, as a player really is a deck with a name
def __init__(self, name, isdealer):
self.name = name
self.isdealer = isdealer
def score(self):
total = 0
hasAce = False
for card in self:
total += card.value
hasAce = hasAce or card.value == 1
return total + 10 if hasAce and total < 12 else total
def __str__(self):
return f"{self.name} has {self.score()} ({super().__str__()})"
def main():
# Welcome the player and explain the rules
print("""Welcome to Blackjack! Here are the Rules
Try to get as close to 21 without going over.
Kings, Queens, and Jacks are worth 10 points.
Aces are worth 1 or 11 points.
Cards 2 through 10 are worth their face value.
(H)it to take another card.
(S)tand to stop taking cards.
The dealer stops hitting at 17
""")
deck = Deck()
# Make player 1 and the dealer
player = Player(input("What's your name? "), False)
dealer = Player("Dealer", True)
while True:
# return cards to the deck
deck.collect(player)
deck.collect(dealer)
shuffle(deck)
# Deal 2 cards to the players
deck.deal(player)
deck.deal(player)
deck.deal(dealer)
deck.deal(dealer)
# Loop: display hands and scores
print()
print("New game:")
print(dealer)
print(player)
# Ask them to hit or stand.
while player.score() <= 21:
choice = input(f"{player.name}, do you want to [h]it, [s]tand or [q]uit? ").upper()
if choice == "Q":
return
if choice == "S":
break
if choice == "H":
print(f"{player.name}, you receive {deck.deal(player)}")
print(player)
while dealer.score() <= 16:
print(f"{dealer.name} receives {deck.deal(dealer)}")
print(dealer)
# Determine Winner
if player.score() > 21 or player.score() <= dealer.score() <= 21:
print(f"{dealer.name} won this round")
else:
print(f"{player.name}, you won this round!")
if __name__ == '__main__':
main()