5
\$\begingroup\$

I want to make my code more efficient by instead of repeating my code again for level two, as the only change from level one to level two is the random numbers change from 1-10 to 1-100. I think I can make these a variable making max_number = 10 for level one and max_number = 100 for level two and use the same code, halfing the amount of lines I have in this code. However, I am unsure of how to do this as I am not extremely experienced. I have gotten tips to use a for loop, and I was wondering if anyone could help me with this:

#The two imports, import modules. They allow me to use functions defined elsewhere such as when I import a random number or
#use sys.exit() to terminate the game
import random
import sys 

#Ask the user for name, use in opening comments

user_name=str(input("Please type in your name: "))
#print instructions
print('''Hello {}! Welcome!
This game is going to develop your number skills! 

So here is how to play:
Firstly, {}, we are going to give you two numbers.
Then you must choose which of these numbers you think is bigger or which number is smaller.
Type this number in and if you are right you will get a point.
Get enough points and you can progress to level two!
''' .format(user_name, user_name))

#Set the scores of both user and computer to 0
user_score = 0
comp_score = 0
level = 1


#Only use the loop when the user score is below three
#Then randomly select to either have type_question to be bigger or smaller and this will define which path the program will take
while user_score < 3:
    bigorsmall = ['bigger', 'smaller']
    type_question = random.choice(bigorsmall)

#Import two random integers, then make sure these integers are not the same
    a1 = random.randint(1, 10)
    a2 = random.randint(1, 10)
    while a1 == a2:
        a2 = random.randint(1, 10)

    print("\nThe two random values are {} and {}. \n " .format(a1, a2))

#-----------------------------------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------------------------------
#If the type of question is bigger than the loop to ask for the bigger number is used
    if type_question == 'bigger':
        bigger = max(a1, a2)

#Ask the user to type in which they think is the bigger number
#The while strand means that no other integers then the integers given are allowed
#The except strand of the loop means that only integers can be entered

        while True:
            try:
                user_num = int(input("Which number is bigger:"))
                while user_num != a1 and user_num != a2:
                    user_num = int(input("Please pick either {} or {}:" .format(a1, a2)))
                break
            except ValueError:
                print("That is not an integer, please try again.")

#If users number is correct then give the user one point, if not give computer a point.             
        if user_num == bigger:
            print("\nCorrect, you get one point, keep playing you are doing great!")
            user_score+=1
        else:
            print('\nSadly that is wrong, keep trying! The bigger number was {}, the computer gets one point.'.format(bigger))
            comp_score+=1

        print("Your score is: {} \nThe computers score: {}" .format(user_score, comp_score))

#-----------------------------------------------------------------------------------------------------------------------
#This is the same loop as previously however the purpose is for the user to find the SMALLER number
#This loop is used if type_question was computer generated randomly as smaller        
    elif type_question == 'smaller':
        smaller = min(a1, a2)

        while True:
            try:
                user_num = int(input("Which number is smaller:"))
                while user_num != a1 and user_num != a2:
                    user_num = int(input("Please pick either {} or {}:" .format(a1, a2)))
                break
            except ValueError:
                print("That is not an integer, please try again.")


        if user_num == smaller:
            print('\nCorrect, you get one point, keep playing you are doing great!')
            user_score+=1
        else:
            print('\nSadly that is wrong, keep trying! The smaller number was {}, the computer gets one point.'.format(smaller))
            comp_score+=1

        print("Your score is: {} \nThe computers score: {}".format(user_score, comp_score))       

#-----------------------------------------------------------------------------------------------------------------------
#encourage the user to keep playing + allow an option to quit the game

cont_game = input("\n{} you are doing great! If you would like to keep playing type 'yes' \nIf you would like to quit press any key and then enter:" .format(user_name))

if cont_game == "yes":
    print("\nYAY!")
else:
    print("Hope you had fun!")
    sys.exit() 

