4
\$\begingroup\$

This is my take on the guess the number game. The user has three attempts to guess the number chosen at random. The number is between 0 and 9, including. It tells the user to guess higher or lower and reveals the number if the game is lost.

Input that is not a number between 0 and 9, including, is not accepted and the cursor will remain on the same line.

Green is for win and red is for retry.

I have tested the code and it works correctly. Please have a go.

guess_the_number.py

"""The user has three attempts to guess what number was chosen at random"""

__all__ = []

import random
import string

from colorama import deinit, init

right_answer = random.choice(string.digits)

as_int = int(right_answer)

MSG_ANS = 'The answer was ' + right_answer
MSG_ASK = 'Guess the number (0-9) '
MSG_OK = '\033[92mCorrect!\033[0m'
MSG_TRY = '\033[91mTry again *\033[0m'


def ask(msg_wrong):
    """
    One attempt

    Non-digits are not accepted

    Digits accepted are removed from the pool
    and are not accepted in subsequent attempts
    """
    global pool

    cursor_up = '\033[A'

    cursor_up_ask = cursor_up + '                       '

    while True:
        answer = input(MSG_ASK)

        if answer and answer in pool:
            break

        answer_sz = len(answer)
        answer_sz = answer_sz * ' '

        line = cursor_up_ask + answer_sz + cursor_up

        print(line)

        print()
        print()

        print(line, end='\r')

    if answer == right_answer:
        print(MSG_OK)

        exit()

    if msg_wrong == MSG_TRY:
        i = int(answer)

        if i > as_int:
            hint = '>'
        else:
            hint = '<'

        msg_wrong = msg_wrong.replace('*', hint)

    print(msg_wrong)

    pool = pool.replace(answer, '')


if __name__ == '__main__':
    pool = string.digits

    init()

    ask(MSG_TRY)

    ask(MSG_TRY)

    ask(MSG_ANS)

    deinit()
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Positives

  • Your function docstring was informative and helped me understand the code better.
  • Good use of variables like cursor_up to manage the ANSI control characters

Small improvements

  • As long as you're bringin in colorama, can you use any of the named constants in provides instead of the control characters?
  • If you need to do something multiple times, I like to see if there's a good way to do n times instead. See if you can make the number of turns more configurable.
  • I'm not sure what you're doing with line = cursor_up_ask + answer_sz + cursor_up. This would be a good place for a comment or a function with a clear name.
  • You're taking len(answer), but shouldn't that always be 1? My understanding is that you're expecting a one-digit answer. It would be nice to have some error handling so that, e.g. 23 isn't treated as a valid guess.
  • You could eliminate the pool global by wrapping it and your functions together in a class. You could also eliminate the global by having more focused functions.
  • Instead of doing a find-replace on the * in MSG_TRY, you can do something like this:
MSG_TRY = '\033[91mTry again {}\033[0m'
msg_wrong = msg_wrong.format(hint)

Larger gripes

  • I'm finding the flow of the program hard to follow. Consider breaking it down into smaller functions so it's more clear what's going on. It could look something like:
def play_one_turn(final_turn = False):
    guess = get_player_input()
    if guess == right_answer:
        print(OK_MSG)
        return True
    if not final_turn:
        show_hint(guess)
    else:
        print(MSG_ANS)

(I'm not saying this is the best way to do things. It's showing how smaller well-named functions can give more clarity with the same structure)

  • exit() in the middle of a function is suspicious. (Is the code missing from sys import exit?) I'd expect to see it at the end of a main function to return a status code back to the user. It should not be used for control flow. It would make more sense to return True or False depending on whether the guess is correct, and let the caller manage things from there.
\$\endgroup\$
-1
\$\begingroup\$

Your code does work, nice try :)

It's an interesting exercise. If you are open to another way of approaching the game, here's what I'd suggest:

import random
import string

def possible_answers() -> list:
    #['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    return [char for char in string.digits]

def generate_answer() -> str:
    return random.choice(possible_answers())

def ask_for_input() -> str:
    print('Your guess > ', end='')
    return input()

def new_game(max_attempts: int):
    correct_answer = generate_answer()
    attempts = []
    print('Welcome to the new round :) ')
    while len(attempts) < max_attempts:
        attempt = ask_for_input()
        if attempt == correct_answer:
            print('Bingo')
            break
        elif attempt not in possible_answers():
            print(f'Invalid input, possible answers: {possible_answers()}')
        elif attempt in attempts:
            print('You already guessed that one, my friend. Try something new :)')
        else:
            attempts += [attempt]
            print('Please try again, best of luck with the next attempt :)')
        

if __name__ == '__main__':
    while True:
        new_game(max_attempts = 3)
  • We are creating a new game after the previous game ends.

  • In Each game we:

    • accept a max_attempts as the game configuration

    • generate a random correct answer

    • accept attempts and act on the attempts accordingly

  • You can easily change the random set by changing the possible_answers() method

  • I'm ignoring output formatting here, but it can be easily added in the new_game() method.

If you don't mind doing a bit of reading, SOLID is something that's worth exploring. Hope you enjoy the reading and keep up the good work :)

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Am really considering downvoting this answer. You are providing an alternative code but without a reason why the said code is better than OP's code. \$\endgroup\$ Commented Jan 23, 2021 at 10:38

Your Answer

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