9
\$\begingroup\$

I am a beginner programmer ( just recently picked it up ). I just completed my first ever project, that is creating a Blackjack game in Python. I would like to know your opinions about it and how it can be improved! Thanks in advance. This code is original and I made it myself without any external assistance.

import random
import os
import time

def clear():
    os.system('cls' if os.name == 'nt' else 'clear')

def addPlayerCard(playerCards, cards):
    playerCards.append(random.choice(cards))

def addDealerCard(dealerCards, cards):
    dealerCards.append(random.choice(cards))

def addSum(hand):
    totalsum = 0
    hand2 = []
    for card in hand:
        if(card == 'A'):
            hand2.append(card)
            hand2.remove(card)
        else:
            hand2.append(card)

    for cards in hand2:
        if(cards == 'A'):
            hand2.append('A')
            hand2.remove('A')
        if(cards == 'J' or cards == 'K' or cards == 'Q'):
            totalsum += 10
        elif(cards == 'A'):
            if(totalsum + 11 <= 21):
                totalsum += 11
            else:
                totalsum += 1
        else:
            totalsum += cards
    return totalsum 

def showDealerHand(dealerCards):
    print("Dealer's hand: [X,", str(dealerCards[1]) + "]")

def showPlayerHand(playerCards):
    print("Player's hand: [" + str(playerCards[0]) + ", " + str(playerCards[1]) + "]")

def checkBlackjack(cards):
    flag1 = 0
    flag2 = 0
    if(cards[0] == 'A' or cards[1] == 'A'):
        flag1 += 1
        if(cards[0] == 'J' or cards[1] == 'J' or cards[0] == 'K' or cards[1] == 'K' or cards[0] == 'Q' or cards[1] == 'Q'):
            flag2 += 1
    if(flag1 + flag2 == 2):
        return True
    else:
        return False

def handCount(hand):
    handCount = len(hand)
    return handCount

def hitOrStand(playerCards, dealerCards, cards, fiveHandRule):
    i = 0
    while(i != 2):
        hitstand = str(input("\nDo you want to hit or stand?\n")).lower()
        if(hitstand[0] == "h"):
            addPlayerCard(playerCards, cards)
            flag1 = addSum(playerCards)
            flag2 = handCount(playerCards)
            clear()
            print("Dealer's hand: [X,", str(dealerCards[1]) + "]")
            print("Player's hand: " + str(playerCards))

            if(flag1 > 21):
                print("\nYOU BUSTED!")
                time.sleep(2)
                return True
            elif(flag2 >= 5 and flag1 <= 21):
                fiveHandRule.append("PASS")
                return True
        if(hitstand[0] == "s"):
            return True

def dealerCheck(dealerSum, dealerCards, cards, playerCards):
    if(dealerSum <= 16):
        print("Dealer's hand: " + str(dealerCards))
        print("Player's hand: " + str(playerCards) + "\n")
        print("_________________________________________________\n")
        print("The Dealer's Cards' Total Sum do not exceed 16.")
        print("The Dealer Hits!")
        addDealerCard(dealerCards, cards)
        dealerSum = addSum(dealerCards)
        time.sleep(2)
        return dealerSum

def blackjackCard(playerCards, dealerCards):
    print("You Got a Blackjack!\n")
    time.sleep(1)

    print("Revealing Dealer's Hand...")
    time.sleep(1)
    print("Dealer's hand: " + str(dealerCards))
    print("Player's hand: " + str(playerCards) + "\n")

    if(checkBlackjack(dealerCards)):
        print("The Dealer Also Has a Blackjack!\n")
        return 8
    else:
        print("The Dealer Does Not Have a Blackjack.\n")
        return 9

def winLose(pSum, dSum, pHandCount):
    if(pSum > dSum and pSum <= 21):
        return 1
    elif(dSum > pSum and dSum <= 21):
        return 2
    elif(pHandCount >= 5 and pSum <= 21):
        return 3
    elif(dSum == pSum and pSum <= 21 and dSum <= 21):
        return 4
    elif(pSum > 21 and dSum <= 21):
        return 5
    elif(dSum > 21 and pSum <= 21):
        return 6
    elif(pSum > 21 and dSum > 21):
        return 7

