# Automate the boring stuff with python - Guess the number

import random

def guessNumber(guess):
if guess == randomNumber:
return 'Good job!'
elif guess > randomNumber:
return'That is to high!'
elif guess < randomNumber:
return 'That is to low!'

guesses = 0

randomNumber = random.randint(1, 20)
# print(randomNumber)

print('Im thinking of a number between 1 and 20.')

print('Take a guess')
guesses = guesses + 1
break

print(f'{answer}, You guessed my number in {guesses} guesses!')



I have been trying to learn programming for on and off a year. Currently I am on a hot streak of 2 weeks!

This is my solution. I did this without cheating or first looking at the answer code. I am very happy with myself that I did this one on my own.

• Micro-review: spelling is incorrect in user messages - should be "That is too high/low". Jun 9, 2023 at 5:59
• Check that user input is numeric before converting it to int.
– nico
Jun 9, 2023 at 7:13

If the user input can't be converted to int, we'll get an exception (and ugly backtrace) here:

answer = guessNumber(int(input()))


I'd be inclined to create a function for reading integer input, something like this:

def input_int(prompt):
while True:
try:
s = input(prompt)
return int(s)
except ValueError:
print(f"Sorry, {s} isn't a valid integer")


It's usually a good idea to put your overall functionality in a function called main, and only call that if this is the top level.

if __name__ == '__main__':
main()


This becomes more important when you start writing Python modules, but even at this stage, it helps give the right scope to our variables.

I don't like using the global variables. guesses is easily removed, since it's only used in the main() loop. But randomNumber is also used by guessNumber(), so we'll need to pass it in as a parameter:

def guessNumber(guess, actual):
⋮


Comparing the user-displayed string seems like a reasonable idea now, but there's actually a problem lurking there. When we add translation to support more languages, that simple test will no longer work.

I think the best fix for that is a complete restructure, so that main() sees your hidden number and the user's guess (so it chooses the message to display, and breaks out when guess == actual).

• Thank you very much, i will try to redo the exercises with the information you gave me! Jun 9, 2023 at 11:04
• I've never understood the if __name__ == '__main__': main() motivation. Why not just put the effective script from main() under the if statement instead of adding an extra step? Jun 9, 2023 at 13:55
• @AndrewHolmgren That way gives you the option of executing the code by importing the file (e.g. from another script or the REPL) and calling main(). (Also makes it easier to parametrise inputs to the script as arguments to main()).
– Seb
Jun 9, 2023 at 14:03
• @AndrewHolmgren: Putting code inside a function is also better for performance in the CPython interpreter, since variables can be local variables, instead of having to do name lookup up in the global scope. why the local function is faster in python and how to prove? / Performance with global variables vs local Jun 12, 2023 at 4:31

to high should be too high.

guessNumber should be guess_number by PEP8.

Im should be I'm.

guesses = guesses + 1 can be guesses += 1.

A good program separates its input/output from its business logic, among other reasons so that you can unit test the logic, and so that you can swap out the interface with a different one. There are many ways to do this; I demonstrate one below. This uses some mid-level concepts that will probably pull you out of beginner material, but I consider that healthy.

from random import randint
from typing import Iterator, Iterable

GUESS_LOWER = 1
GUESS_UPPER = 20

def get_guess() -> int:
"""
Keep asking the user for a guess until we get one that is numeric and within the guess bounds.
"""
while True:
try:
guess = int(input('Take a guess: '))
except ValueError:
print('Invalid number')
continue

if not (GUESS_LOWER <= guess <= GUESS_UPPER):
print('Number out of range')
else:
return guess

def calculate_game(guesses: Iterable[int], answer: int) -> Iterator[int]:
"""

:param guesses: An infinite iterator of user guess integers.
:param answer: The integer the user is trying to guess
:return: An iterator of -1, 0 or 1, meaning too-low, correct or too-high.
"""

for guess in guesses:
if diff == 0:
yield 0
break
yield diff // abs(diff)

"""Run the game with console input/output."""

# This makes an iterator that infinitely yields the return value of get_guess().
guesses = iter(get_guess, None)

responses = {
-1: 'That is too low!',
0: 'Good job!',
1: 'That is too high!',
}

# enumerate() gives us pairs: the number of guesses (starting at 1),
# and the diff from calculate_game().
for guess_count, diff in enumerate(calculate_game(guesses, answer), start=1):
print(responses[diff])
return guess_count

def main() -> None:
print(f"I'm thinking of a number between {GUESS_LOWER} and {GUESS_UPPER}.")
print(f'You guessed my number in {guess_count} guesses!')

if __name__ == '__main__':
main()

• "I consider that healthy" - agreed; that's a good program to learn from. Jun 9, 2023 at 14:36
while answer != 'Good job!':
print('Take a guess')

The break statement is not needed, since the while loop will stop once the the answer is 'Good job!'{
while answer != 'Good job!':