HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Python text game

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
textgamepython

Problem

I've written a simple text game in Python to show you. I would be glad if you would spare a quick look and point out the style errors so I wouldn't pick up bad habits.

``
""" A text game "Bob, the Cookie Cooker". """

import random

COINS_FOR_WIN = 1000000
FINE_COOKIE_PRICE = 30
POOR_COOKIE_PRICE = 10
PRICE_DEVIATION = 5
HUGE_DOUGH_AMOUNT = 1000
DOUGH_PRICE = 10
MAX_LOAN = 10000
LOAN_PRICE = 100
TICKET_PRICE = 1
LOTTERY_PRIZE = 2
BEG_MONEY = 8
TIME_COOKIE_COOK = 1
TIME_BIG_DEAL = 60
TIME_SMALL_DEAL = 30
TIME_GRANDMA = 60
TIME_BANK_SERVICE = 5
TIME_LOTTERY = 1
TIME_BEGGING = 1440
CHANCE_LOTTERY = 1000
CHANCE_SKILL_COOK = 1000
CHANCE_SKILL_GRANDMA = 100

cookies = 0
minutes = 0
skill = 0
coins = 0
dough = 0
bribe = 1
debt = 0
term = 0

def main():
""" The main function. """
intro()
while menu():
if check_for_end():
break

def intro():
""" Print's out a welcoming string. """
print ( """
.--,--.
. ,.' Bob,
|___| the Cookie Cooker
:o o: O
_~^~'_ |
/' ^
\=)
.' _______ '~|
`(> ")
if is_int(user_input):
user_input = int(user_input)
if min_bound != None and user_input max_bound:
print ("Number is too big.")
continue
break
return user_input

def is_int(string):
""" Returns if string can be converted into a positive integer or 0. """
try:
value = int(string)
return True
except ValueError:

Solution

This is a very good start - the code is broken up into logical functions, generally follows the style guide and includes explanatory docstrings. Well done!

One of the pet peeves in Python's style guide, which was being discussed on SO a short while ago, is using whitespace to line up operators, e.g.:

COINS_FOR_WIN        = 1000000
FINE_COOKIE_PRICE    =      30


would generally be written:

COINS_FOR_WIN = 1000000
FINE_COOKIE_PRICE = 30


This reduces the maintenance overhead if you add or rename a constant, or change one of the values.

You can use multiline strings and textwrap.dedent (see Avoiding Python multiline string indentation), rather than line after line of print, to neaten up the blocks of text:

print(textwrap.dedent('''
                                .--,--.
                                `.  ,.'          Bob,
                                 |___|    the Cookie Cooker
                                 :o o:   O      
                                _`~^~'_  |  
                              /'   ^   `\=)
                            .'  _______ '~|
                            `(<=|     |= /'
                                |     |
                                |_____|
                         ~~~~~~~ ===== ~~~~~~~~
       Welcome, my dear player! I am Bob, the Cookie Cooker! I dream to
       ...
'''))


There are generally two ways to avoid global state:

  • Pass all necessary state into and out of a function explicitly; or



  • Encapsulate the state in another object (e.g. a dictionary or class).



For the first, for example:

def cookie_loop(amount, minutes):
    """ Tries to cook and sell the specified amount of cookies. """
    for i in range(amount):
        minutes += TIME_COOKIE_COOK
        if dough > 0:
            cook_cookie(i)
        else:
            be_confused(i)
    return minutes


As the required state is passed in and returned explicitly, there's no need for global, but you now need to call e.g. minutes = cookie_loop(amount, minutes) rather than cookie_loop(amount) in work.

However, note that some functions take and modify many different parts of the state, so passing these all explicitly back and forth would quickly get out of hand:

minutes, coins, bribe = grandma(minutes, skill, coins, bribe)


This suggests encapsulation might be helpful, e.g. you could have a dictionary:

game_state = dict(
    cookies=0,
    minutes=0,
    skill=0,
    coins=0,
    dough=0,
    bribe=1,
    debt=0,
    term=0,
)


Now you only pass a single state parameter around, then e.g. state['minutes'] += TIME_COOKIE_COOK. Alternatively, you could look into OOP, and develop a CookieGame class that holds all of the state and provides methods for manipulating it, rather than passing it around to different functions.

In terms of splitting it up into smaller parts, note that the various tasks could be standalone functions, that take and mutate a single object representing a player (which holds e.g. the player's skill and money) and return the elapsed time, and could live in separate modules. For example:

from grandma import visit_grandma
from kitchen import bake_cookies
from supermarket import buy_dough

# import other standalone tasks

TASKS = {
    'Visit grandma': visit_grandma,
    'Bake some cookies': bake_cookies,
    'Buy some dough': buy_dough,
    # build dictionary of tasks
}

# user makes a choice from the keys

minutes += TASKS[key_choice](user)  # call task function with user object


This should make adding a new task as easy as importing the appropriate function and adding it to TASKS. Here e.g. grandma.py would hold all of the constants specific to that task, along with the task function (and any supporting sub-functions). Utility functions like get_valid_int would be in a separate file, e.g. utils.py, so that any task module can access them.

Code Snippets

COINS_FOR_WIN        = 1000000
FINE_COOKIE_PRICE    =      30
COINS_FOR_WIN = 1000000
FINE_COOKIE_PRICE = 30
print(textwrap.dedent('''
                                .--,--.
                                `.  ,.'          Bob,
                                 |___|    the Cookie Cooker
                                 :o o:   O      
                                _`~^~'_  |  
                              /'   ^   `\=)
                            .'  _______ '~|
                            `(<=|     |= /'
                                |     |
                                |_____|
                         ~~~~~~~ ===== ~~~~~~~~
       Welcome, my dear player! I am Bob, the Cookie Cooker! I dream to
       ...
'''))
def cookie_loop(amount, minutes):
    """ Tries to cook and sell the specified amount of cookies. """
    for i in range(amount):
        minutes += TIME_COOKIE_COOK
        if dough > 0:
            cook_cookie(i)
        else:
            be_confused(i)
    return minutes
minutes, coins, bribe = grandma(minutes, skill, coins, bribe)

Context

StackExchange Code Review Q#86904, answer score: 3

Revisions (0)

No revisions yet.