2

Using VS2015 Python 3.4
Having some issues with this while counter. It is slowly driving me insane as I'm sure it 'should' work but isn't updating the counter. I've ran stepping debug and can see the counter resetting to 3 before the while condition line. It is annoying me to say the least.

import random
import getpass
print ('Welcome to Rock, Paper or Sissors\nEnter an option.')
user_obj = getpass.getpass('Rock, Paper or Sissors: ').lower()
ai_obj = input('Rock, Paper or Sissors: ').lower()
rps = ('rock', 'paper', 'sissors')
#ai_rps = ['rock', 'paper', 'sissors']
#ai_obj = random.choice(ai_rps)
counter = 3
def rps_game(user_obj, ai_obj):
    print('Player selected %s ' % user_obj)
    print('Computer selected %s ' % ai_obj)
    condition = user_obj in rps and ai_obj in rps
    while condition == True and counter >= 0:     
        if user_obj == ai_obj:
            print('Its a draw!')
        elif user_obj == 'rock':
            if ai_obj == 'paper':
                print('You lose!')
                break
            else:
                print('You win!')
        elif user_obj == 'paper':
            if ai_obj == 'sissors':
                print('You lose!')
                break
            else:
                print('You win!')
        elif user_obj == 'sissors':
            if ai_obj == 'rock':
                print('You lose!')
            else:
                print('You win!')
                break    
    else:
        counter += 1 
        print('Invalid input, please select Rock, Paper or Sissors')
        rps_game(user_obj, ai_obj)

rps_game(user_obj, ai_obj)
Lafexlos
  • 7,618
  • 5
  • 38
  • 53
  • 3
    Are you getting an error? I'm getting an error. I get `UnboundLocalError: local variable 'counter' referenced before assignment`. Is that the error you're getting? – Kevin Dec 28 '15 at 14:40
  • This is true, however if I move the variable inside of the defined function, it turns the while loop infinite. Please excuse my ignorance I'm still pretty new to coding. – Kris Wolf Burton Dec 28 '15 at 15:01
  • You are not breaking out of your loop consistantly. In some points you break after you win, other times you keep playing. Check all of your loop breaking scenarios – FuriousGeorge Dec 28 '15 at 15:05
  • Also note that in the while loop you're checking that condition == True, but no where in your loop are you modifying the value of condition, so it's kind of pointless. – FuriousGeorge Dec 28 '15 at 15:07
  • Same with counter (if you never hit the last else statement) – FuriousGeorge Dec 28 '15 at 15:07
  • var condition is a comparison of input against accepted 'objects' in this case rock paper and sissors. – Kris Wolf Burton Dec 28 '15 at 16:30

2 Answers2

1

counter resets to its original value because it is a global variable. Global variables in Python behave differently than in other languages. Try this snippet :

counter = 3
def f():
    counter = 2 # Shadows global 'counter' by a newly defined one
f()
print (counter) # Prints 3 !

You could expect the printed value to be 2, but is 3 instead. Global variables are immutable by default, and attempt to modify it in a local scope without global keyword will shadow by local definition instead. See this discussion to use global variable although it would be preferable to rework your program to use local variables.

EDIT: There is also a mistake, counter is initialized to 3 and only incremented, so the condition >= 0 will always be satisfied (thus creating an infinite loop). You can try:

[...]
def rps_game(user_obj, ai_obj, counter):
    if counter <= 0:
         return
    print('Player selected %s ' % user_obj)
    print('Computer selected %s ' % ai_obj)
    while user_obj in rps and ai_obj in rps :
    [...]
    else:
        print('Invalid input, please select Rock, Paper or Sissors')
        rps_game(user_obj, ai_obj, counter-1)

rps_game(user_obj, ai_obj, 3)
Community
  • 1
  • 1
Diane M
  • 1,503
  • 1
  • 12
  • 23
  • I don't know why this answer got a downvote, it's perfectly correct. +1 – xXliolauXx Dec 28 '15 at 16:13
  • 1
    I'm not the downvoter, but globals are not immutable (at least not in the sense that tuples and strings are). Create a global list variable and append an item to it from a function without declaring it global. – pat Dec 28 '15 at 16:54
  • The first example you gave make prefect sense to me, and I understand (at least I think I do :P ) how subsequent value declaration outside of a function would behave differently. I worked out that moving the counter inside the def() coursed a infinite loop (which was because of the unsatisfied call on condition due to it using the original user input) But also, due to the counter variable not being 'updated' when counter -= was executed. * – Kris Wolf Burton Dec 28 '15 at 16:58
  • (I don't quite understand how counter += 1 came to be copied as I changed it from adding to count to subtracting from counter around 10am today. Sorry for that). Though I did not know you could add those kinda of changes in the argument of a func call. Thank you for that little gem :) Still at a miss why, even then the variable is changed to local . . . oh wait, . . . . . I bet that and statment isn't helping matters. *facepalm – Kris Wolf Burton Dec 28 '15 at 16:58
0

The else containing the counter update is indented so that it hangs off of the while. That means that it executes only when the while loop terminates because the condition is false (not when you break out). In this case, the condition is false when either the user or ai input is not in the list of valid inputs or the counter is negative. The counter can never be negative because it starts at 3 and is only ever incremented. If the user input is invalid, the error message is printed, the counter is incremented, and then you use recursion to call the function with the same arguments. Since the arguments haven't changed, the function should do the same thing it just did, unless it depends on global data (counter). However, if counter is local (initialized inside the function), each invocation of the function gets a fresh copy. If counter is global (initialized outside the function, but declared global inside the function), it still won't work because you need to decrement it. And even then, because none of the other inputs have changed, it will just print the error message over and over.

Your loop bound appears to expect counter to count down from 3 since it keeps going as long as counter is positive. Incidentally, using >= as the conditional will make the loop execute 4 times (3, 2, 1, and 0). You might want to change the conditional to strictly greater than, and change the counter increment to a counter decrement.

Next, you are using recursion (the function calls itself), but this should be unnecessary since the loop gets you back to where you need to be.

Next, the lose cases break out of the loop, but the win cases do not. If you win, the code will loop, not modifying the counter or any of the other inputs, so it will not terminate.

Finally, the user input is read from outside the function, so it never changes inside the loop. If the user enters an illegal value, the loop will just keep testing the same value. You probably want to read the user input inside the loop.

pat
  • 12,587
  • 1
  • 23
  • 52