Guess the number game using jupyter notebook

I made a number guessing game in Python 3 using jupyter notebook. I would appreciate it a lot if I could get some feedback on my code.

from random import randint
randnumb = randint(1,100)
guesses = 0
maxGuesses = 5

while guesses < maxGuesses:
personInput = int(input('What is your guess? '))
guesses = guesses + 1

if personInput == randnumb:
print()
print('You won! It took you', guesses, 'guess(es)!')
break

if personInput > randnumb and guesses != maxGuesses:
print('Guess lower', '(', maxGuesses - guesses,'guess(es) left)')
print()

if personInput < randnumb and guesses != maxGuesses:
print('Guess higher', '(', maxGuesses - guesses,'guess(es) left)')
print()

if guesses == maxGuesses and personInput != randnumb:
print()
print('You lost! You ran out of your guesses!')


Pretty clean looking code, but let's look at a few ways it could be better or more pythonic:

• Use elif statements:

if personInput == randnumb:
# ...
elif personInput > randnumb:  # You already know they're != at this point
# ...
elif ...

• Use snake case for your variable names. E.g., use person_input instead of personInput (which is camel case), and rand_num instead of randnumb. In that second case, it's also more common to shorten "number" to "num" instead of "numb".

• Use format strings instead of print's argument concatenation, e.g.:

print("Guess lower ({} guesses left)".format(max_guesses - guesses))

• Check for and handle errors:

try:
person_input = int(input("What is your guess? "))
except ValueError:
print("Not sure what you meant by that, please input an integer")
continue

• I prefer to be more safe than sorry when handling my own incrementor, such as guesses, just in case I do something funky that lets it skip over max_guesses:

# Since you "break" on correct answer, we don't need to check that again
if guesses >= max_guesses:
# ...


EDIT:

One more thing. In Python, to write a script (rather than a module), you should check if __name__ == '__main__': as follows:

from random import randint

def main():
randnumb = randint(1,100)
guesses = 0
maxGuesses = 5

while guesses < maxGuesses:
# ...

if __name__ == '__main__':
main()


This allows for safe code re-use, and makes sure that the code doesn't get run if this file gets imported from another file. It's not technically necessary for a one-file script like you're writing here, but it's a good practice to get into anyway.

• FYI: you don't need the explicit <!-- language --> declarations. – Daniel May 1 '18 at 21:27
• @Coal_ Heh, got my markdown code-reviewed :P I've just had issues on SO getting code to format properly between bullets; just double-indenting like that is the solution? – scnerd May 1 '18 at 21:28
• Yep, that's all. – Daniel May 1 '18 at 21:34
• I applied almost every tip to my code, especially the format strings is a really nice way of cleaning things up but I still had a few questions. In what context does it matter if I use elif instead of if after already using an if conditional? How can I make sure the player can only input a number between 1 and 100? What do you exactly mean with "you should check if name == 'main':", I don't really get what I should achieve with this piece of code and what it does. – Ian May 2 '18 at 19:03
• @Ian elif is equivalent to else: if, and should be used when only one of a series of conditions should be entered, exclusive to the others. In your case, the guess is either above, below, or equal to the correct answer, it can never be more than one of those. elif captures this logic cleanly and simplifies your conditions in the process. – scnerd May 2 '18 at 19:06

Just a few notes:

• You should display the range from which the number has been chosen, and specify if the endpoint is included or not (i.e. can the number be 100?).
• 5 guesses aren't enough to find the number reliably, even with a perfect strategy (a binary search). Do you want your game to include a part of chance? If not, max_guesses should be 7 (math.ceil(math.log(100, 2)))

Well done.

• You could make it better by 'encapsulating' it into a function, so that you can use it or use a modified version of it in your other projects.

• Instead of checking 'maxGuesses' several times, you can make use of logical flags.

• personally I prefer using "for loops" if you know number of iterations.

• maybe you can improve it by asking the user if he wants to play again or not.

from random import randint

def GuessNumber(maxGuesses=5):
randnumb = randint(1,100)
# a logical flag to check whether user guessed the number or not - at the end of all guesses
user_won = False

for i in range(maxGuesses):
personInput = int(input('What is your guess? '))
if personInput == randnumb:
print()
print('You won! It took you {} guess(es).'.format(i+1))
user_won = True
break

elif personInput > randnumb:
print('Guess lower, {} guess(es) left.'.format(maxGuesses-i-1))
print()

else:
print('Guess higher, {} guess(es) left.'.format(maxGuesses-i-1))
print()

# if user did not guess the number in all guesses
if not user_won:
print()
print('You lost! You ran out of your guesses!')


=====

update: a way to play it several times:

Here is what we want to do:

• we want to make a function, that works with "any" game.

• that function asks the user if he want to play again or not:

______ if he wants to play again, then we run the game again.

______ if he does not want to play again, then end the program.

• we can "break" the problem to smaller problems,

by making another function that asks the user if he wants to play again or not:

______ if he want, then that function returns True

______ if he does not want, then it returns False

• I encourage you to stop reading and try to do it yourself, and below is a code to do it.

=====

def playAgain():
"""returns True if user wants to play again, False otherwise."""
while True:
# ask user if he/she wants to play again
play_again = input('Play again? <y/n>:')
# if first character of user's answer is 'y', then return True.
# strip: is used to remove empty spaces that could be in start or end of user's input
# lower: is used to take the first character and make it in lowercase, for example
# if user types 'Yes' or 'yes' both will be accepted, and return True
if play_again.strip().lower()[0] == 'y':
return True
# if user types 'no' - either in lowercase or uppercase - return False
elif play_again.strip().lower()[0] == 'n':
return False
# if user writes another word that does not start with 'y' nor 'n', ask him again - that's why its a loop
else:
print('Wrong input! Please enter "yes" or "no"!')

def playSeveralTimes(game_func):
"""takes game's function - without () - runs it as many times as user wants."""
while True:
# run the game
game_func()
# if user does not want to play again - break out of loop
if not playAgain(): break

# run the game
playSeveralTimes(GuessNumber)

• Thanks a lot for the tips, I haven't been that busy with functions and for loops but I will definitely practice it more. I just had one question, how can I let the user play the game again? I tried some googling and playing around in the code but I can't seem to figure it out. – Ian May 2 '18 at 19:09
• @Ian You're welcome, added a way to the comment, I encourage you not to read the code directly, try to do it first yourself. – Yousef May 3 '18 at 12:00