# Number guessing game

i'm a beginner and this is the second program that i have written by myself from scratch, i wrote a basic snake game with PYgame but i didn't do it by myself, so anyway, what do you guys think of this? i want honest opinions, no sugar-coating ! does it have any problems? is there any improvements that can be done?

import random
print "Guess my number!"
user_name = raw_input()
print "Hello %s, i'm thinking of a number between 1 and 20." %(user_name)
def gameloop():
random_number = random.randrange(1,21)
print "Take a guess."
vectory_message = "Well done %s, You beat me!" %(user_name)
defeat_message = "Sorry %s, You lost!" %(user_name)
attempts = 0
maximum_attempts = 7
user_number = int(raw_input())
attempts += 1
print "You have %s  lives left." %(maximum_attempts - attempts)
while True:
if user_number == random_number:
print vectory_message
attempts = 0
second_chance()

elif user_number > random_number:
user_number  = int(raw_input("A bit lower ..."))
attempts += 1
if attempts == maximum_attempts:
print defeat_message
second_chance()
print "You have %s  lives left." %(maximum_attempts - attempts)
continue

elif user_number < random_number:
user_number = int(raw_input("Guess higher ..."))
attempts += 1
if attempts == maximum_attempts:
print defeat_message
second_chance()
print "You have %s  lives left." %(maximum_attempts - attempts)
continue

def second_chance():
while True:
choice = raw_input("wanna give it another shot?(y/n)")
if choice == "y":
gameloop()
elif choice == "n":
quit()
else:
print "That wasn't a (y) or (n)!, Try again."
continue
gameloop()

• Questions asking for advice about code not yet written are off-topic. – BCdotWEB Apr 19 '16 at 12:28
• Hey, i have deleted the part where i ask for the extra functionality, i guess i'll have to figure that one out by myself. there, now it's not " not yet written" and it's done and i'm pretty sure it's within the rules. – Ahmed.fawzy Apr 19 '16 at 14:18

## Inappropriate function calls

The biggest conceptual error that you have made is treating functions as if they were goto labels. The top-level code calls gameloop(), which calls second_chance(), which in turn may call gameloop(), which calls second_chance(). The mutual recursion makes a deep stack trace, like this:

wanna give it another shot?(y/n)^CTraceback (most recent call last):
File "guess.py", line 50, in <module>
gameloop()
File "guess.py", line 20, in gameloop
second_chance()
File "guess.py", line 44, in second_chance
gameloop()
File "guess.py", line 20, in gameloop
second_chance()
File "guess.py", line 42, in second_chance
choice = raw_input("wanna give it another shot?(y/n)")
KeyboardInterrupt


The remedy is to change second_chance() to return True or False to indicate whether the user wants to play again. A properly structured program shouldn't have to call quit() to break out of that mess. It should just end naturally.

## "You have n lives left"

That line of code appears three times. Ideally, it should be written just once.

If you happen to guess the number correctly on the first shot, you still print "You have 6 lives left." before the victory message, which is weird. (You misspelled victory_message, by the way.)

Since you are substituting a number, you should use the %d format instead of %s.

## Counting loop

You don't really use attempts. Rather, it's always referenced as (maximum_attempts - attempts), so you might as well count down instead.

The idiomatic way to write a counting loop in Python is using some kind of range() or xrange().

## Suggested solution

In addition to fixing the issues above, this solution also reorganizes the code so that the function definitions are not interleaved with the top-level code. It also places the "I'm thinking of a number between 1 and 20" message next to the number-generating statement for maintainability.

import random

def play_game(user_name, maximum_attempts=7):
"""
Play a number-guessing game.  Return True if the number is guessed
correctly within the allotted attempts.
"""
random_number = random.randrange(1,21)
print "Hello %s, I'm thinking of a number between 1 and 20." % (user_name)
user_number = None

for remaining_attempts in range(maximum_attempts, 0, -1):
prompt = ("Take a guess." if user_number is None else
"A bit lower ..." if user_number > random_number else
"A bit higher ...")
user_number = int(raw_input(prompt + "\n"))
if user_number == random_number:
print "Well done %s, You beat me!" % (user_name)
return True
print "You have %d lives left." % (remaining_attempts - 1)
else:
print "Sorry %s, You lost!" % (user_name)
return False