def gameEnd(result):
    if(result == 1):
        print("\nCongratulations, You Won! Your Cards' Total Sum Is Higher Than The Dealer's.")
        return 1
    elif(result == 2):
        print("\nYou Lost! The Dealer's Cards' Total Sum Is Higher Than Your's.")
        return 2
    elif(result == 3):
        print("\nCongratulations, You Won! You Have a 5 Card Hand Without Busting, Winning by 5-Card Charlie.")
        return 1
    elif(result == 4):
        print("\nIt's a Push! Your Cards' Total Sum Is The Same As The Dealer's!")
        return 3
    elif(result == 5):
        print("\nYou Lost! You Busted but The Dealer Did Not. The Total Sum of Your Cards Exceeded 21.")
        return 2
    elif(result == 6):
        print("\nCongratulations, You Won! The Dealer Busted but You Did Not. The Total Sum of The Dealer's Cards Exceeded 21.")
        return 1
    elif(result == 7):
        print("\nIt's a Push! Both Parties Busted. Both Parties' Total Sum of Their Respective Cards Exceeded 21.")
        return 3
    elif(result == 8):
        print("\nIt's a Push! Both Parties Have a Blackjack!")
        return 3
    elif(result == 9):
        print("\nCongratulations, You Won! You Have a Blackjack but The Dealer Does Not!")
        return 1

def gameEnd2(result):
    if(result == 1 or result == 3 or result == 6 or result == 9):
        return 1
    if(result == 2 or result == 5):
        return 2
    if(result == 4 or result == 7 or result == 8):
        return 3
    
def Blackjack():
    clear()
    cards = ['A','J','K','Q', 2, 3, 4, 5, 6, 7, 8, 9, 10]
    dealerCards = []
    playerCards = []

    addPlayerCard(playerCards, cards)
    addPlayerCard(playerCards, cards)
    addDealerCard(dealerCards, cards)
    addDealerCard(dealerCards, cards)
    showDealerHand(dealerCards)
    showPlayerHand(playerCards)
    checkBlackjack(playerCards)
    checkBlackjack(dealerCards)

    blackjack = False
    if(checkBlackjack(playerCards)):
        blackjack = True

    fiveHandRule = []
    if(blackjack == False):
        while(hitOrStand(playerCards, dealerCards, cards, fiveHandRule) != True):
            hitOrStand(playerCards, dealerCards, cards, fiveHandRule)

        if(len(fiveHandRule) <= 0):
            clear()
            print("Revealing Dealer's Hand...")
            time.sleep(1)
            dSum = addSum(dealerCards)
            if(dSum <= 16):
                while(dSum <= 16):
                    dealerCheck(dSum, dealerCards, cards, playerCards)
                    dSum = addSum(dealerCards)
            
            if(dSum > 16):
                print("Dealer's hand: " + str(dealerCards))
                print("Player's hand: " + str(playerCards))
            if(dSum > 21):
                print("\nTHE DEALER BUSTED!")
                time.sleep(2)

            pSum = addSum(playerCards)
            dSum = addSum(dealerCards)
            pHandCount = handCount(playerCards)

            winLose(pSum, dSum, pHandCount)

            result = winLose(pSum, dSum, pHandCount)

            gameEnd(result)
        else:
            print("Five-Hand Charlie!")
            time.sleep(2)
            result = 3
            gameEnd(result)
    else:
        result = blackjackCard(playerCards, dealerCards)
        gameEnd(result)

    input("Input any key to continue.\n")
    return result

def endgame(result, gamelog):
    clear()
    print("The Match Has Been Concluded.")
    flag = gameEnd2(result)
    if(flag == 1):
        print("Result: WIN")
        gamelog.append('W')
    elif(flag == 2):
        print("Result: LOSS")
        gamelog.append('L')
    elif(flag == 3):
        print("Result: TIE")
        gamelog.append('T')
    input("Input any key to continue.\n")
    clear()

def lobby(gamelog):
    totalgames = len(gamelog)
    totalwins = 0
    totallosses = 0
    totalties = 0
    for game in gamelog:
        if(game == 'W'):
            totalwins += 1
        elif(game == 'L'):
            totallosses += 1
        elif(game == 'T'):
            totalties += 1
    winrate = (2 * totalwins + totalties) / (2 * totalgames) * 100
    winrate = round(winrate, 2)
    
    print("Your Stats")
    print("_________________________________________________\n")
    print("TOTAL GAMES: " + str(totalgames))
    print("WINS: " + str(totalwins))
    print("LOSSES: " + str(totallosses))
    print("TIES/PUSHES: " + str(totalties) + "\n")
    print("YOUR WINRATE: " + str(winrate) + "%")
    print("_________________________________________________\n")

gamelog = []
print("Welcome to Darrance's Blackjack. Have fun!")
time.sleep(2)

def main():
    result = Blackjack()
    endgame(result, gamelog)
    lobby(gamelog)
    option = input("Would you like to play again? (Y/N)\n").lower()
    if(option[0] == 'y'):
        main()
    elif(option[0] == 'n'):
        clear()
        print("Thank you for playing Darrance's Blackjack!")
        input("Input any key to exit program.\n")
        clear()

