\$\begingroup\$
\$\endgroup\$
0
Please feel free to review my countdown numbers game solver. Posted here too for people working on the same problem. For clarity the print statement "Eval..." means the program has used BODMAS on an entire mathematical statement (e.g: 1*3+10 = 31)
"Running total" means the program recalculates the total after each move (e.g: 1*3+10 = 13 because 1*3 = 3, 3+10 = 13)
import re
def unpack(method):
string = method
special = ["*","+","-","/"]
list_sum = []
list_special = []
numbers = (re.findall(r"[\w']+", string))
for char in string:
if char in special:
list_special.append(char)
for index in range (len(numbers)-1):
to_eval = numbers[index] + list_special[index] + numbers[index+1]
list_sum.append(f'{to_eval} = {eval(to_eval)}')
numbers[index+1] = str(eval(to_eval))
return list_sum
def evaluate (method,running_sum):
global clear_status
if eval(method) == target:
if method not in list_methods:
list_methods.append(method)
print (f'Eval: {method}')
clear_status = True
return True
if running_sum == target:
if method not in list_methods:
list_methods.append(method)
print (f'Running sum: {unpack(method)}')
clear_status = True
return True
clear_status = False
return False
def new_total (total,item,operation):
if operation == "+": return total + item
if operation == "-": return total - item
if operation == "*": return total * item
if operation == "/" and item != 0: return total / item
return ""
def solve (array,total=0,method="",list_method=[]):
if len(array) == 0:
return
for (index,item) in enumerate(array):
#Set methods and totals to "":
add_method, sub_method, mul_method, div_method = "", "", "", ""
add_total, sub_total, mul_total, div_total = 0, 0, 0,0
#Assign methods and totals to a list:
methods = [add_method, sub_method, mul_method, div_method]
totals = [add_total, sub_total, mul_total, div_total]
str_func = ["+", "-", "*", "/"]
#Create new array
remaining = array[:index] + array[index+1:]
#Sets new totals and new "methods"
for index_1 in range (len(methods)):
if method =="":
if str_func[index_1] != "/" and str_func[index_1] != "*" and str_func[index_1] != "-":
methods[index_1] = str(array[index])
totals[index_1] = new_total(total, item, str_func[index_1])
else:
methods[index_1] = method + str_func[index_1] + str(array[index])
totals[index_1] = new_total(total, item, str_func[index_1])
#Evaluates each total and method
for index_2 in range (len(methods)):
try:
if evaluate(methods[index_2], totals[index_2]):
if clear_status == True:
methods[index_2]=""
totals[index_2]=0
return
except Exception as e:
pass
#Recursively calculates next move
for index_3 in range (len(methods)):
try:
solve(remaining, total= totals[index_3],method= methods[index_3])
except Exception as e:
pass
str_array = input("Please enter the starting numbers, separated by commas: ")
array = array=[int(item.strip()) for item in str_array.split(",")]
target = int(input("Please enter a target value: "))
print (f'Solutions for {array} to reach {target}')
list_methods = []
solve(array)
if list_methods == []:
print ("Unsolvable countdown problem")
to_close = input("Press any key to exit...")
1 Answer 1
\$\begingroup\$
\$\endgroup\$
- Docstrings: You should include a
docstring
at the beginning of every method/class/module you write. This will help any documentation indentify what your code is supposed to do. - solve(): My biggest problem with this method was the beginning. You create eight different variables for your methods and total, then put them into a list. Instead of having these eight one time use variables, just set the default values inside the list. Since you don't work with any of the eight variables after putting them into the list, they are obsolete, so can be removed.
- Variable/Parameter/Method Spacing: There shouldn't be spaces between the method name and the opening parentheses.
- Parameter Reassignment: In
unpack
you have a passed parametermethod
. You then reassign that parameter to a variablestring
. This is very unnecessary. You have simply have the parameterstring
, so you're working directly with what is passed, instead of taking the time and trouble to reassign to a variable. - Use of
global
: It's not recommended to useglobal
variables in python, or any language. This StackOverflow answer provides more insight. - Boolean Comparison: Instead of checking
if variable == True
, you can simply check the variable itself, like so:if variable:
. - Unused Exceptions (
as e
): If you don't intend to work with an exception that you catch, it is unnecessary to have the extra codeas e
, since that will be a variable you never use. You can simply remove that code. - Unused Parameters: In
solve
, you never use thelist_method=[]
that is passed. You should remove any parameters you don't use, you risk adding complexity and reducing readability. - Constants Naming: Any constants in your code should be UPPERCASE, so make it clear to anyone reading your code, including yourself, that they are constant variables.
in list
vsin [...]
: You create a list of operators for the sole purpose of checking if an operator is in that list of operators. This is unnecessary. You can simplify your code by creating an anonymous list to check containment. This reduces the amount of variables in your code, increasing readability and reducing complexity.- if name == main guard: Any code that isn't contained in a method or class should be contained in a
if __name__ == '__main__'
guard. This will prevent any of that code from being executed if you decide to import the file, for other uses. _
for unused variables: Your last line of code is a variable that allows the user to exit when they want, by pressing any key. You create a variable for this purpose alone. Since you never use this variable, and it's only used to exit the program, you can use a_
, to make it clear that that variable is to be, essentially, ignored.
Updated Code
"""
Module Docstring:
Explanation of your code goes here
"""
import re
def unpack(string):
""" Unpacks the passed `string` """
list_sum = []
list_special = []
numbers = (re.findall(r"[\w']+", string))
for char in string:
if char in ["*", "+", "-", "/"]:
list_special.append(char)
for index in range(len(numbers) - 1):
to_eval = numbers[index] + list_special[index] + numbers[index + 1]
list_sum.append(f'{to_eval} = {eval(to_eval)}')
numbers[index + 1] = str(eval(to_eval))
return list_sum
def evaluate(method, running_sum):
""" Evaluates the passed `method` """
if eval(method) == TARGET:
if method not in LIST_METHODS:
LIST_METHODS.append(method)
print(f'Eval: {method}')
clear_status = True
return True
if running_sum == TARGET:
if method not in LIST_METHODS:
LIST_METHODS.append(method)
print(f'Running sum: {unpack(method)}')
clear_status = True
return True
clear_status = False
return False
def new_total(total, item, operation):
""" Determines the operator and returns the new total """
if operation == "+":
return total + item
if operation == "-":
return total - item
if operation == "*":
return total * item
if operation == "/" and item != 0:
return total / item
return ""
def solve(array, total=0, method=""):
""" Solves the passed numbers and target """
if not array:
return
for index, item in enumerate(array):
#Assign methods and totals to a list:
methods = ["", "", "", ""]
totals = [0, 0, 0, 0]
str_func = ["+", "-", "*", "/"]
#Create new array
remaining = array[:index] + array[index+1:]
#Sets new totals and new "methods"
for index_1 in range(len(methods)):
if method == "":
if str_func[index_1] != "/" and str_func[index_1] != "*" and str_func[index_1] != "-":
methods[index_1] = str(array[index])
totals[index_1] = new_total(total, item, str_func[index_1])
else:
methods[index_1] = method + str_func[index_1] + str(array[index])
totals[index_1] = new_total(total, item, str_func[index_1])
#Evaluates each total and method
for index_2, value_2 in enumerate(methods):
try:
if evaluate(value_2, totals[index_2]):
if clear_status:
methods[index_2] = ""
totals[index_2] = 0
return
except Exception:
pass
#Recursively calculates next move
for index_3, value_3 in enumerate(methods):
try:
solve(remaining, total=totals[index_3], method=value_3)
except Exception:
pass
if __name__ == '__main__':
clear_status = None
STR_ARRAY = input("Please enter the starting numbers, separated by commas: ")
ARRAY = ARRAY = [int(item.strip()) for item in STR_ARRAY.split(",")]
TARGET = int(input("Please enter a target value: "))
print(f'Solutions for {ARRAY} to reach {TARGET}')
LIST_METHODS = []
solve(ARRAY)
if LIST_METHODS == []:
print("Unsolvable countdown problem")
_ = input("Press any key to exit...")
answered Aug 4, 2019 at 3:46
lang-py