7
\$\begingroup\$

The following Python code is a guessing game in which players have to guess a number between 1 and 10. They only get 3 guesses, and the code will also display if the number is too high or too low, which the player tries to guess. When the while loop exits, because either the number of guessesleft has reached its limit of three guesses or the number has been guessed. The code after the while loop will then determine why the while loop exited. If its because the player guessed the number, then the player wins. If its because the player depleted the 3 guesses, the player loses. I'm wondering if this is a good way to write it. It works, but is this acceptable? I did look at other solutions, but one used a for loop and one a function, but at this moment I'm not at functions yet, so without functions again would this be acceptable.

#Guessing game

import random
name = input("Hello player 1 may I have your name please ? ")
computerguess = random.randint(1,10)
guessesleft = 3
print("Hello player " + name + " provide a guess between 1 and 10 please, you have " + str(guessesleft) + " left" )


playerguess = int(input())

guessesleft-=1

while computerguess != playerguess and guessesleft > 0 :
    if playerguess < computerguess:
        print("Your guess " + str(playerguess) +  " is too low try again ! " + str(guessesleft) + " left")

    elif playerguess > computerguess:
        print("Your guess " + str(playerguess) + " is too high try again ! " + str(guessesleft) + " left")

    print("Hello player " + name + " provide a guess between 1 and 10 please")

    playerguess = int(input())
    guessesleft-=1

if playerguess == computerguess:
    print("YEEESSSS That was it the number I was looking for was " + str(computerguess) + " and you had " + str(guessesleft) + " turns left")


else:
    print("Sorry you lose buddy , you have " + str(guessesleft) + " left the number I had in mind was "+ str(computerguess))

\$\endgroup\$
3
  • \$\begingroup\$ The other reviews are pretty good; one comment I have is use underscores to separate words, e.g. player_guess instead of playerguess, guesses_left instead of guessesleft, etc. If you want to dig in to Python naming conventions, you can check out peps.python.org/pep-0008/#naming-conventions for more details, but if you only use underscores to separate words and ignore the rest of the conventions, you'll increase your code readability significantly. \$\endgroup\$
    – hlongmore
    Jul 17, 2023 at 22:52
  • \$\begingroup\$ Haven't you learnt about for loops and break statements either? I was going to suggest using that instead of while. \$\endgroup\$
    – md2perpe
    Jul 18, 2023 at 9:03
  • \$\begingroup\$ You can change your elif ... just to else. You already check that its not equal in the while check, so if playerguess < computerguess is false, then playerguess > computerguess must be true. So no need to check for that. Saves a bit of code and makes it cleaner \$\endgroup\$ Jul 18, 2023 at 13:26

2 Answers 2

5
\$\begingroup\$

The main issue I see in the code is that you repeat the following two lines of code:

playerguess = int(input())
guessesleft-=1

They happen before the loop, and then again inside the loop. You should avoid repeating code because it makes it harder to maintain the code: if you need to make a change to one of these lines, you need to remember to make the same change in all the copies.

Instead, turn the loop logic upside down. Inside the loop, ask for the number first, then test it. I guess the important keyword here is break. It will exit the loop.

guessesleft = 3

while guessesleft > 0:
    print("Hello player " + name + " provide a guess between 1 and 10 please, you have " + str(guessesleft) + " left" )
    playerguess = int(input())
    guessesleft -= 1

    if playerguess < computerguess:
        print("Your guess " + str(playerguess) +  " is too low try again ! " + str(guessesleft) + " left")
    elif playerguess > computerguess:
        print("Your guess " + str(playerguess) + " is too high try again ! " + str(guessesleft) + " left")
    else:
        break

You can do the same thing without the break statement if you initialize the playerguess variable to a value that is not the right guess, like so:

guessesleft = 3
playerguess = -1  # any number guaranteed not to be equal to computerguess

while computerguess != playerguess and guessesleft > 0:
    print("Hello player " + name + " provide a guess between 1 and 10 please, you have " + str(guessesleft) + " left" )
    playerguess = int(input())
    guessesleft -= 1

    if playerguess < computerguess:
        print("Your guess " + str(playerguess) +  " is too low try again ! " + str(guessesleft) + " left")
    elif playerguess > computerguess:
        print("Your guess " + str(playerguess) + " is too high try again ! " + str(guessesleft) + " left")

This initialization forces the while loop to run at least once, so you always get to ask the user a number.

I personally prefer the break version, because it's more explicit, it's readily apparent what the logic is.


