Finding The Solution To A Math Logic Puzzle with Python
Section 6.9 Debugging
This week's discussion is focused on Section 6.9 of Downey, A. (2015). Think Python: How to think like a computer scientist. Green Tea Press. https://greenteapress.com/thinkpython2/thinkpython2.pdf
While browsing social media, I encountered a math logic puzzle that presented the perfect opportunity to solve a problem with Python.
Incidentally, the puzzle also presented a great opportunity to discuss this week's lesson on debugging and submit the discussion for my "University of The People" Discussion Assignment.
There are 3 possibilities to consider when debugging code:
A precondition is violated:
• There is something wrong with the arguments the function is getting.
For example, if a function is designed to take integers as an argument. but a string is entered, the function can no longer serve its purpose as it was designed. It will present
A postcondition is violated:
• There is something wrong with the function itself.
For example, a runtime error occurs when a recursive function is run without the conditions being met to break the recursion.
There’s a return value error:
• There is something wrong with the return value or the way it is being used.
For example, if a function is designed to return a string and a mathematical operator (other than ‘+’) is applied to that return value, a syntax error will occur.
Here’s a more specific example:print(‘PeanutButter’ + ‘JellyTime’)
returns “PeanutButterJellyTime”
.
But,
print(‘PeanutButter’ - ‘JellyTime’)
results in a Syntax Error.
The print() function returns a Syntax error because two strings cannot be subtracted as integers. The "-" operator only applies to integers.
Here is an example of precondition and postcondition violations I encountered when creating a solution to the math logic puzzle.
This program should accept a year as a 4-digit integer and fulfill the following conditions:
myAge = (sum of the digits of birth_year)
The sum of digits is less than 100.
The sum of digits + the birth_year = 2021.
The birth_year must be greater than 1921.
The program was created through the process of incremental development - which is essentially, breaking a program into smaller testable pieces before putting it all together.
A Precondition Violation:
The countYears() function on line 32 first validates the argument passed to the year parameter by checking to see if the input year is between 1921 and 2021. Once this condition is met, the argument is passed to the yearDigitSum() function on line 34.
The following screenshot demonstrates what happens when there is a precondition violation.
Here, an else statement is added on line 37 to indicate to a user what went wrong.
Because the year 1900 was entered, the “else” condition was executed.
A Postcondition Violation:
In the example below, a postcondition is violated.
The first two outputs are correct; Both years 1996 and 2014 are solutions.
However, the years 2018 and 2019 do not meet the conditions to be valid solutions - yet, the yearDigitSum() function is returning them with corresponding possible ages (3 and 2 respectively).
Because 2018 + 3 = 2021 and 2019 + 2 = 2021, this indicates something must be going wrong near line 27 or 28 of the yearDigitSum() function.
After adding some scaffolding to print the counted_year and year_total variables, it still wasn’t clear why the years 2018 and 2019 were making it out of the conditional checks.
I tried adding another iteration method; Instead of using the range() function, I did the following:
year_list = [] ele = 0 while (ele < len(year_list)) ele += 1
This resulted in more years being printed that still didn’t meet all of the required conditions. However, all was not lost.
After removing the while loop, I ran the code and encountered an indentation error.
The console complained that the yearDigitSum() function with the newly implemented scaffolding had an indentation error.
If improper indentation could break the code, is it possible that improper indentation could yield undesired results?
As a test, I moved the year_total < 100 conditional statement on Line 27 a few blocks to the left to line up with the “for ele” iteration on Line 24 …
And there it was, the expected solutions!
The years 1996 and 2014 both meet the conditions.
A Return Value Error:
Our last example displays an instance of a return value error and a precondition violation.
The countYears() function requires an integer, yet on line 45, the return value for its recursion is passed a string (in the message variable) as an argument.
The resulting TypeError shows that the comparison operator “<=“ is not supported between an integer and a NoneType (referring to the variable).
Programming Fundamentals Question:
Section 6.8 of Think Python 2e introduces the built-in function, isinstance. This function is used to confirm the type of an argument. How can the isinstance function be used to further validate the argument passed to the countYear() function in order to prevent encountering a return value error?
The entire code to the math logic puzzle is included below:
current_year = int(2021)
#myAge = sum of digits of birth year
#myAge < 100
#possible earliest birth year > 1921
#myAge = 2021 - birthyear
#What birth years fulfill these conditions?
#Find all years from 1922 whose sum < 100.
# Print that list
startYear = 1921
#Create A Function That Takes 4 digits
def yearDigitSum(counted_year):
year_string = str(counted_year)
#Create an empty array as "year_list"
year_list = []
year_total = 0
#Separate digits into an array
for i in year_string:
#Append each number to the array
year_list.append(int(i))
for ele in range(0, len(year_list)):
year_total += year_list[ele]
#Checks to see if numbers are less than 100
if year_total < 100:
#This line may be problematic:
#print (counted_year)
#print (f'{year_list}')
#print (f'Sum of year: {year_total}')
if (counted_year + year_total) == 2021:
print (f'Possible Age: {year_total}\nBorn in {counted_year}\n')
def countYears(year):
if 1921 <= year < 2021:
yearDigitSum(year)
year += 1
countYears(year)
elif year == 2021:
return None
else:
message = print('Please enter a year between 1921 and 2021.')
return None
countYears(startYear)