3
\$\begingroup\$

I've decided to learn python as my main language and after watching some tutorial on youtube I decided to write this blackjack game using OOP. Im sure it has a lot of beginner mistakes and might be simply bad. I also don't guarantee that it will always work but i would appreciate any feedback you could give me that could help me write better and cleaner code in the future.

Thanks in advance.

from random import randint
from os import system


class Deck:
    def __init__(self):
        self.deck = [("Ace of Clubs", 1), ("Ace of Diamonds", 1),
                     ("Ace of Hearts", 1), ("Ace of Spades", 1),
                     ("2 of Clubs", 2), ("2 of Diamonds", 2),
                     ("2 of Hearts", 2), ("2 of Spades", 2),
                     ("3 of Clubs", 3), ("3 of Diamonds", 3),
                     ("3 of Hearts", 3), ("3 of Spades", 3),
                     ("4 of Clubs", 4), ("4 of Diamondss", 4),
                     ("4 of Hearts", 4), ("4 of Spades", 4),
                     ("5 of Clubs", 5), ("5 of Diamonds", 5),
                     ("5 of Hearts", 5), ("5 of Spades", 5),
                     ("6 of Clubs", 6), ("6 of Diamonds", 6),
                     ("6 of Hearts", 6), ("6 of Spades", 6),
                     ("7 of Clubs", 7), ("7 of Diamonds", 7),
                     ("7 of Hearts", 7), ("7 of Spades", 7),
                     ("8 of Clubs", 8), ("8 of Diamonds", 8),
                     ("8 of Hearts", 8), ("8 of Spades", 8),
                     ("9 of Clubs", 9), ("9 of Diamonds", 9),
                     ("9 of Hearts", 9), ("9 of Spades", 9),
                     ("10 of Clubs", 10), ("10 of Diamonds", 10),
                     ("10 of Hearts", 10), ("10 of Spades", 10),
                     ("Jack of Clubs", 10), ("Jack of Diamonds", 10),
                     ("Jack of Hearts", 10), ("Jack of Spades", 10),
                     ("Queen of Clubs", 10), ("Queen of Diamonds", 10),
                     ("Queen of Hearts", 10), ("Queen of Spades", 10),
                     ("King of Clubs", 10), ("King of Diamonds", 10),
                     ("King of Hearts", 10), ("King of Spades", 10)]

    def get_deck(self):
        return self.deck

    def remove_from_deck(self, index):
        self.deck.pop(index)

    def __len__(self):
        return len(self.deck)


class Tokens:
    def __init__(self, amount):
        self.amount = amount

    def __str__(self):
        return str(self.amount)

    def bet_tokens(self, amount):
        if self.amount - amount >= 0:
            self.amount -= amount
            return True
        print(f"Not enough tokens!")
        return False

    def add_tokens(self, amount_won):
        self.amount += amount_won

    def get_tokens(self):
        return self.amount


class Blackjack:

    def __init__(self,):
        self.tokens = Tokens(100)
        self.second_card_hidden = True

    def deal_a_card(self, hand, deck):
        x = randint(0, len(self.deck) - 1)
        hand.append(deck.get_deck()[x])
        deck.remove_from_deck(x)

    def open(self, bet_amount):
        if self.tokens.bet_tokens(bet_amount):
            self.deal_a_card(self.user_hand, self.deck)
            self.deal_a_card(self.user_hand, self.deck)
            self.deal_a_card(self.dealer_hand, self.deck)
            self.deal_a_card(self.dealer_hand, self.deck)
            return True
        return False

    def get_sum_of_cards(self, hand):
        sum_of_cards = 0
        for card in hand:
            sum_of_cards += card[1]

        for card in hand:
            if card[0] in ["Ace of Spades", "Ace of Hearts", "Ace of Diamonds", "Ace of Clubs"]:
                if sum_of_cards <= 11:
                    sum_of_cards += 10
        return sum_of_cards

    def check_for_bust(self, hand):
        return self.get_sum_of_cards(hand) > 21

    def get_cards(self, hand):
        cards = ""
        for card in hand:
            cards += card[0] + ", "
        return cards[:-2]

    def reset_hands_and_deck(self):
        self.deck = Deck()
        self.user_hand = []
        self.dealer_hand = []
        self.second_card_hidden = True

    def clear_screen_print_cards(self):
        system("cls")
        print(self)

    def check_for_win(self):
        user_score = self.get_sum_of_cards(self.user_hand)
        dealer_score = self.get_sum_of_cards(self.dealer_hand)

        if user_score == dealer_score:
            print("Tie!")
            return "tie"
        elif user_score > dealer_score:
            print("You win!")
            return "win"
        elif dealer_score > user_score:
            print("You lose!")
            return "lose"

    def check_for_blackjack(self):
        user_score = self.get_sum_of_cards(self.user_hand)
        dealer_score = self.get_sum_of_cards(self.dealer_hand)

        if user_score == dealer_score == 21:
            print("You both hit a blackjack!")
            return "tie"
        elif user_score == 21:
            print(f"You hit a blackjack!")
            return "blackjack_user"
        elif dealer_score == 21:
            system("cls")
            self.second_card_hidden = False
            print(game)
            print("Dealer hit a blackjack!")
            return "blackjack_dealer"
        return 0

    def hit_or_stand(self):
        decision = input("(hit/stand h/s): ")
        while decision.lower() not in ["hit", "h", "stand", "s"]:
            decision = input("(hit/stand h/s): ")

        if decision in ["hit", "h"]:
            return 1
        return 0

    def token_distributor(self, result, bet_amount):
        if result == "blackjack_user":
            self.tokens.amount += bet_amount + 1.5 * bet_amount
        elif result == "tie":
            self.tokens.amount += bet_amount
        elif result == "win":
            self.tokens.amount += (2 * bet_amount)

    def __str__(self):
        if self.second_card_hidden:
            return f"\tDealer hand({self.dealer_hand[1][1]})\n[{self.dealer_hand[1][0]}, <card hidden>]\n\n[{self.get_cards(self.user_hand)}]\n\tPlayer hand({self.get_sum_of_cards(self.user_hand)})\n"
        else:
            return f"\tDealer hand({self.get_sum_of_cards(self.dealer_hand)})\n[{self.get_cards(self.dealer_hand)}]\n\n[{self.get_cards(self.user_hand)}]\n\tPlayer hand({self.get_sum_of_cards(self.user_hand)})\n"


