Black Jack Game, that kinda works... not really

The last step of the Python Terminal Game Portfolio Project is to make a blog post.
So I’m gonna let this count for that step.

I decided to move on from this project. It doesn’t work as anticipated. The main problem was the dealer continued to hit even though his hand had a value greater than 16.

I’m moving on to continue learning without spending too much time on this project. However, if anyone responds to this post, I’ll definitely read the response, update my code and try to learn from it.

import random
from random import shuffle
import time
from time import sleep
# I could use w2n to simplify getting the values of each card
# import word2number
# from word2number import w2n

class Card:
    def __init__(self, rank, suit):
        self.rank = rank
        self.suit = suit
        self.value = 0
    def card_value(self):
        if self.rank == 'Two':
            self.card_value = 2
        elif self.rank == 'Three':
            self.card_value = 3
        elif self.rank == 'Four':
            self.card_value = 4
        elif self.rank == 'Five':
            self.card_value = 5
        elif self.rank == 'Six':
            self.card_value = 6
        elif self.rank == 'Seven':
            self.card_value = 7
        elif self.rank == 'Eight':
            self.card_value = 8
        elif self.rank == 'Nine':
            self.card_value = 9
        elif self.rank in ['Ten', 'Jack', 'Queen', 'King']:
            self.card_value = 10
    def __repr__(self):
        return "{rank} of {suit}".format(rank = self.rank, suit = self.suit)

ranks = ['Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King', 'Ace']
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
# This is a list of all the card objects
deck = [Card(rank, suit) for suit in suits for rank in ranks]

# Previously had Deck class here, erased it, using list instead

class Player:
    def __init__(self, name, number):
        self.name = name
        self.number = number
        self.money = 500
        self.hand = []
        self.bet = 0
        self.hand_value_before_aces = 0
        self.hand_value_after_aces = 0
    def __repr__(self):
        return "This is player number {num}, {name}".format(num = self.number, name = self.name)
    
class Dealer:
    def __init__(self):
        self.hand = []
        self.hand_value_before_aces = 0
        self.hand_value_after_aces = 0
    
list_of_players = []

def add_hand_value_before_aces(player):
    for card in player.hand:
        player.hand_value_before_aces += card.value
def ace_values(player):
    for card in player.hand:
        if card.rank == 'Ace' and (player.hand_value_before_aces + 11) <= 21:
            card.value = 11
        elif card.rank == 'Ace' and (player.hand_value_before_aces + 11) > 21:
            card.value = 1
def add_hand_value_after_aces(player):
    value_of_aces = 0
    for card in player.hand:
        if card.rank == 'Ace':
            value_of_aces += card.value
    player.hand_value_after_aces = player.hand_value_before_aces + value_of_aces


initiate_game_player_str = input("Would you like to play a game of blackjack? \n 'Yes' or 'No'? ")
if initiate_game_player_str == "Yes":
    initiate_game_boolean = True
else:
    initiate_game_boolean = False
    print("Ok, have a nice day.")
if initiate_game_boolean == True:
    str_num_players = input("How many players do you have? \n '1', '2', or '3'? ")
    if str_num_players == '1':
        int_player_num = 1
    elif str_num_players == '2':
        int_player_num = 2
    elif str_num_players == '3':
        int_player_num = 3
    else:
        print("You didn't type in one of the given options")
for i in range(int_player_num):
    player_num = 1
    list_of_players.append(Player(input("What is the name of player number {num}? ".format(num = player_num)), player_num))
    player_num += 1


sleep(0.3)

dealer = Dealer()
def start_new_round():
    sleep(0.8)
    for player in list_of_players:
        print("{name} you have a balance of {bal}".format(name = player.name, bal = str(player.money)))
        sleep(0.3)
        player.bet = input("How much would you like to bet? \n Type an int < your bal: ")
        if int(player.bet) > player.money:
            player.bet = input("Your bet was to high, try again...")
    random.shuffle(deck)
    print("The cards have been shuffled")
    sleep(0.3)
    for player in list_of_players:
        while len(player.hand) < 2:
            player.hand.append(deck[-1])
            deck.pop()
            

    while len(dealer.hand) < 2:
        dealer.hand.append(deck[-1])
        deck.pop()
    print("The dealer has one {card}, and their other card is hidden.".format(card = dealer.hand[1:]))
    sleep(0.3)
    round_cycle()