main()
\$\endgroup\$
2
\$\begingroup\$

I felt the code was well written! Pretty modular, cleanly written code and very verbose print statements and a well-established interactive gameplay. :D

Overall

For a longer game, it might be a bit more readable to write the same code but using a Blackjack class or something similar. It allows you to extend the code to handle multiple ongoing games easily, save and load games, basically just treat each game as a single object and then you call methods on it to interact with the game. It won't be very difficult to convert most of what you've written into a class-format.

Important

  1. I do not understand the need of hand2.append('A') followed immediately by hand2.remove('A'). Since you begin hand2 as an empty list, these two lines will cancel each other and you will never have an 'A' in hand2, and the if block does nothing. Moreover, in the next for loop, you are editing hand2 while iterating over it. It is a bad idea to change a mutable list while iterating over it. Since hand2 will never contain 'A' in your case, you don't end up into trouble, but the practice of editing mutable objects while iterating over them can lead to quite problematic issues. Here is a replacement which works equivalent to the code you have. Does this do valid calculation of the the hand-sum?:
def addSum(hand):
    totalsum = 0
    hand2 = []
    for card in hand:
        if card != 'A':
            hand2.append(card)

    for cards in hand2:
        if(cards == 'J' or cards == 'K' or cards == 'Q'):
            totalsum += 10
        else:
            totalsum += cards
    return totalsum 

Game

I think the above point might break the core-game logic. Apart from that, I am not sure if the remaining code works as you want it to for a valid BlackJack game. I don't know BlackJack enough to be able to check the working of the core-game logic. However a couple of suggestions:

  1. Assuming it might be more accurate to play BlackJack with a full deck (13 cards of 4 suites), you can have the cards list contain all the 52 cards, and with every addPlayerCard or addDealerCard you remove the chosen card. This will keep the probabilities of the cards being selected more accurate than the current implementation. Currently, each card is equally likely as the next card, which is not the case when you start with a full deck. On removing an 'A' for example, you only have 3 more 'A' in the deck while each of the other cards have 4 of them in the deck making 'A' slightly bit less probable.

  2. Further additions and enhancements can include the possibility of splitting your hand, different rules for the dealer to hit or not etc.

Code shortening

  1. You can shorten some conditions for if making the code a bit more compact/readable. Here are three examples. Also, != True is the same as == False which can be written as shown below. The same shortening can be applied for gameEnd2.
# if(cards[0] == 'A' or cards[1] == 'A')
if 'A' in cards[:2]

# if(cards[0] == 'J' or cards[1] == 'J' or cards[0] == 'K' or cards[1] == 'K' or cards[0] == 'Q' or cards[1] == 'Q')
if (cards[0] in 'JQK') or (cards[0] in 'JQK'):

# if(blackjack == False)
if not blackjack:
  1. handCount is not needed, since you can easily use len wherever you use handCount. Furthermore, inside handCount function you define a variable and call it handCount. Such a renaming might make longer codes end up having difficult-to-debug bugs. It is a good idea to not give variables names which overlap with the already-used names, especially in the same scope.

  2. There are two calls to checkBlackjack(playerCards) inside the Blackjack function which do not store the saved value to anything and have no effect. You can remove them. Similarly, there is a call to winLose which does nothing, right above a useful call result = winLose(...) which does something.

  3. Instead of the for loop to count the number of wins, losses and ties, you can use list.count('W') with 'W', 'L', 'T'.

  4. Instead of the blackjack code block, you can directly use checkBlackjack where needed.

#    blackjack = False
#    if(checkBlackjack(playerCards)):
#        blackjack = True
#
#    fiveHandRule = []
#    if(blackjack == False):

fiveHandRule = []
if not checkBlackjack(playerCards):

Modularity (and further shortening :D)

  1. Since you already have the showDealerHand and showPlayerHand defined, you should use them in other functions instead of re-writing the print functions to display cards separately in all the other functions. You can rewrite the two functions so that they can work even when the player hand has more than two cards.

Misc

  1. In hitOrStand, the i variable is never changed, and hence, you will never get out of the while loop until you reach a return True statement. You can remove i completely, and keep while True. And in the BlackJack function where you call this hitOrStand you can replace the while loop with a single call to hitOrStand since it does not return anything but True and it always returns True when the function ends

# i = 0
# while(i != 2)
while True:

# while(hitOrStand(playerCards, dealerCards, cards, fiveHandRule) != True):
#           hitOrStand(playerCards, dealerCards, cards, fiveHandRule)

