# Higher or Lower guessing game

After an extended absence from coding, I decided I would make something to try and get back on my feet again. I ended up making a project that I have made several times before, a higher-lower guessing game. Now while the coding process went well and I came out with something that works quite well, I feel like I fell into a lot of my old bad coding practices. I feel it would be good that before I move onto bigger projects that I post this here and see where I'm screwing up.

I would just like to know if there are any just general bad practices or things that could be made better.

from random import randint

def find_min_max() -> list:
"""Find ths minimum and maximum values for the number to be between"""
while True:
try:
min_max = [int(input("Please enter the min value: ")), int(input("Please enter max value: "))]
if min_max[0] < min_max[1]:
return min_max
else:
print("Make sure the min number is smaller than the max number.")
except ValueError:

def find_guess_limit():
"""Finds if the user wants a guess limit, and sets it if they do"""
while True:  # Finds if the user wants a guess limit
guess_limit = input("Would you like to limit your guess's? (Y/N): ").lower()
if (guess_limit == "y") or guess_limit == "n":
if guess_limit == "n":
return None
while True:  # Gets what the user wants the guess limit to be
try:
guess_limit = int(input("What would you like the guess limit to be: "))
if guess_limit > 0:
return guess_limit
else:
print("Make sure the number is greater than zero.")
except ValueError:
else:
print("Please only enter Y or N.")

def setup():
"""Sets up the game parameters"""
min_max = find_min_max()
guess_limit = find_guess_limit()
return min_max, guess_limit

def play_round(num: int) -> bool:
"""Goes through one guess by the player"""
while True:
try:
guess = int(input("What number is your guess: "))
if guess == num:
print("You got it!")
return True
elif guess > num:
print("The number is lower.")
return False
elif guess < num:
print("The number is higher.")
return False
except ValueError:

def reset() -> [bool]:
"""Checks if the player wants to quit, and if they don't, checks if they want to change the rules"""
while True:  # Checks if the user wants to quit the game
close = input("Would you like to exit the game? (Y/N): ").lower()
if (close == "y") or (close == "n"):
if close == "y":
quit()
else:
break
print("Please only enter Y or N.")
while True:  # Checks if the user wants the rules to be changed
rule_change = input("Would you like to change the rules? (Y/N): ").lower()
if (rule_change == "y") or (rule_change == "n"):
if rule_change == "y":
return True
else:
return False
print("Please only enter Y or N.")

min_max, guess_limit = setup()
while True:
num = randint(min_max[0], min_max[1])
if guess_limit is None:
while True:
if play_round(num):
break
else:
for _ in range(guess_limit):
won = play_round(num)
if won:
break
if not won:
print("Sorry, you ran out of guess's")
if reset():
min_max, guess_limit = setup()



## Return tuples

find_min_max is not well-suited to returning a list. Instead:

• Separate min_max into two variables, min_val and max_val
• return min_val, max_val
• Change your return hint to Tuple[int, int]

This is what setup already does, though setup would benefit from unpacking the result from find_min_max, and should get its own return type hint.

## Grammar

your guess's -> your guesses

## Input validation

if (guess_limit == "y") or guess_limit == "n":
# ...
else:
print("Please only enter Y or N.")


is better-stated, I think, as

if guess_limit == 'n':
return None

if guess_limit != 'y':
print("Please only enter Y or N.")
continue

# ...


This allows de-indentation of the rest of the loop, takes care of the simple cases first, and reduces the number of comparisons necessary.

Similarly,

    if (rule_change == "y") or (rule_change == "n"):
if rule_change == "y":
return True
else:
return False
print("Please only enter Y or N.")


can be

if rule_change in {'y', 'n'}:
return rule_change == 'y'
print("Please only enter Y or N.")


# Unnecessary construction of lists

min_max = [int(input("Please enter the min value: ")), int(input("Please enter max value: "))]


Unnecessary list construction here. It requires you to do min_max[] to get the correct value. Just remove the list construction and separate it into two variables.

min_val, max_val = int(input("Please enter the min value: ")), int(input("Please enter max value: "))

min_val = int(input("Please enter the min value: "))
max_val = int(input("Please enter the max value: "))


Your except block will catch any bad input. That's good! We can further improve it by doing the following

(optional)

Say I enter the first value correctly, but mess up when I input the second one. The problem here is I have to re-enter the first value again. We can solve this with just a few more lines of code, overall a better experience;

I personally prefer having a function like such

def num_input(prompt, err_msg = "Invalid input!"):
while True:
try:
num = int(input(prompt))
except Exception:
print(err_msg)
continue
break
return num


That way I can take input in the following manner

min_val = num_input("Please enter the min value: ")
max_val = num_input("Please enter the max value: ")


Now each variable has its own while True loop which ensures you get good, valid input.

# If-statement logic

        if (guess_limit == "y") or guess_limit == "n":
if guess_limit == "n":
return None
while True:  # Gets what the user wants the guess limit to be
try:
...
...

else:
print("Please only enter Y or N.")

• The parenthesis are not required

Notice you are checking for guess_limit == "y" twice here. How about you re-structure this and make it

        if guess_limit == "n":
return None
if guess_limit != "y":
print("Please only enter Y or N.")
continue
while True:  # Gets what the user wants the guess limit to be
try:
.......


Not only here, but there are 2-3 instances in your code where having a num_input function like the one in my previous point will reduce a lot of repetition. They all follow the same rule, put them in a function.

# Splitting work

def find_guess_limit():
"""Finds if the user wants a guess limit, and sets it if they do"""
while True:  # Finds if the user wants a guess limit
guess_limit = input("Would you like to limit your guess's? (Y/N): ").lower()
if (guess_limit == "y") or guess_limit == "n":
if guess_limit == "n":
return None
while True:  # Gets what the user wants the guess limit to be
try:
guess_limit = int(input("What would you like the guess limit to be: "))
if guess_limit > 0:
return guess_limit
else:
print("Make sure the number is greater than zero.")
except ValueError:

Better split this into two functions, wants_limit_guess() and find_limit_guess(). Follow the single-responsibillity-principle. That way you can do
if (wants_limit_guess())