def play_again():
"""
Ask if the user wants to play again.  Return True if the answer is "y".
"""
while True:
choice = raw_input("Wanna give it another shot? (y/n) ")
if choice == "y":
return True
elif choice == "n":
return False
else:
print "That wasn't a (y) or (n)!, Try again."

print "Guess my number!"
user_name = raw_input("What's your name? ")
while True:
play_game(user_name)
if not play_again():
break

• That's very helpful, clean and short. so thank you for that. just one silly question, what is the "/n" for? – Ahmed.fawzy Apr 19 '16 at 17:36
• The "\n" is a newline, to preserve the original behaviour where the cursor sits on the next line while waiting for user input. – 200_success Apr 19 '16 at 17:38
• ohh, now i get it, that's way much better than what i used to do. i used to just print the statement and then have the user input variable on a newline, which is really sloppy. – Ahmed.fawzy Apr 19 '16 at 17:40

The game loop should be structured differently. Also I'm ignoring second_chance.

There are two main things that should happen after I pick a number:

1. I'm correct, you say I win and the game ends.
2. I'm wrong, you tell me if I'm higher or lower.

This in psudocode could be:

while True:
user-input = get-user-input()
if user-input == random-number:
stop game
else:
display if user is higher or lower.


This should highlight that it's pretty much Python. And we could do with another function.

First, stop game should exit out of the game, and tell us if we won or not. This can simply be return True.

The logic to display if the user is higher or lower then the guess is an if and an else. If the guess is larger then you want to display "A bit lower ..." otherwise (the else) "Guess higher ...". To do this simpler I will use a turnery operator.

And so it'd be:

guess_text = "Take a guess."
random_number = random.randrange(1,21)
while True:
user_number = int(raw_input(guess_text))
if user_number == random_number:
return True
else:
guess_text = "A bit lower..." if user_number > random_number else "Guess higher..."


Ok so we now have a solid loop, but it's not much of a game. And the rest of your elif's are pretty good here. I'd change the attempts = 0 to return false and remove the continue.

This should get you:

guess_text = "Take a guess."
random_number = random.randrange(1,21)
attempts = 0
maximum_attempts = 7
while True:
user_number = int(raw_input(guess_text))
if user_number == random_number:
print "Well done %s, You beat me!" %(user_name)
return True
else:
guess_text = "A bit lower..." if user_number > random_number else "Guess higher..."

attempts += 1
if attempts == maximum_attempts:
print "Sorry %s, You lost!" %(user_name)
return False
print "You have %s  lives left." %(maximum_attempts - attempts)


Now you should know that return is only allowed in a function. This means you need to make a function that gets passed the user_name. So:

def play_game(user_name):
guess_text = "Take a guess."
attempts = 0
...


Finally you want to be able to play multiple games! I'll use second_chance for the basis for this loop.

• You should change quit() to break.
• Remove the continue.
• Put all the prints and definition for user_name at the top (not in the loop).
• Rename it as main.

This should get you:

def main():
print "Guess my number!"
user_name = raw_input()
print "Hello %s, i'm thinking of a number between 1 and 20." %(user_name)
while True:
choice = raw_input("wanna give it another shot?(y/n)")
if choice == "y":
play_game(user_name)
elif choice == "n":
break
else:
print "That wasn't a (y) or (n)!, Try again."


Finally I'd recommend using str.format over %.

So as you should guess, your code was pretty good! Apart from the duplicate questions.

• Well, thank you. but why is the str.format method is better than the % ? – Ahmed.fawzy Apr 19 '16 at 17:41
• @Ahmed.fawzy You can do a lot more with str.format then %. Also you don't have to know the type of input, '{}'.format is good for all, where with % you need to do'%s' % 'a' or %d % 1. There also is/was some quirks with % that meant it was warned against in the docs. – Peilonrayz Apr 19 '16 at 17:58