hitOrStand(playerCards, dealerCards, cards, fiveHandRule)

  1. Instead of calling main() recursively to continue playing game, you can use a single while loop to achieve the same result. While there is nothing wrong or bad in your implementation, you will keep creating recursive calls and recursive scopes inside the function without needing too. In a more complicated code, this might enter into space-issues or might reach the maximum recursion depth allowed by python. (Glossing over the possibility of tail-recursion since I am unsure how well is the python interpreter optimized for tail-recursion). Here is one alternative
#    result = Blackjack()
#    endgame(result, gamelog)
#    lobby(gamelog)
#    option = input("Would you like to play again? (Y/N)\n").lower()
#    if(option[0] == 'y'):
#        main()
#    elif(option[0] == 'n'):
#        clear()
#        print("Thank you for playing Darrance's Blackjack!")
#        input("Input any key to exit program.\n")
#        clear()
def main():
    result = Blackjack()
    endgame(result, gamelog)
    lobby(gamelog)
    while input("Would you like to play again? (Y/N)\n").lower()[0] == 'y':
        result = Blackjack()
        endgame(result, gamelog)
        lobby(gamelog)
    clear()
    print("Thank you for playing Darrance's Blackjack!")
    input("Input any key to exit program.\n")
    clear()

Minor comments

  1. len cannot be negative, hence len(x) <= 0 is the same as len(x) == 0
  2. In checkBlackjack, instead of the flags becoming 1 and then you checking if their sum equals 2, it might be a more readable implementation (i.e. your intent would be clearer) if you use flag as a boolean. See below
flag = True
if not ('A' in cards[:2]):
    flag = False
if not ((cards[0] in 'JQK') or (cards[1] in 'JQK')):
    flag = False
return flag
  1. input returns a string already, so you do not need to do str(input())
  2. For a prettier printing, you can use the .format method.
\$\endgroup\$
1
\$\begingroup\$

I'm new to coding so i can't analise the code in depth but i tried it and i found a minor issue. After the dealing if i press enter, the code stops winth IndexError: string index out of range. To avoid it i modified the hitOrStand function :

def hitOrStand(playerCards, dealerCards, cards, fiveHandRule):

i = 0
while (i != 2):
    hitstand = input("\nDo you want to hit or stand?\n")
    if hitstand == (""):
        print("Please type 'h'or 's' , reshuffling..\n"
              "")
        main()

    elif (hitstand[0] == "h"):
        addPlayerCard(playerCards, cards)
        flag1 = addSum(playerCards)
        flag2 = handCount(playerCards)
        clear()
        print("Dealer's hand: [X,", str(dealerCards[1]) + "]")
        print("Player's hand: " + str(playerCards))

Well done tho :)

\$\endgroup\$
1
\$\begingroup\$

I will add my minor review comment's to already present reivews.

Python provides great simplicity to coding if statements.

#if(pSum > dSum and pSum <= 21):
if(21 >= pSum > dSum):

#elif(dSum > pSum and dSum <= 21):
elif(21 >= dSum > pSum):

#elif(dSum == pSum and pSum <= 21 and dSum <= 21):
elif(dSum == pSum <= 21):

Since, gameEnd is only printing message and return result is not used. I might as well remove it completely.

def gameEnd(result):
    ...

replace this with an array and use that array.

print_result = [
    "",
    "\nCongratulations, You Won! Your Cards' Total Sum Is Higher Than The Dealer's.",
    "\nYou Lost! The Dealer's Cards' Total Sum Is Higher Than Your's.",
    "\nCongratulations, You Won! You Have a 5 Card Hand Without Busting, Winning by 5-Card Charlie.",
    "\nIt's a Push! Your Cards' Total Sum Is The Same As The Dealer's!",
    "\nYou Lost! You Busted but The Dealer Did Not. The Total Sum of Your Cards Exceeded 21.",
    "\nCongratulations, You Won! The Dealer Busted but You Did Not. The Total Sum of The Dealer's Cards Exceeded 21.",
    "\nIt's a Push! Both Parties Busted. Both Parties' Total Sum of Their Respective Cards Exceeded 21.",
    "\nIt's a Push! Both Parties Have a Blackjack!",
    "\nCongratulations, You Won! You Have a Blackjack but The Dealer Does Not!",
]

print(print_result(result)) # where ever gameEnd needs to print.

Since, you are having upto 9 possible result messages your gameEnd2 error checking is complicated. But this can be improved if you club results returning from blackjackCard and winLose as:

# Dealer Win at 2 and 5.
Make that 1, 2
# Tie at 4, 7 and 8.
Make that 3, 4, 5
# Player Win at 1, 3, 6 and 9
Make that 6, 7, 8, 9

# or any other clubbed combination

Now this will change the above array for prints. And simplify gameEnd2 if-else.

if result < 3:
    'Loss'
elif result < 6:
    'Tie'
elif result < 10:
    'Win'
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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