The previous day, I helped someone build a really simple console "Lucky Number" game. It's really simple, the user has to choose a number, the program will also choose one, and if there's a match between the two, the user wins.
To made the game a bit more visual, the numbers are initially blue and displayed in a nice way.
I'm looking for the following things in a review, but any advice is welcome:
- is the code structured ok?
- did I use the correct data structures along the program? (e.g.: is it okay for
Colors
to be anEnum
, shouldnumbers
be anumber
:color
map and so on) - are type annotations used correctly?
- are the function / variable names suggestive enough?
- is there anything in terms of performance that can be improved?
Here's the code:
import random
import sys
import time
from enum import Enum
from typing import Dict
LINE_WIDTH = 5
EXTRA_PADDING = 6
# seconds to wait until the next number
# is picked up
SECONDS_TO_WAIT = 5
MIN_NUMBER = 1
MAX_NUMBER = 30
class Colors(Enum):
red = '\033[91m'
green = '\033[92m'
blue = '\033[94m'
end = '\033[0m'
def assign_color_to_number(number: int, default_color: str) -> str:
"""
Show ANSI colors in terminal for nicer UX.
Arguments:
number (int): A number between `MIN_NUMBER` and `MAX_NUMBER`
default_color (str): The color of a number
Returns:
str: Each number with it's own color
"""
for color in Colors:
if color.name == default_color:
return f'{color.value}{number}{Colors.end.value}'
else:
return f'{number}'
def print_table(numbers_color_map: Dict[int, str]) -> None:
"""
Print a nice table with colorful numbers wrapped after
`LINE_WIDTH` numbers.
Arguments:
numbers_color_map (dict): A map between numbers and their color.
"""
print('\n')
padding = max([int(len(color.name)) for color in Colors]) + len(str(MAX_NUMBER)) + EXTRA_PADDING
for number, default_color in numbers_color_map.items():
colored_number = assign_color_to_number(number, default_color).ljust(padding)
print(colored_number, end=' ') if number % LINE_WIDTH != 0 else print(colored_number)
print('\n')
def countdown(seconds: int) -> None:
"""
Let user know how much until the next game.
Arguments:
seconds (int): How many seconds until next game.
"""
for second in range(seconds, -1, -1):
sys.stdout.write(f"\r{second} seconds remaining until next "
f"number will be picked.")
time.sleep(1)
sys.stdout.flush()
print('\n')
def get_user_number() -> int:
"""
Sanitize user's input.
Returns:
int: User's chosen number.
"""
while True:
try:
value = int(input(f'Please chose a number between '
f'{MIN_NUMBER} and {MAX_NUMBER}: '))
if MAX_NUMBER < value < MIN_NUMBER:
print(f"Please insert a number between {MIN_NUMBER} and "
f"{MAX_NUMBER}!\n")
continue
return value
except ValueError:
print(f"Please insert a number between {MIN_NUMBER} and "
f"{MAX_NUMBER}!\n")
continue
def play():
numbers_color_map = {
number: 'blue' for number in range(MIN_NUMBER, MAX_NUMBER + 1)
}
print_table(numbers_color_map)
user_number = get_user_number()
print(f"You chose {user_number}. GOOD LUCK!\n\n")
countdown(SECONDS_TO_WAIT)
random_number = random.randint(MIN_NUMBER, MAX_NUMBER)
if user_number != random_number:
numbers_color_map.update({
random_number: 'green',
user_number: 'red'
})
print_table(numbers_color_map)
print(f' Your number: {user_number}\n'
f'Computer number: {random_number}\n\n'
f'You did not win. GOOD LUCK NEXT TIME!')
else:
print('You guessed it! Congrats! Please fill in the information '
'below in order to receive your prize.\n\n')
# reset our numbers to their initial color
numbers_color_map.update({
random_number: 'blue',
user_number: 'blue'
})
def main():
while True:
os.system('cls' if os.name == 'nt' else 'clear')
print('\nWELCOME TO THE LUCKY NUMBER!')
play()
answer = input('\nPlay again? [Y]es/[N]o: ')
if answer.lower() == 'n':
break
print('\nTHANKS FOR PLAYING!')
if __name__ == '__main__':
main()
You can either c/p the code and run it locally or play it here.