3
\$\begingroup\$

This is the latest version of my Blackjack game and I did a quite big update to it. Now you can make an account that is saved in the MySQL database and you can bet money that are also saved in the database.

from random import shuffle
import os
import cymysql
from getpass import getpass
import sys


def shuffled_shoe():
    shoe = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'A', 'J', 'Q', 'K']*4
    shuffle(shoe)
    return shoe


def deal_card(shoe, person, number):
    for _ in range(number):
        person.append(shoe.pop())


def deal_hand(shoe, player, dealer):
    deal_card(shoe, player, 2)
    deal_card(shoe, dealer, 2)


def score(person):
    non_aces = [c for c in person if c != 'A']
    aces = [c for c in person if c == 'A']
    total = 0
    for card in non_aces:
        if card in 'JQK':
            total += 10
        else:
            total += int(card)
    for card in aces:
        if total <= 10:
            total += 11
        else:
            total += 1
    return total


def display_info(still_playing, player, dealer, money, money_bet, player_stands):
    os.system('cls' if os.name == 'nt' else 'clear')
    print(f"Money: ${money}")
    print(f"Money bet: ${money_bet}")
    print("Your cards:   [{}] ({})".format("][".join(player), score(player)))
    if player_stands:
        print("Dealer cards: [{}] ({})".format("][".join(dealer), score(dealer)))
    else:
        print("Dealer cards: [{}][?]".format(dealer[0]))
    first_hand = len(dealer) == 2
    if score(player) == 21:
        print("Blackjack! You won")
        still_playing = False
        money += money_bet * 2
    elif first_hand and score(dealer) == 21:
        print("Dealer got a blackjack. You lost!")
        still_playing = False
    elif score(player) > 21:
        print("Busted! You lost!")
        still_playing = False
    if player_stands:
        if score(dealer) > 21:
            print("Dealer busted! You won")
            money += money_bet * 2
        elif score(player) > score(dealer):
            print("You beat the dealer! You won!")
            money += money_bet * 2
        elif score(player) < score(dealer):
            print("Dealer has beaten you. You lost!")
        else:
            print("Push. Nobody wins or losses.")
            money += money_bet
        still_playing = False
    return still_playing, money


def hit_or_stand():
    while True:
        print("What do you choose?")
        print("[1] Hit")
        print("[2] Stand")
        ans = input("> ")
        if ans in '12':
            return ans


def bet():
    print("How much money do you want to bet?")
    money = int(input("> "))
    return money


def player_play(shoe, player, dealer, money, money_bet, player_plays, player_stands):
    while not player_stands:
        if hit_or_stand() == '2':
            player_stands = True
            player_plays = False
        elif not player_stands:
            deal_card(shoe, player, 1)
            display_info(True, player, dealer, money, money_bet, player_stands)
            if score(player) >= 21:
                player_plays = False
                break
    return player_plays, player_stands


def dealer_play(shoe, dealer, DEALER_MINIMUM_SCORE):
    while score(dealer) <= DEALER_MINIMUM_SCORE:
        deal_card(shoe, dealer, 1)
    return False


def play_again(cur, money, email):
    while True:
        print("\nDo you want to play again?")
        print("[1] Yes")
        print("[2] No")
        ans = input("> ")
        if ans == '1':
            return True
        elif ans == '2':
            cur.execute("UPDATE `users` SET `money`=%s WHERE `email`=%s", (money, email))
            cur.close()
            return False


def get_user_info():
    while True:
        email = input("Email address (max. 255 chars.): ")
        password = getpass("Password (max. 255 chars.): ")
        if len(email) < 255 and len(password) < 255:
            return email, password


def register(cur, email, password):
    cur.execute("INSERT INTO `users` (`Email`, `Password`) VALUES (%s, %s)", (email, password))


def login(cur, email, password):
    cur.execute("SELECT * FROM `users` WHERE `Email`=%s AND `Password`=%s LIMIT 1", (email, password))
    return bool(cur.fetchall())


def check_account(cur, email):
    cur.execute("SELECT * FROM `users` WHERE `Email`=%s LIMIT 1", (email,))
    return bool(cur.fetchone())


def start():
    print("Do you want to start playing? (Y)es/(N)o")
    ans = input("> ").lower()
    if ans == 'y':
        return True
    elif ans == 'n':
        return False


def main():
    conn = cymysql.connect(
        host='127.0.0.1',
        user='root',
        passwd='',
        db='blackjack'
    )
    with conn:
        cur = conn.cursor()
        email, password = get_user_info()
        checked = check_account(cur, email)
        if checked:
            loggedin = login(cur, email, password)
            if loggedin:
                print("You've succesfully logged-in!")
            else:
                print("You failed logging-in!")
                sys.exit()
        else:
            register(cur, email, password)
            print("You've succesfully registered and recieved $1000!")
        cur.execute("SELECT `money` FROM `users` WHERE `email`=%s", (email,))
        money = cur.fetchone()
        cash = money[0]
    keeps_playing = start()
    while keeps_playing:
        shoe = shuffled_shoe()
        player = []
        dealer = []
        still_playing = True
        player_plays = True
        player_stands = False
        money_bet = bet()
        cash -= money_bet
        deal_hand(shoe, player, dealer)
        still_playing, cash = display_info(still_playing, player, dealer, cash, money_bet, player_stands)
        while still_playing:
            while player_plays:
                player_plays, player_stands = player_play(shoe, player, dealer, cash, money_bet, player_plays, player_stands)
            still_playing = dealer_play(shoe, dealer, 17)
        still_playing, cash = display_info(still_playing, player, dealer, cash, money_bet, player_stands)
        keeps_playing = play_again(cur, cash, email)


if __name__ == '__main__':
    main()
\$\endgroup\$
2
\$\begingroup\$

A couple possible bugs:

  • When a user registers, there's text saying that they get $1000 to start. I don't see where this money comes from in the code.
  • In display_info(), if player_stands is True, I think it could be possible to display both "Busted! You lost!" and ""Dealer busted! You won"

I don't see the point of passing still_playing to display_info. If we don't pass it, set still_playing to True at the start of the function, the overall behavior doesn't change.

I think display_info() is doing an awful lot. It:

  • shows the current state
  • determines the winner
  • changes the player's money, and
  • Tells you who won/lost

I would try to separate these out.

hit_or_stand() returns a number, so the caller has to remember what it means. Consider using a constant or an enum instead.

The user's password gets stored in plaintext. Consider encrypting it.

It seems unconventional to have a function parameter be in all caps. Per PEP 8:

Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL."

So it would look more natural to do, e.g.

import sys

DEALER_MINIMUM_SCORE = 17

[...]

def dealer_play(shoe, dealer):
    while score(dealer) <= DEALER_MINIMUM_SCORE:
        deal_card(shoe, dealer, 1)
    return False

or maybe

def dealer_play(shoe, dealer, dealer_minimum_score):
    while score(dealer) <= dealer_minimum_score:
        deal_card(shoe, dealer, 1)
    return False

[...]

still_playing = dealer_play(shoe, dealer, DEALER_MINIMUM_SCORE)
\$\endgroup\$
  • \$\begingroup\$ First bug won't happen because in the database I chose 'money' to have a default value of 1000. Second bug won't happen either. If I set still_playing as True and I don't pass it to display_info, then if I get a Blackjack, I will have to play the entire game to see that. I will try to split display_info a bit. \$\endgroup\$ – Maria Laura Mar 24 at 12:16

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.