#-----------------------------------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------------------------------   
#Start of a level two
#Same rules apply so no need for new information
#This loop is the same as previous loops, so comments are only placed where changes have been made

user_score = 0
comp_score = 0
level = 2

print("YOU HAVE GOT TO LEVEL {}! \nThe numbers now could be between 1 and 100! \nSame rules apply.".format(level))

print("Your score has reset to 0 and you must get 5 points to win at this game.")

while user_score < 5:
    bigorsmall= ['bigger', 'smaller']
    type_question = random.choice(bigorsmall)

#In level two the integers could be from 1 to 100   
    a1 = random.randint(1, 100)
    a2 = random.randint(1, 100)
    while a1 == a2:
        a2 = random.randint(1, 100)

    print("\nThe two random values are {} and {}. \n " .format(a1, a2))

#-----------------------------------------------------------------------------------------------------------------------
    if type_question == 'bigger':
        bigger = max(a1, a2)

        while True:
            try:
                user_num = int(input("Which number is bigger:"))
                while user_num != a1 and user_num != a2:
                    user_num = int(input("Please pick either {} or {}:" .format(a1, a2)))
                break
            except ValueError:
                print("That is not an integer, please try again.")

        if user_num == bigger:
            print("\nCorrect, you get one point, keep playing you are doing great!")
            user_score+=1
        else:
            print('\nSadly that is wrong, keep trying! The bigger number was {}, the computer gets one point.'.format(bigger))
            comp_score+=1

        print("Your score is: {} \nThe computers score: {}" .format(user_score, comp_score))

#-----------------------------------------------------------------------------------------------------------------------
    elif type_question == 'smaller':
        smaller = min(a1, a2)

        while True:
            try:
                user_num = int(input("Which number is smaller:"))
                while user_num != a1 and user_num != a2:
                    user_num = int(input("Please pick either {} or {}:" .format(a1, a2)))
                break
            except ValueError:
                print("That is not an integer, please try again")

        if user_num == smaller:
            print('\nCorrect, you get one point, keep playing you are doing great!')
            user_score+=1
        else:
            print('\nSadly that is wrong, keep trying! The smaller number was {), the computer gets one point.'.format(smaller))
            comp_score+=1

        print("Your score is: {} \nThe computers score: {}".format(user_score, comp_score))       

#-----------------------------------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------------------------------
#End of game, automatically terminate the program
print("You have won the game, I hope you had fun!")
sys.exit()
\$\endgroup\$
0
4
\$\begingroup\$

Comments vs documentation

Comments should be used to explain how the code works. Things that don't need to be explained are non-esoteric language features (for example you don't need to comment on what imports do). In general you want your code to be as self-explanatory as possible, without the use of comments.

You generally don't need comments like

#-----------------------------------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------------------------------

that just separate two sections - well organized code (and judicious use of whitespace) should do that just fine.

Documentation should be used to explain what the code is intended to do. This is generally done with a module level docstring and then class/function/method level docstrings as appropriate.

PEP8

In general, unless there is a pressing reason not to (i.e. you're working in a larger code base that has it's own style, or you're doing an assignment for a class that requires a certain style, or you really don't like PEP8 styles), you should try to follow the PEP8 coding conventions. This includes things like naming:

  • Variables and functions should be named using snake_casing
  • Classes should be named using PascalCasing
  • Constants should be named using UPPER_SNAKE_CASING

It also includes spacing - for example you almost always want to use a single space to clearly separate operators from their arguments, items in a list, parameters to a function, etc.

This includes indentation - you generally want your comments to be at the same indentation as the thing they're commenting about.

Lastly, line-length. You generally want lines to be, at most, 79 characters long and your docstrings to be, at most, 72 characters long. Your text editor/IDE should have a way to set a ruler that shows you when you've gone too far.

Constants

You should define constants at the top of your file. For example,

BIG_OR_SMALL = ['bigger', 'smaller']

Also avoid magic numbers - you have things like

while user_score < 3:

which could be better defined this way

# Maps from the current level to the upper score limit
SCORE_LIMIT = {
    1: 3,
    2: 5
}

...

while user_score < SCORE_LIMIT[level]:

You can also set constants as members of an Enum, which is a convenient way to group related constants.

from enum import Enum
...
GAME_MODES = Enum('GAME_MODES', 'BIGGER SMALLER')

Limit the scope of try blocks

A try block exists to do something you know might cause an error. You generally want to put as little as possible inside of that try block so you only catch an error from the specific line you think will cause it

    try:
        user_num = int(input("Which number is bigger:"))
    except ValueError:
        print("That is not an integer, please try again.")
    else:
        while user_num != a1 and user_num != a2:
            user_num = int(input("Please pick either {} or {}:" .format(a1, a2)))
        break

Use functions!

Right now you have a lot of repeated code - we can use functions to consolidate that. Additionally, a named function can make it much more clear what you're trying to do without the use of comments. Right now you have code like

a1 = random.randint(1, 10)
a2 = random.randint(1, 10)
while a1 == a2:
    a2 = random.randint(1, 10)

that could be more clearly represented as

def get_two_random_numbers(start, end):
    first = random.randint(start, end)
    second = random.randint(start, end)
    while first == second:
        second = random.randint(start, end)

    return first, second

...

a1, a2 = get_two_random_numbers(1, 10)

All together

Here is your full program, rewritten in a way I think makes sense. Not everything I've changed in here was explicitly mentioned in the review above, but I think it should all be self explanatory from the previous comments

import random
import sys

from enum import Enum


user_name=str(input("Please type in your name: "))
print('''Hello {0}! Welcome!
This game is going to develop your number skills! 

So here is how to play:
Firstly, {0}, we are going to give you two numbers.'''
'Then you must choose which of these numbers you think is bigger or which '
'number is smaller.'
'''
Type this number in and if you are right you will get a point.
Get enough points and you can progress to level two!
'''.format(user_name))

# Maps from the current level to the upper score limit
USER_SCORE_LIMIT = {
    1: 3,
    2: 5
}
SCORE_RANGE = {
    1: (1, 10),
    2: (1, 100)
}
GAME_MODES = Enum('GAME_MODES', 'BIGGER SMALLER')


def get_two_random_numbers(start, end):
    first = random.randint(start, end)
    second = random.randint(start, end)
    while first == second:
        second = random.randint(start, end)

    return first, second


def choose_game_mode():
    return random.choice(list(GAME_MODES))


def get_user_guess(options):
    user_num = None
    while user_num not in options:
        try:
            user_num = int(input("Which number is bigger? "))
        except ValueError:
            print("That is not an integer, please try again.")
        else:
            if user_num not in options:
                print("Please pick either {} or {}.".format(*options))


def play_round(correct_answer, options):
    return get_user_guess(options) == correct_answer


def continue_playing():
    cont_game = input("\n{} you are doing great! If you would like to keep playing type 'yes' \nIf you would like to quit press any key and then enter:" .format(user_name))

    return cont_game == "yes"


def play_game(level):
    user_score, computer_score = 0, 0

    score_range = SCORE_RANGE[level]
    print("You are playing at level {}!\nThe numbers can be between {} and {}".format(level, score_range[0], score_range[1]))

    while user_score < USER_SCORE_LIMIT[level]:
        numbers = first, second = get_two_random_numbers(*SCORE_RANGE[level])
        game_mode = choose_game_mode()

        print("\nThe two random values are {} and {}. \n " .format(first, second))

        if game_mode is GAME_MODES.BIGGER:
            correct_answer = max(first, second)
            message_string = 'bigger'
        else:
            correct_answer = min(first, second)
            message_string = 'smaller'

        if play_round(correct_answer, numbers):
            print("\nCorrect, you get one point, keep playing you are doing great!")
            user_score+=1
        else:
            print('\nSadly that is wrong, keep trying! The {} number was {}, the computer gets one point.'.format(message_string, correct_answer))
            computer_score+=1

        print("Your score is: {} \nThe computers score: {}".format(user_score, computer_score))

    if continue_playing() and level == 1:
        play_game(level + 1)
    else:
        print("Thanks for playing!")

    sys.exit()

