Testing Rock, Paper, Scissors. I pass, it doesn't!


#1

My code passes me along. I get the green button and the game plays well until....

I enter an "invalid choice". We write an interesting line or two to deal with it and low and behold it throws exceptions. Advisors couldn't make it work either. I have the code I wrote here, and a solution a friend helped me come up with (all his work, above my current knowledge level).

"""This is a rock paper scissors game. I am writing now to justify the term mulitline comment and make all of those quotation marks work for a living!"""

from random import randint
from time import sleep

options = ["R", "P", "S"]
lose_msg = "You lost!"
win_msg = "You win!"

def decide_winner(user_choice, computer_choice):
  print "Your selection: %s" % user_choice
  print "Computer selecting..."
  sleep(1)
  print "The computer's choice: %s" % computer_choice
  user_choice_index = options.index(user_choice)
  computer_choice_index = options.index(computer_choice)
  if user_choice_index == computer_choice_index:
    print "It's a tie!"
  elif user_choice_index == 0 and computer_choice_index == 2:
    print win_msg
  elif user_choice_index == 1 and computer_choice_index == 0:
    print win_msg
  elif user_choice_index == 2 and computer_choice_index == 1:
    print win_msg
  elif user_choice_index > 2:
    print "Invalid Choice!"
    return
  else:
    print lose_msg
    
def play_RPS():
  print "Rock, Paper, Scissors"
  user_choice = raw_input("Select R for rock, P for Paper, or S for scissors\n")
  sleep(1)
  user_choice = user_choice.upper()
  #computer_choice = options [randint(0,2)]
  computer_choice =  options [randint(0, len(options)-1)]
  decide_winner(user_choice, computer_choice)

play_RPS()

So, that's how I did it, and it passed me. But as stated won't deal with garbage input from the user. Chime in when you see my error, or if you have noticed the same thing yourself. I have an experienced friend help me from time to time, he was as puzzled as the Advisors I've chatted with.

Thanks again, have an interesting day!

PS, if you're interested in the optional solution I'll post in a later reply to avoid confusion.


RPS.py - Step 18 Question
#2

There is no validation of the user input?

In one line, what is your question?


#3

Isn't:

print "Invalid Choice!"'

validating improper user input? Is that what you mean?

Answering your last question:

is the user choice in the list?

I considered that the list had three items indexed 0,1, and 2. That if the user chose bigger than 2 it wouldn't be in the list. I figured I was covered in the other direction as '0' is the lowest index number for a list (correct). But it seems I have suggested an element that isn't in the list, and therefore the exception.

Forgive my layman's attempt at explaining myself.


#4

i needed some time to read over the instructions, this:

  elif user_choice_index > 2:
    print "Invalid Choice!"
    return

won't catch invalid user input. I will read the instructions again to see what it is suppose to do. (i seemed to have missed it)

personally, i would do the validation directly after i promted the user:

user_choice = raw_input("Select R for rock, P for Paper, or S for scissors\n") 
user_choice = user_choice.upper()
# validate userchoice here
sleep(1)

i mean, if the user picks something which is not in the list, it kind of sucks.

here is what i would do, i would simply compress the two lines where you prompt the user input and do the case conversion:

user_choice = raw_input("Select R for rock, P for Paper, or S for scissors\n").upper()

then i would build a while loop to validate the user entered something which is in the list:

user_choice = raw_input("Select R for rock, P for Paper, or S for scissors\n").upper()
while user_choice not in options:
   user_choice = raw_input("That is not a valid option\nSelect R for rock, P for Paper, or S for scissors\n").upper()

now i am going to read the instructions again. not in is very useful to check if something is present in a list


#5

In the end I made it work here by using not in but suppose it would be ok either way?

The "hint" is fairly clear that this is how it has to be done. And though I couldn't make it work at home (Geany python 2.7) I could only get a "green button" with the code above.

It's fun, but odd. I appreciate your input and it was fun to see that you noticed the inefficiency of calling uppper where I did just like my buddy (who suggested not it). At this point in the course we haven't been shown those operators.

We're playing with in on Git at moment. Like I said, I'm a student, but he's a pro.

Jeez, this is good fun. I should have put down the soldering iron a while back :grinning:

Edit, am I missing something...the ~ doesn't seem to be doing what I expected.


#6

not in is a fine choice :slight_smile:

not is covered at this point in the course, right? in maybe not, i don't know, would have to check. The combination of not in is not covered indeed

I still have to check the instructions again, i will do that later. See where the instructions go wrong

the not in solution like i proposed doesn't work? Hm.. let me check

Nice that you have a friend who helps you :slight_smile:


#7

I can't recall being shown not at this point in the course.

And yes it is fun to have a friend help but I do feel as though I try his patience with my novice questions. I'm like a dog with a bone here. I got past the school code checker with the wrong answer!

I have to know why it is implied that this solution (my incorrect but passing code) is valid. It must be close to a working piece of code. I think my buddy would rather I move on.

So, it is also nice to have the forum, advisors and others who can fill in for him while he is earning a living! :slight_smile:


#8

not has been covered, i found the exercise here

yep, helping a novice can take a lot of patience, you should drink a beer together as a think you :wink: (if he likes beer or something) :stuck_out_tongue: He will appreciate that.

You can move on, gain more knowlegde and come back later

I thought the advisor was unable to help you?


#9

The advisor was great, but she felt I had written the code as instructed. She never did help me figure it out and it was submitted as a bug. If you check the suggested code (hints), I pretty much wrote it just as suggested to "pass". Later I went through it with yet another advisor who agreed the code wouldn't run as the hints suggested you write it. I figured most were satisfied to get the "green button" and move on. And maybe some forgot to check for the "Invalid response" when testing their code.

Now it's off to see if any of these correct methods will also get a passing grade (green button), and to re-read what I have forgotten or missed regarding not. Thanks for finding that for me (pink ears)!

This has been fun. I'd gladly buy you a beer, and have full intentions of doing the same for my aforementioned friend! I'll come back and post my results after kicking the tires. But I would also appreciate yours or anyone elses thinking regarding the RPS code as it is suggested (seen above). I am concerned that I'm not getting it, and so if others see this flaw (in the hints and the passing grade) I will feel a little better.

Edit: followed your link.. oh shoot yeah while studying Booleans! Blast, I had forgotten about it by the time we got up to lists! And connecting it with in shouldn't have been rocket science either, thanks again. I'll be back before they close the thread.


#10

okay, good luck! Let me know if you need any more help. I will keep this thread open :wink:


#11

Don't run off :grimacing:

So, I've studying this a for a minute and I have one quick question. Why won't this if statement `not in options' run when it's written into the first def in that string of elif statements?

def play_RPS():
print "Rock, Paper, Scissors"
user_choice = raw_input("Select R for rock, P for Paper, or S for scissors\n").upper()
if user_choice not in options:
print "Invalid Choice"
return
sleep(1)
#computer_choice = options [randint(0,2)]
computer_choice = options [randint(0, len(options)-1)]
decide_winner(user_choice, computer_choice)

That runs (here anyhow)

But why can't I:

elif user_choice_index not in options:
print "Invalid Choice!"
return

in the first decide_winner definition where I am comparing the computer's choice to the users in those elif statements? Here again, they were started there as it was suggested in by the instructions as I built it. If that order of constructing this game is poorly thought out, I would like to understand why. And in this case I just can't get that "not in" statement to run in that definition.

Thanks for your continued interest (now that's one heck of an assumption :wink:)


#12

Never mind, I see the difference and I am only leaving my stupid question here in case it might help someone else.

user_choice vs user_choice_index

oh duh, it's been a long day!


#13

take a break, work further tomorrow :slight_smile:


#14

As promised I am back. The code below, offered as a solution by you guys works just dandy here and at home.

from random import randint
from time import sleep

options = ["R", "P", "S"]
lose_msg = "You lost!"
win_msg = "You win!"

def decide_winner(user_choice, computer_choice):
  print "Your selection: %s" % user_choice
  print "Computer selecting..."
  sleep(1)
  print "The computer's choice: %s" % computer_choice
  user_choice_index = options.index(user_choice)
  computer_choice_index = options.index(computer_choice)
  if user_choice_index == computer_choice_index:
    print "It's a tie!"
  elif user_choice_index == 0 and computer_choice_index == 2:
    print win_msg
  elif user_choice_index == 1 and computer_choice_index == 0:
    print win_msg
  elif user_choice_index == 2 and computer_choice_index == 1:
    print win_msg
  else:
    print lose_msg
    
def play_RPS():
  print "Rock, Paper, Scissors"
  user_choice = raw_input("Select R for rock, P for Paper, or S for scissors\n").upper()
  if user_choice not in options:
    print "Invalid Choice"
    return
  sleep(1)
  #computer_choice = options [randint(0,2)]
  computer_choice =  options [randint(0, len(options)-1)]
  decide_winner(user_choice, computer_choice)

play_RPS()

But there wouldn't have been any way I could have arrived at that given the hint:

def decide_winner(user_choice, computer_choice):
    # ...
    if user_choice_index == computer_choice_index:
        # ...
    elif user_choice_index == 0 and computer_choice_index == 2:
        # ...
    # An elif statement goes here
        # ...
    # An elif statement goes here
        # ...
    elif user_choice_index > 2:
        # Print a message here
        # Exit the block here

What also bothered me, and I hope you might help me understand is, why I can use the not in statement in that definition but when I used it to replace index>2 in the previous definitions elif statement it would not run. In fact it through the same exception. I tried that, in order to try the statements in the same order suggested in the hint.

Do you follow me? :confused:


#15

so i tried to code it, but i forgot to save, and then something went wrong... pff.. not going to start again

yea, but the problem is here:

user_choice_index = options.index(user_choice)

even in the documentation (which is linked to in the exercise), says the following:

list.index(x)
Return the index in the list of the first item whose value is x. It is an error if there is no such item.

it tells you an error is raised if the index doesn't exist, of course you could use this to your advantage:

try: 
    user_choice_index = options.index(user_choice)
except ValueError:
   print "that is not a valid option!"

but this also hasn't been taught.

The other thing, i hate the if/elif statements. I think they are horrible in-efficient (while the lesson is talking about efficiency, lol). i would do the following:

  if user_choice_index == computer_choice_index:
    return "It's a tie!"
  return lose_msg user_choice_index == (computer_choice_index + 1) % len(options) else win_msg

and then print the returned result:

print decide_winner(user_choice, computer_choice)

but that might a bit above the level of the exercise, sorry, was bored :stuck_out_tongue:


#16

No, this is fine, I'm coming along here regarding the order of operations (sorry, jargon).

It begs the question, and I hate to keep harping on this, how did anyone get it to work following the hint (that I have pasted above from the project)?

elif user_choice_index > 2: print "Invalid Choice!" is just not going to work, correct?

And now as we talk further it would seem that this statement has to run in the second function play_RPS regardless.

Hope your time seems well spent :wink: And thanks again!


#17

this should be impossible, without getting creative

no, because here:

user_choice_index = options.index(user_choice)

.index() will throw an error if the item is not in the list.


#18

And now I think we've come to the close of this chapter... unless lurkers want to jump in and save this old dog I believe the subject is closed.

@stetim94 much thanks. If we ever meet, I owe you a beer (or two even).


#19

i doubt it, but i can of course ask other moderators to take a look :wink:

No problem, hope you understand it better now :slight_smile:


#20

The instructions for step 18 are wrong. They have the user do this:

elif user_choice_index > 2:
  print "Invalid user choice!"
  return

The value of user_choice_index will not be greater than 2 even when the user has entered an invalid choice. Instead, the program will have crashed.

A quick fix would be to modify the instructions to have the user do this instead:

if user_choice not in options:
  print "Invalid user choice!"
  return

However, this if block would need to be moved to the beginning of the decide_winner function, and definitely prior to this statement:

user_choice_index = options.index(user_choice)

Otherwise the call to the index method would still raise an error when the user enters an invalid choice. The correct policy for handling invalid input is to insulate the user from a program crash. End users should not have to deal with run time errors. Either display an informative message or take some other appropriate action, such as using a default value.

A reasonable means of dealing with the issue in this project would be to depart from the instructions by creating a function that collects and validates the user input within a while loop. Have it return the input when it is valid.