def play_blackjack(game):
    game.reset_hands_and_deck()
    system("cls")
    print(f"Welcome to Blackjack!, Your tokens: {game.tokens.get_tokens()}")
    bet_amount = input(
        f"How much do u want to bet? max({game.tokens.get_tokens()}): ")
    while (not bet_amount.isdigit()) or (int(bet_amount) not in range(1, game.tokens.get_tokens() + 1)):
        system("cls")
        bet_amount = input(
            f"How much do u want to bet? max({game.tokens.get_tokens()}): ")
    bet_amount = int(bet_amount)
    system("cls")
    if game.open(bet_amount):
        print(game)
        check = game.check_for_blackjack()
        if check:
            game.token_distributor(check, bet_amount)
            return 1
        elif not check:
            while game.hit_or_stand():
                game.deal_a_card(game.user_hand, game.deck)
                if game.get_sum_of_cards(game.user_hand) == 21:
                    game.token_distributor("win", bet_amount)
                if game.check_for_bust(game.user_hand):
                    game.clear_screen_print_cards()
                    print("Bust!")
                    return 1
                game.clear_screen_print_cards()
            game.second_card_hidden = False
            while game.get_sum_of_cards(game.dealer_hand) < 17:
                game.deal_a_card(game.dealer_hand, game.deck)
                if game.check_for_bust(game.dealer_hand):
                    game.clear_screen_print_cards()
                    print("Dealer bust!")
                    game.tokens.amount += bet_amount * 2
                    return 1
            game.clear_screen_print_cards()
            check = game.check_for_win()
            if check:
                game.token_distributor(check, bet_amount)
                return 1


game = Blackjack()
play_again = True

while play_again:
    play_blackjack(game)
    play_again = True if input(
        f"You have {game.tokens.get_tokens()} tokens left\nPlay again? (y/n): ").lower() == "y" else False
system("cls")
print(f"Game over!, your ended up with {game.tokens.get_tokens()} tokens")
\$\endgroup\$
1
  • 1
    \$\begingroup\$ What quickly jumps into my mind is that the play_blackjack method is very long and complex. Try breaking that down into other functions. Also change the sequence of functions so that it reads from top to bottom. eg. play_game (which seems to be the game loop) first and it then jumps down to each function in the sequence they are getting called. Otherwise it's pretty easy to read. Great Job on that. \$\endgroup\$
    – elauser
    Commented Jul 17, 2020 at 11:50

1 Answer 1

1
\$\begingroup\$

Blackjack.hit_or_stand

This function can be shortened to the following

def hit_or_stand(self):
    while decision := input("(hit/stand h/s): ").lower()[0]:
        if decision in 'hs':
            return decision == 'h'

using python 3.8's new assignment expressions, and instead of returning true or false, returning the comparison. This evaluates to a boolean, so it does the same thing but looks a lot nicer.

Blackjack.get_sum_of_cards

This function can be shortened to the following

def get_sum_of_cards(self, hand):
    sum_of_cards = sum(card[1] for card in hand)
    aces = ["Ace of Spades", "Ace of Hearts", "Ace of Diamonds", "Ace of Clubs"]
    extra_sum = sum(10 for card in hand if card[0] in aces and sum_of_cards <= 11)
        
    return sum_of_cards + extra_sum

using pythons built in sum function. Also, putting all the aces into their own list makes it a bit clearer.

Blackjack.get_cards

This function can be shortened to the following

def get_cards(self, hand):
    return ', '.join(card[0] for card in hand)

using pythons string function join. This also removes the need to remove the last two characters.

Portability

Currently, clearing the screen only works on Windows, as cls is an invalid command on macOS/unix. Something like this will work fine:

import os

def clear_screen_print_cards(self):
    os.system('cls' if os.name == 'nt' else 'clear')
    print(self)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.