Shez
Shez

Reputation: 21

Drawing unique cards from a deck for a game of BlackJack in Python

I'm making a multi-player game of blackjack and having been encountering issues getting the code (in python) to deal unique cards to multiple players. The code below has been dealing the same set of cards to all players.

An example of the results for 2 players is as follow:

Player1's first card is Four of Hearts. Player1's second card is Nine of Clubs. Player2's first card is Four of Hearts. Player2's second card is Nine of Clubs.

I have been reviewing the code and making amendments to the function of dealingcards, e.g. by using a while loop to go through each player and appending two cards to their hand. However, the result is no different.

Will anyone be able to advise where the error in the code lies? Thank you.

Code as shown below:

"""This is a game of Blackjack."""
import random


"""Defining the attributes of the cards"""
suits = ('Hearts', 'Diamonds', 'Spades', 'Clubs')
ranks = ('Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King', 'Ace')

"""Class class must be able to understand the Suit and Rank of a card"""
class Card:
    def __init__(self,suit,rank):
        self.suit = suit
        self.rank = rank

    def __str__(self):
        return self.rank + " of " + self.suit

"""Deck class must be able to:
Instantiate a new deck and hold as a list of card objects;
Shuffle a deck through a method call; and
Deal cards from the deck."""

class Deck:
    def __init__(self):
        self.all_cards = []
        for suit in suits:
            for rank in ranks:
                created_card = Card(suit,rank)
                self.all_cards.append(created_card)

    def shuffle(self):
        random.shuffle(self.all_cards)

    def deal_one(self):
        return self.all_cards.pop()

    def __str__(self):
        statement = ""
        for card in self.all_cards:
            statement = statement + card.rank + " of " + card.suit + "\n"
        return statement

"""Hand class must be able to:
Store the hands of the player;
Compute the value of each hand"""

class Playerhand:
    def __init__(self,name,hands=[[],[],[],[]]):
        self.name = name
        self.hands = hands

        
    def num_of_hands(self):
        num_of_hands = 0
        for hand in self.hands:
            if hand != []:
                num_of_hands += 1
            else:
                pass
        return num_of_hands

    def show_first_card(self):
        print("{}'s first card is {}".format(self.name,self.hands[0][0]))
        print("{}'s second card is {}".format(self.name,self.hands[0][1]))


    def value_of_hands(self):
        value_of_hands = []
        for hand in self.hands:
            total_value = 0
            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}
            check_for_aces = []
            for card in hand:
                if card.suit == 'Ace':
                    check_for_aces.append('Ace')
                else:
                    pass

            for card in hand:
                total_value = total_value + values[card.rank]
                
                for item in range(0,len(check_for_aces)):
                    if total_value > 21:
                        total_value = total_value - 10
                    else:
                        pass

            value_of_hands.append(total_value)

        return value_of_hands

"""Pre-game:
Asking for the number of players to CREATE A LIST OF PLAYERS ['Dealer', 'P1', 'P2', etc.] and a DICTIONARY OF WAGERS {"P1": $X, "P2": $Y, etc.}"""

def number_of_players():
    number_of_players = input("How many players are we expecting? Please enter 1 to 7: ")
    while number_of_players.isdigit() == False or int(number_of_players) not in range(1,8):
        number_of_players = input("Only accepting integer values between 1 to 7. Please enter 1 to 7: ")
    return int(number_of_players)

def check_float(potential_float):
    try:
        float(potential_float)
        return True
    except:
        return False

def playername(no_of_players):
    print("We are expecting {} player(s). We are going to start collecting information on player names and wager!".format(no_of_players))
    player_list = ['Dealer']

    for number in range(1,no_of_players+1):
        name = input("Player {}, what is your name? ".format(number))
        while name in player_list:
            name = input("Player {}, your chosen name has been taken. Please give us an alternative: ")
        player_list.append(name)

    return player_list

def playerwager(player_list):
    player_wager = {}
    for number in range(1, len(player_list)):
        wager = input("{}, how much are you betting? Please input number: $ ".format(player_list[number]))
        while check_float(wager) is False:
            wager = input("{}, please input a valid wager in number: $ ".format(player_list[number]))
        wager = float(wager)
        player_wager[player_list[number]] = wager
        player_wager["Dealer"] = 0
    return player_wager

def dealingcards(player_list):
    newdeck = Deck()
    newdeck.shuffle()
    print(newdeck)                                                      # Remove in due course.

    for player in player_list:
        for item in range(0,2):
            Playerhand(player).hands[0].append(newdeck.deal_one())
        Playerhand(player).show_first_card()

#GAME ON
x = number_of_players()
playerlist = playername(x)
playerwager(playerlist)
dealingcards(playerlist)

Upvotes: 2

Views: 820

Answers (2)

Heiner Früh
Heiner Früh

Reputation: 127

This part of the code is wrong:

for player in player_list:
    for item in range(0,2):
        Playerhand(player).hands[0].append(newdeck.deal_one())
    Playerhand(player).show_first_card()

You have to store the playerhand and not redefine it when showing the first card:

playerhands = []
for player in player_list:
    playerhands.append(Playerhand(player,hands=[[],[],[],[]]))
    for item in range(0,2):
        playerhands[-1].hands[0].append(newdeck.deal_one())
    playerhands[-1].show_first_card()

EDIT: This is the true source of the problem "Least Astonishment" and the Mutable Default Argument

Upvotes: 1

Gabe Ron
Gabe Ron

Reputation: 329

While Heiner is on the right track, I went through with a debugger and found that doing:

ph = Playerhand(player)

Mysteriously wasn't clearing the hand. The data seems to almost be static. However, a simple bandaid fix is as follows:

for player in player_list:
    ph = Playerhand(player)
    ph.hands[0] = []
    for item in range(0, 2):
        card = newdeck.deal_one()
        ph.hands[0].append(card)
    ph.show_first_card()

The hands were never clearing and that's why they kept outputting the same hand.

Upvotes: 0

Related Questions