Bryce Gardner
Bryce Gardner

Reputation: 29

Python rock, paper, scissors game

I am using Python and I am trying to write a simple program that simulates a rock, paper, scissors game. Everything works except for when I enter an invalid response (something other than rock, paper, or scissors) when I get this error.

Traceback (most recent call last):
File "C:/Users/home/Desktop/BAGARDNER/Python/rock_pape_scissors.py", line 88, in <module>
main()
File "C:/Users/home/Desktop/BAGARDNER/Python/rock_pape_scissors.py", line 14, in main
number = user_guess()
File "C:/Users/home/Desktop/BAGARDNER/Python/rock_pape_scissors.py", line 48, in user_guess
return number
UnboundLocalError: local variable 'number' referenced before assignment

I understand that this is telling me that number isn't referenced, but from what I understand of the code, it shouldn't need a number when the qualifier is false.

#import random module
import random
#main function
def main():
    #intro message
    print("Let's play 'Rock, Paper, Scissors'!")
    #call the user's guess function
    number = user_guess()
    #call the computer's number function
    num = computer_number()
    #call the results function
    results(num, number)

#computer_number function
def computer_number():
    #get a random number in the range of 1 through 3
    num = random.randrange(1,4)
    #if/elif statement
    if num == 1:
        print("Computer chooses rock")
    elif num == 2:
        print("Computer chooses paper")
    elif num == 3:
        print("Computer chooses scissors")
    #return the number
    return num

#user_guess function
def user_guess():
    #get the user's guess
    guess = input("Choose 'rock', 'paper', or 'scissors' by typing that word. ")
    #while guess == 'paper' or guess == 'rock' or guess == 'scissors':
    if is_valid_guess(guess):
        #if/elif statement
        #assign 1 to rock
        if guess == 'rock':
            number = 1
        #assign 2 to paper
        elif guess == 'paper':
            number = 2
        #assign 3 to scissors
        elif guess == 'scissors':
            number = 3
        return number
    else:
        print('That response is invalid.')
        user_guess()

def is_valid_guess(guess):
    if guess == 'rock' or 'paper' or 'scissors':
        status = True
    else:
        status = False
    return status

def restart():
    answer = input("Would you like to play again? Enter 'y' for yes or \
    'n' for no: ")
    #if/elif statement
    if answer == 'y':
        main()
    elif answer == 'n':
        print("Goodbye!")
    else:
        print("Please enter only 'y' or 'n'!")
        #call restart
        restart()

#results function
def results(num, number):
    #find the difference in the two numbers
    difference = num - number
    #if/elif statement
    if difference == 0:
        print("TIE!")
        #call restart
        restart()
    elif difference % 3 == 1:
        print("I'm sorry! You lost :(")
        #call restart
        restart()
    elif difference % 3 == 2:
        print("Congratulations! You won :)")
        #call restart
        restart()

main()

Thank you for your help!

Upvotes: 2

Views: 33668

Answers (4)

Razani Dev
Razani Dev

Reputation: 11

Just change input to raw_input

Upvotes: 1

Henry Keiter
Henry Keiter

Reputation: 17188

Here's your problem:

if guess == 'rock' or 'paper' or 'scissors':

This line in is_valid_guess doesn't do what you think it does. Instead, it always returns True. What you're looking for is something like this:

if guess == 'rock' or guess == 'paper' or guess == 'scissors':

or more concisely:

if guess in ('rock', 'paper', 'scissors'):

The problem is that what you have always returns True because of how Python evaluates strings in a boolean context. The line if guess == 'rock' or 'paper' or 'scissors': evaluates as:

if (guess == 'rock') or ('paper') or ('scissors'):

What this means is that Python checks to see if guess == 'rock'. If that's true, the conditional evaluates to True. If it's false, the interpreter tries to evaluate bool('paper'). This always evaluates to True because all non-empty strings are "truthy". Therefore, your whole conditional is always True, and every string is "valid".

As a result, your code considers all strings "valid" and then blows up when it fails to assign a number to a guess that is not actually supported.


As a final note, your is_valid_guess method could be trimmed a bit, since you're just returning the result of your boolean expression. Rather than using the status variable as an intermediate, you can just compute the expression and return it right away. I also use the lower() method of string objects to allow for case-insensitive guessing, in case that's something you want to allow.

def is_valid_guess(guess):
    return guess.lower() in ('rock', 'paper', 'scissors')

You've got another issue, which you mentioned in the comments: you've implemented user_guess in a recursive fashion, so that it calls itself if the user enters an invalid guess. However, in this case, it does not return the result of the recursive call. You need to either return the recursive result by changing the last line of user_guess to:

return user_guess()

Or else you should make that function use a loop instead of recursion, which is what I would do, since the function is not inherently recursive. You can do something like this:

def user_guess():
    # get first guess
    guess = input("Choose 'rock', 'paper', or 'scissors' by typing that word. ")

    # If that guess is invalid, loop until we get a valid guess.
    while not is_valid_guess(guess):
        print('That response is invalid.')
        guess = input("Choose 'rock', 'paper', or 'scissors' by typing that word. ")

    # Now assign the (valid!) guess a number
    # This dictionary is just shorthand for your if/elif chain.
    guess_table = {
        'rock' : 1,
        'paper' : 2,
        'scissors' : 3
    }

    # Return the number associated with the guess.
    return guess_table[guess.lower()]

Upvotes: 3

Bucket
Bucket

Reputation: 7521

As other users have pointed out, you need to change your validation in is_valid_guess to:

if guess == 'rock' or guess == 'paper' or guess == 'scissors':

While this won't solve your immediate problem, it is good (upvote-worthy) advice, and will let you avoid some errors you would have run into.

Additionally, no matter what the user inputs, you always return what they type in. To prevent this, you must return user_guess() in your else block:

if is_valid_guess(guess):
    #if/elif statement
    #assign 1 to rock
    if guess == 'rock':
        number = 1
    #assign 2 to paper
    elif guess == 'paper':
        number = 2
    #assign 3 to scissors
    elif guess == 'scissors':
        number = 3
    return number
else:
    print('That response is invalid.')
    return user_guess() # <-- right here

Upvotes: 1

musical_coder
musical_coder

Reputation: 3896

Change

if guess == 'rock' or 'paper' or 'scissors':

to

if guess == 'rock' or guess == 'paper' or guess == 'scissors':

In fact, to make the function as streamlined as possible, just do this:

def is_valid_guess(guess):
    return guess == 'rock' or guess == 'paper' or guess == 'scissors'

Upvotes: 1

Related Questions