if __name__ == '__main__':
    play_game(1)
\$\endgroup\$
2
  • \$\begingroup\$ Hi, thank you very much it looks very good, however because I am new at code and this a teacher may look over he will not think I did this, I dont understand this bit def get_two_random_numbers(start, end): is there anyway ti simplify my code will fewer changes. What does def mean? :) \$\endgroup\$ – SophieSarf Aug 4 '15 at 22:35
  • 2
    \$\begingroup\$ @SophieSarf I didn't write up this code review so you could turn it in as your own work for an assignment - I wrote it because this is the advice I would give to anyone for their code. If you are having trouble understanding what def means I suggest you learn about functions (I included a link in the post body) \$\endgroup\$ – Dan Oberlam Aug 5 '15 at 2:30
2
\$\begingroup\$

Nitpicks

There is no need to convert the result of input( ... ) to a str using the str function. Rather, you can do something like this:

user_name = input("Enter your name: ")

There is no need to call sys.exit at the end of your program. It will automatically terminate.

You also have a few style issues. Here's a small list:

  • You should have spaces between operators. (Mathematical, binary, assignment)
  • You have some magic numbers in places, like 3, or 10. Magic numbers should be assigned to a variable, for clarity.
  • Words in variables should be underscore separated. Names like bigorsmall should be renamed to big_or_small.

You also have some useless comments that can be removed, like the separator comments #--- ... or the comments at the beginning of your file. If you want to determine if a comment is needed, read the code the comment is describing. If the code is understandable without the comment, the comment can be removed.


Proper input validation

There are also many places where you get user input, and then validate it, like this:

cont_game = input( ... )

if cont_game == "yes":
    print("YAY")
else:
    print("Hope you had fun")

There are a few things wrong with doing something like this. What if the user enters something like "Yes", "y", or "yes."? The best way to do this would be to remove all non-alphanumeric characters, and lower the string like this:

import re

...

continue_game = re.sub(r"[^a-zA-Z0-9]", "", input("Would you like to play again? ").lower()))

This is a pain to type more that once though, so you can extract this into a re-usable function, like this:

def clean_input(prompt):
    return re.sub(r"[^a-zA-Z0-9]", "", input(prompt).lower()))

Now, you can just do this:

continue_game = clean_input("Would you like to play again? ")

if continue_game in ["yes", "y"]:
    ...
else:
    ...
\$\endgroup\$
1
  • \$\begingroup\$ I suggest to move your most important points to the top. Input validation is definitely more important than nitpicks, and it sparks more interest in readers \$\endgroup\$ – janos Aug 4 '15 at 20:00
1
\$\begingroup\$

I believe the exercise is to teach you how to write and use functions. Functions allow you to reuse code you have written. They allow you to break your code into smaller pieces that provide a function and return a value. They make it much easier to debug programs you have written. For instance you can do your integer input in a function as I show in the example below. Functions decrease the total amount of code you have to write and debug. They make your program more readable.

def input_check_integer_bigger() :
#Ask the user to type in which they think is the bigger number
#The while strand means that no other integers then the integers given are allowed
#The except strand of the loop means that only integers can be entered

    while True:
        try:
            user_num = int(input("Which number is bigger:"))
            while user_num != a1 and user_num != a2:
                user_num = int(input("Please pick either {} or {}:" .format(a1, a2)))
            return user_num
        except ValueError:
            print("That is not an integer, please try again.")
\$\endgroup\$
1
  • 1
    \$\begingroup\$ That comment underneath the function signature should be a docstring. \$\endgroup\$ – Ethan Bierlein Aug 4 '15 at 16:23

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.