player_want_to_continue_counter = 0
players_want_to_continue = False
def round_cycle():
    dealer_stay = False
    stay_counter = 0
    round_cycle_complete = False
    while round_cycle_complete == False:
        for player in list_of_players:
            hit_or_stay_str = input("{name} your hand is \n{hand} \nWould you like to 'Hit' or 'Stay'? ".format(name = player.name, hand = player.hand))
            if hit_or_stay_str == 'Stay':
                stay_counter += 1
            elif hit_or_stay_str == 'Hit':
                player.hand.append(deck[-1])
                deck.pop()
                add_hand_value_before_aces(player)
                ace_values(player)
                add_hand_value_after_aces(player)
                print("Your hand's value is {val}".format(val = str(player.hand_value_after_aces)))
            else:
                print("You typed in something other then one of the given options, and ruined the game...")
        add_hand_value_before_aces(dealer)
        ace_values(dealer)
        add_hand_value_after_aces(dealer)         
        if dealer.hand_value_after_aces >= 17:
            dealer_stay = True
            print("The dealer is staying")
        else:
            dealer.hand.append(deck[-1])
            deck.pop()
            print("The dealer hit and picked up {card} \n The dealer has \n -hidden card- and {d_hand}".format(card = dealer.hand[-1], d_hand = dealer.hand[1:]))
        if stay_counter == int_player_num and dealer_stay == True:
            round_cycle_complete = True
            print("The dealer flips over their hidden card")
            print("The deal has:")
            print(dealer.hand)
        if round_cycle_complete == True:
            for player in list_of_players:
                if player.hand_value_after_aces > dealer.hand_value_after_aces and player.hand_value_after_aces < 22:
                    print("{name} you beat the dealer".format(name = player.name))
                    player.money += (player.bet)
                    print("Your new balance is {bal}".format(bal = player.money))
                    sleep(0.5)
                else:
                    print("Better luck next time {name}".format(name = player.name))
                    player.money += (player.bet)
            for player in list_of_players:
                if input("{name} if you'd like to continue type 'Y' ") == 'Y':
                    player_want_to_continue_counter += 1
            if player_want_to_continue_counter == len(list_of_players):
                players_want_to_continue = True


start_new_round()

while players_want_to_continue == True:
    dealer_stay = False
    round_cycle_complete = False
    stay_counter = 0
    player_want_to_continue_counter = 0
    players_want_to_continue = False
    start_new_round()

# The dealer will hits endlessly and never stays

If you’re using python 3.10 or higher consider using match-case syntax

becomes

    match self.rank:
        case 'Two':
            self.card_value = 2
        case 'Three':
            self.card_value = 3
        case 'Four':
            self.card_value = 4
        case 'Five':
            self.card_value = 5
        case 'Six':
            self.card_value = 6
        case 'Seven':
            self.card_value = 7
        case 'Eight':
            self.card_value = 8
        case 'Nine':
            self.card_value = 9
        case 'Ten' | 'Jack' | 'Queen' | 'King':
            self.card_value = 10
1 Like

Oooh that’s very interesting. That definitely cleans it up a little. Thanks for the input.

I’d just lookup what you have:

# if you're going to have these global, put them above Card
CARD_RANKS = ['Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King', 'Ace']
CARD_SUITS = ['Clubs', 'Diamonds', 'Hearts', 'Spades']

class Card:
    def __init__(self, rank, suit):
        # now you can make sure rank and suit are valid
        if rank not in CARD_RANKS:
            raise "{} invalid rank".format(rank)
        if suit not in CARD_SUITS:
            raise "{} invalid suit".format(suit)
        self.rank = rank
        self.suit = suit
        # don't store this:
        # self.value = 0

    def value(self):
        rank_idx = CARD_RANKS.index(self.rank)
        return [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11][rank_idx]

    def __repr__(self):
        return "{rank} of {suit}".format(rank = self.rank, suit = self.suit)

# don't generate this statically
# in the future you may want to do this more than once for restart
def init_deck():
    return [Card(rank, suit) for suit in CARD_SUITS for rank in CARD_RANKS]

Now, you have the Ace peculiarity for this particular game. Frankly, I’m not sure I’d even have a value method attached to card. Instead, do that in your hand calculations.

I think I’d have a calc_hand that gives the current max value below 21.

I’m not sure the current hand_value_after_aces addresses multiple aces properly. Or, that high cards prior mightn’t mess it up.

1 Like

How you converted the ranks into values was so much cleaner.

I think you’re right that calculating the value of hand is the way to go rather than giving an ace the value.

Thanks for your input.