You should also learn about "string interpolation", which is the feature where variables inside a string are replaced with their values. This type of string starts with an f before the opening quotes, like so:

print(f"Your guess {playerguess} is too low try again! {guessesleft} guesses left.")

On the very last line, where you do

print("Sorry you lose buddy , you have " + str(guessesleft) + " left the number I had in mind was "+ str(computerguess))

guessesleft is guaranteed to be 0. The program logic doesn't allow it to reach this line unless the user is out of guesses.

\$\endgroup\$
3
  • \$\begingroup\$ I'd avoid using break. It's best if the loop declaration says when the loop ends. \$\endgroup\$
    – JollyJoker
    Jul 18, 2023 at 8:25
  • \$\begingroup\$ @JollyJoker I think break could have a valid use here, especially in conjunction with else. Since there are two ways to exit the loop (one being a win condition and the other a loss condition), you could distinguish them by either exiting the loop normally or by breaking out. \$\endgroup\$
    – Seb
    Jul 18, 2023 at 12:42
  • 1
    \$\begingroup\$ @Seb People have been arguing about break and continue for ages. Here's a Stackexchange question \$\endgroup\$
    – JollyJoker
    Jul 18, 2023 at 13:24
4
\$\begingroup\$

Structuring

Currently you just have one module that executes all its code once it is interpreted. This is fine for trivial scripts, but is a bad habit to get used to. Consider breaking up your code into functions and calling the main function within an if __name__ == '__main__' guard, so that the code does not run when imported as a module.

Naming

You use lower case names for variables, which is mostly okay. However, PEP 8 suggests snake_case for a reason. In composit words, the underscore can help improving readability. Consider player_guess instead of playerguess and guesses_left instead of guessesleft etc.

Don't repeat yourself

The hints I gave regarding structuring the program can also help you to get rid of duplicate code, such as print("Hello player " + name + " provide a guess between 1 and 10 please").
Also you check playerguess == computerguess twice, by checking the inverse in the loop condition first. Again, restructuring your program may improve upon this.

Know your functions

The built-in input() you use takes an optional prompt as argument, so you don't need to print it manually before calling input().

Use f-strings if possible

Don't concatenate variables with strings using a str() conversion and the + operation. Instead, use f-strings to automatically interpolate the variables.

Use type-hints

In small scripts this may (or may not) be an unnecessary overhead. But once a project grows, it's good to have a visible cue in the function's signatures as to what type of argument they expect and return.

Suggested change:

"""A number guessing game."""

from random import randint


def guessing_game(player_name: str, target: int, guesses: int = 3) -> None:
    """Play the guessing game as long as the user has guesses left."""
    
    while guesses > 0:
        guesses -= 1
        
        if evaluate_guess(read_guess(player_name), target, guesses):
            return
            
    print(f"Sorry you lose buddy, you have {guesses} left. The number I had "
          f"in mind was {target}")


def read_guess(player_name: str) -> int:
    """Let the player guess an integer."""
    
    while True:
        try:
            guess = int(input(f"Hello player {player_name} provide a guess "
                              f"between 1 and 10 please: "))
        except ValueError:
            continue

        if 1 <= guess <= 10:
            return guess


def evaluate_guess(player_guess: int, target: int, guesses_left: int) -> bool:
    """Evaluate the user guess and display a message."""

    if player_guess == target:
        print("YEEESSSS That was it the number I was looking for was "
              f"{player_guess} and you had {guesses_left} turns left")
        return True

    if player_guess < target:
        print(f"Your guess {player_guess} is too low, try again! "
              f"{guesses_left} left")
    else:
        print(f"Your guess {player_guess} is too high, try again! "
              f"{guesses_left} left")

    return False
        

if __name__ == "__main__":
    guessing_game(
        input("Hello player 1 may I have your name please ? "),
        randint(1, 10),
        3
    )
\$\endgroup\$
3
  • \$\begingroup\$ OP said “I'm not at functions yet” — they are still learning the very basics. You should adjust your review to OP’s level, otherwise they won’t learn much. \$\endgroup\$ Jul 17, 2023 at 17:48
  • 1
    \$\begingroup\$ I reviewed OP's question and my review and I don't see any reason to change it. Thank you anyway for your kind suggestion. \$\endgroup\$ Jul 17, 2023 at 18:44
  • 3
    \$\begingroup\$ It might be good to explain a bit more why using if __name__ == '__main__' is recommended: It make the functions testable without running all the code. One can import a single function from the module and run just it. \$\endgroup\$
    – md2perpe
    Jul 18, 2023 at 9:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.