Everything we do is practice for something greater than where we currently are. Practice only makes for improvement.
– Les Brown
One of the more important skills with coding is understanding how to break down a problem into smaller chunks. Especially when you’re first beginning, it’s easy to immediately start typing lines of code before you have fully thought through the problem.
I’m going to walk you through how I approached a recent code challenge. I’m currently at the point where I can usually get the job done, but my solution is not always the most efficient possible. Once I walk through my initial approach, I’ll share my instructor’s feedback and show you my revisions.
The Challenge
Create a game where the computer guesses the user’s number (an integer between 1 and 100 OR a given minimum and maximum) in the fewest tries possible. Also include a way to check it the user is cheating.
Step 1 – Understand the Problem
I started by considering a simpler version of the problem – guess a number between 1 and 10. I created a list of the numbers 1-10 and considered the most efficient way to narrow down the possible numbers.
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/07/img_0546.jpg?w=1024)
I want to begin by guessing a number and asking the user if their number is less than, greater than or equal to my guess. If I start with a number close to one of the ends of the list, I may luck out and guess the answer quickly, but probability is not on my side here. As you can see, if 9 is guessed first, there is only a 20% chance that you can settle on the answer and an 80% chance that the target number is in the range of 1-8.
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/07/img_0547.jpg?w=1024)
A better method would be to guess the middle number, thus reducing the length of possible numbers by roughly half each time. If the list of possible numbers is even, the middle value would be a decimal. However, that would be a “wasted” guess, since the user’s number is an integer. So instead, I will round down to the next lowest integer.
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/07/img_0548.jpg?w=1024)
With each new piece of information from the user, the list of possible numbers is shortened until the user-selected number can be determined.
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/07/img_3d6e383d951a-1.jpeg?w=1024)
Step 2 – Plan Out the Control Flow
This problem is a great candidate for a while loop because the list of possible numbers is shortened with each iteration and I want the code block to execute until the list is short enough for the number to be determined.
I also know that I need a way to check for cheating. In order to do this, I decided that I would create a dictionary to keep track of each round, the user’s input (<, > or =) and the current range that the number is in.
Finally, I considered what code would need to be reused and could be placed within a nested function. I decided to make a function check_answer() that would verify a solution and also call out any cheating.
Step 3 – Write the Code
First, I created the function with a default minimum of 1 and maximum of 100. I created a list of possible numbers that spanned the given range and initialized a counter to keep track of how many iterations occur and an empty dictionary to hold the results of each iteration. Finally, I printed an invitation to the game.
def guessing_game(n_min = 1, n_max = 100):
possible_nums = list(range(1,101))
counter = 0
rounds = {}
print(f"Let's play a guessing game! Think of a number between {n_min} and {n_max}, inclusive.\n")
Next, I built the while loop that would guess a number, record and analyze the user’s response and create a new list of possible numbers. The loop is set to run as long as the list of possible numbers is greater than 1, because at that point, the user’s number can be determined.
On each iteration, the index of the middle number (or the middle number rounded down to the nearest integer is found using floor division. The number to be guessed is accessed using the index with the list of possible numbers. Also, the counter is incremented by one since a round is occurring.
Then, the user is asked how the guessed number relates to their number and their input is stored in the variable result. This result is then checked along with the length of the list. If the list still has a length greater than 3, a new list is created. Otherwise, there is enough information to call the nested function answer_check().
Finally, the dictionary is updated with the current value of the counter as the key and a tuple of (result, smallest possible number, largest possible number).
while len(possible_nums) > 1:
index = len(possible_nums)//2
guess_num = possible_nums[index]
counter += 1
result = input(f'Is your number <, > or = {guess_num}?')
if result == '=':
return answer_check(guess_num)
elif result == '<' and len(possible_nums)> 3:
possible_nums = possible_nums[:index]
elif result == '<' and len(possible_nums)<= 3:
return answer_check(possible_nums[0])
elif result == '>' and len(possible_nums)> 3:
possible_nums = possible_nums[index+1:]
else:
return answer_check(possible_nums[-1])
rounds[counter] = (result, guess_num, possible_nums[0], possible_nums[-1])
Once I was happy with the while loop, I created the function to check the answer and look for cheating. First, the user is asked if their number matches the guessed number and this is stored in the variable check.
If they respond in the affirmative, a confirmation message is printed. If they say “No,” the dictionary is updated once more to capture the final iteration and a for loop prints the results of each round, thus calling out the cheater.
def answer_check(number):
check = input(f"\n Is your number {number}? (Y/N) \n")
if check == 'Y':
return(f"Excellent! I guessed your number in {counter} tries.")
else:
if result == '<':
rounds[counter] = (result, guess_num, possible_nums[0], guess_num)
else:
rounds[counter] = (result, guess_num, guess_num, possible_nums[-1])
print("Something's fishy here.")
for key,value in rounds.items():
print( f"On round {key}, you said your number was {value[0]} {value[1]}.")
print(f"This means your number is between {value[2]} and {value[3]}.\n")
Step 4 – Consider Edge Cases
Any time a user is asked to provide input, it is possible that they enter something other than what they are asked. I added in checks for this.
The final game looks like this:
def guessing_game(n_min=1, n_max=100):
possible_nums = list(range(1,101))
counter = 0
rounds = {}
print(f"Let's play a guessing game! Think of a number between {n_min} and {n_max}, inclusive.\n")
def answer_check(number):
check = input(f"\nIs your number {number}? (Y/N)\n")
if check not in 'Y,N':
print("I'm sorry. I do not understand your answer.")
check = input(f"\nIs your number {number}? (Y/N)\n")
if check == 'Y':
return(f"Excellent! I guessed your number in {counter} tries.")
else:
if result == '<':
rounds[counter] = (result, guess_num, possible_nums[0], guess_num)
else:
rounds[counter] = (result, guess_num, guess_num, possible_nums[-1])
print("Something's fishy here.")
for key,value in rounds.items():
print( f"On round {key}, you said your number was {value[0]} {value[1]}.")
print(f"This means your number is between {value[2]} and {value[3]}.\n")
while len(possible_nums) > 1:
index = len(possible_nums)//2
guess_num = possible_nums[index]
counter += 1
result = input(f'Is your number <, > or = {guess_num}?')
if result not in "<,>,=":
print("I'm sorry. I do not understand your answer.")
result = input(f'Is your number <, > or = {guess_num}?')
if result == '=':
return answer_check(guess_num)
elif result == '<' and len(possible_nums)> 3:
possible_nums = possible_nums[:index]
elif result == '<' and len(possible_nums)<= 3:
return answer_check(possible_nums[0])
elif result == '>' and len(possible_nums)> 3:
possible_nums = possible_nums[index+1:]
else:
return answer_check(possible_nums[-1])
rounds[counter] = (result, guess_num, possible_nums[0], possible_nums[-1])
And here is an example of the output:
Let's play a guessing game! Think of a number between 1 and 100, inclusive. Is your number <, > or = 51?> Is your number <, > or = 76?< Is your number <, > or = 64?> Is your number <, > or = 70?> Is your number <, > or = 73?< Is your number <, > or = 72?< Is your number 71? (Y/N) Y
Out[124]:
'Excellent! I guessed your number in 6 tries.'
And an example of cheating:
Let's play a guessing game! Think of a number between 1 and 100, inclusive. Is your number <, > or = 51?> Is your number <, > or = 76?< Is your number <, > or = 64?< Is your number <, > or = 58?< Is your number <, > or = 55?> Is your number <, > or = 57?< Is your number 56? (Y/N) N Something's fishy here. On round 1, you said your number was > 51. This means your number is between 52 and 100. On round 2, you said your number was < 76. This means your number is between 52 and 75. On round 3, you said your number was < 64. This means your number is between 52 and 63. On round 4, you said your number was < 58. This means your number is between 52 and 57. On round 5, you said your number was > 55. This means your number is between 56 and 57. On round 6, you said your number was < 57. This means your number is between 56 and 57.
Step 5 – Refine the Code
After feedback from my instructor that I didn’t actually need to build the full lists of possible numbers (duh!), I refined my code to only consider the minimum value, the middle value (number to be guessed) and the maximum value.
def guessing_game(n_min=1, n_max=100):
n_min = n_min
n_max = n_max
#We start with the given min and max values.
counter = 0
rounds = {}
guess_num = (n_min + n_max)//2
#Again, floor division is used to round down to the nearest integer.
print(f"Let's play a guessing game! Think of a number between {n_min} and {n_max}, inclusive.\n")
def answer_check(number):
check = input(f"\nIs your number {number}? (Y/N)\n")
#verify valid input
if check not in 'Y,N':
print("I'm sorry. I do not understand your answer.")
check = input(f"\nIs your number {number}? (Y/N)\n")
elif check == 'Y':
return(f"Excellent! I guessed your number in {counter} tries.")
else:
if result == '<':
rounds[counter] = (result, guess_num, n_min, guess_num)
else:
rounds[counter] = (result, guess_num, guess_num, n_max)
print("Something's fishy here.")
for key,value in rounds.items():
print( f"On round {key}, you said your number was {value[0]} {value[1]}.")
print(f"This means your number is between {value[2]} and {value[3]}.\n")
# If the guessed number is equal to either the min or max, the loop can stop.
while n_min != guess_num or n_max != guess_num:
#recalculate the middle number
guess_num = (n_min + n_max)//2
counter += 1
result = input(f'Is your number <, > or = {guess_num}?')
#verify valid input
if result not in "<,>,=":
print("I'm sorry. I do not understand your answer.")
result = input(f'Is your number <, > or = {guess_num}?')
if result == '=':
return answer_check(guess_num)
#if the difference between the min and max is 2, the answer can be verified.
elif result == '<' and n_max - n_min == 2:
return answer_check(n_min)
#change the max number to one less than the guess
elif result == '<':
n_max = guess_num - 1
elif result == '>' and n_max - n_min == 2:
return answer_check(n_max)
else:
n_min = guess_num + 1
rounds[counter] = (result, guess_num, n_min, n_max)
return answer_check(guess_num)
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/02/math-1547018_1920.jpg?w=1024)
Lesson of the Day
I learned that in Seaborn, you can add _r to a color palette to reverse the order the colors are applied.
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/02/garbage-3259455_1920.jpg?w=1024)
Frustration of the Day
My course has some html and CSS content to help us with webscraping. I really do not enjoy coding in these languages. I will NOT be putting any web designers out of a job! 🙂
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/02/runner-555074_1920.jpg?w=1024)
Win of the Day
I finally got over my fear and wrote my first technical post! It definitely felt a little strange to write and I know that I will improve with repetition. Practice is funny that way:)
![](https://fromteachingtotech928923879.wordpress.com/wp-content/uploads/2021/02/review-5205754_1920.jpg?w=1024)
Current Standing on the Imposter Syndrome Scale
3/5