4
\$\begingroup\$

I am familiar with Python but recently I have been trying to solidify my skills as a Python developer. I decided to do a project that would help me build some skills using the fundamentals of the language (limited modules, mostly just vanilla Python). I did this Slot Machine program, inspired by Tech with Tim, and I was wondering if I could get some feedback on my code about best practices in Python and improvements I can make to make my code more production-level.

CODE:

# Author: Kade Carlson
# Date: 12/30/2022
# Slot Machine program that is going to be used to test my skills. Slot machine of 3 lines, players can bet on a chosen number of
# lines from 1 to 3. If a row has all the same symbols then it's a win and their bet is multiplied by that symbol's value.

# Imports
import os
import random

# Constants
MAX_BET = 1000
MIN_BET = 10
SYMBOLS = ["@", "#", "%", "&"]
SYMBOL_VALUES = {"@": 2, "#": 3, "%": 5, "&": 7}


def set_balance():
    """Ask the user for the amount of money that they would like to deposit into their account."""
    balance = int(input("How much would you like to deposit?: $"))
    if balance <= 0:
        os.system("cls")
        print("Please input an amount greater than $0...")
        balance = set_balance()

    return balance


def set_bet(balance):
    """Ask the user for the amount of money that they would like to bet and on how many lines."""
    bet = int(input("How much would you like to bet?: $"))
    if not MIN_BET <= bet <= MAX_BET:
        os.system("cls")
        print(f"Please bet an amount between ${MIN_BET} and ${MAX_BET}")
        bet = set_bet(balance)

    if bet > balance:
        os.system("cls")
        print(f"Not enough money. Your balance is ${balance}.")
        bet = set_bet(balance)

    return bet


def set_lines(balance, bet):
    """Ask the user for how many lines they want to bet on."""
    lines = int(input("How many lines do you want to bet on (1-3)?: "))
    if not 1 <= lines <= 3:
        os.system("cls")
        print("You must bet on either 1, 2, or 3 lines")
        lines = set_lines(balance, bet)

    if bet * lines > balance:
        os.system("cls")
        print(f"Not enough money. Your balance is ${balance}. Your bet is ${bet}.")
        lines = set_lines(balance, bet)

    return lines


# Print the slot machine
def print_machine():
    """Prints the slot machine with the symbols in a 3x3 format."""
    row = []
    copy_row = []
    for i in range(9):
        row.append(random.choice(SYMBOLS))
        if (i + 1) % 3 == 0:
            print("", end="|")
            for symbol in row:
                print(symbol, end="|")
            print()
            print("-------")
            copy_row.append(row)
            row = []

    return copy_row


def get_symbol_value(symbol):
    """Returns the symbol value that the bet is multiplied by."""
    for key, value in SYMBOL_VALUES.items():
        if key == symbol:
            return value


# Check for wins, add winnings to deposit balance
def check_winnings(rows, bet, balance):
    for row in rows:
        for symbol in row:
            if symbol != row[0]:
                balance -= bet
                break
        else:
            balance += bet * get_symbol_value(symbol)

    return balance


def game(balance):
    """This function contains the game logic"""

    bet = set_bet(balance)
    lines = set_lines(balance, bet)
    print(f"Your balance is ${balance}")
    print(f"You bet ${bet}")
    print(f"You bet on {lines} lines for a total bet of ${bet * lines}")
    rows = print_machine()
    new_balance = check_winnings(rows[0:lines], bet, balance)
    print(f"Your new balance is: ${new_balance}")

    return new_balance


def main():
    balance = set_balance()
    new_balance = game(balance)

    query = input(
        "Press q to quit, d to deposit more, or any other key to make another bet: "
    )
    while query != "q":
        if new_balance < MIN_BET:
            query = input(
                f"Balance below minimum bet (${MIN_BET}). Press q to quit or d to deposit more: "
            )
            if query != "q" or query != "d":
                print("Can't bet more. Bye")
                break
        if query == "d":
            new_balance += set_balance()
            print(f"Your new balance is ${new_balance}")
            game(new_balance)
        else:
            game(new_balance)
        query = input(
            "Press q to quit, d to deposit more, or any other key to make another bet: "
        )


if __name__ == "__main__":
    main()

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Some of the initial comment lines look like they should be a module """docstring""".


The os.system() call is expensive and non-portable. You might assign cls = "\x1b[2J" and print it. Or import ansi and use erase.

Break out a def cls(): helper, since you use it several times.

        balance = set_balance()

That chews up stack space each time you call yourself. Prefer a while loop instead.


The get_symbol_value function is insane. It should just be return SYMBOL_VALUES.get(symbol), if you bother to define it at all.

Also, if not found, the current code falls off the end and returns None. Not clear if that was intended, since neither the docstring nor optional type hinting addresses that topic. If a value is always supposed to be found, then use return SYMBOL_VALUES[symbol]. That way you'll get a nice helpful diagnostic error if it turns out that symbol is not present.


# Check for wins, add winnings to deposit balance

You were doing great on docstrings, you were killing it. And now, bury it in a comment? No, no, promote this to a docstring, please.

And nuke the # Print the slot machine comment, as it isn't telling us anything new.

def print_machine():
    ...
    return copy_row

Hmmm, the identifier suggests that we evaluate for side effects, for printing, yet we're also computing some cool return value?!? Better document what that value is, please!


This seems to be mis-named:

def set_balance():

That is, we're maintaining a balance. But this function comes up with a value that will be added to the balance. It's not like it sets it.

We commonly encounter getters and setters, named get_foo() and set_foo(). This function is not one of those.


Overall, it looks pretty good, no big maintenance surprises.

Consider breaking out the balance, and the slot machine, into separate classes.

\$\endgroup\$
1
  • \$\begingroup\$ Wow, thank you so much for the feedback. This will be very helpful for me to continue to learn. \$\endgroup\$ Dec 31, 2022 at 17:15

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.