One of my favourite programming exercises is solving the “countdown” numbers game. Basically, you are given a set of input numbers and have to solve the target by adding, multiplying, subtracting or dividing them (using each input number only once).

As before, this isn’t an ideal solution, as I’m still getting to grips with Python. It uses recursion to find the first solution. I don’t keep track of the closest answer yet.

import unittest
class SolverUnitTests(unittest.TestCase):
testCases = (
(0, [], True),
(1, [], False),
(1, [1], True),
(2, [1], False),
(2, [1,1], True),
(2, [1,7], False),
(3, [1,1,1], True),
(1, [3,2], True), # subtract
(1, [2,3], True),
(6, [2,3], True), # multiply
(7, [2,3], False),
(4, [8,2], True), # divide
(4, [2,8], True), # divide
(4, [9,2], False),
(14, [1,7,1], True), # add and multiply
(18, [1,7,3], True), # subtract and multiply
(100, [11, 1, 11, 1], True),
(2010, [25, 4, 2, 10, 5, 2], True),
(2011, [25, 4, 2, 10, 5, 2], True),
(2012, [25, 4, 2, 10, 5, 2], True),
(2013, [25, 4, 2, 10, 5, 2], True),
(2014, [25, 4, 2, 10, 5, 2], True),
(16, [2,2,2], False)
)
def testCanSolve(self):
for (target, numbers, solveable) in self.testCases:
print 'solving', target, 'with', numbers
solver = Solver(target)
self.assertEqual(solveable, solver.Solve(numbers))
def add(first,second):
answer = first + second
return (answer, '%d+%d=%d' % (first,second,answer))
def subtract(first,second):
answer = first - second
if answer < 0:
answer = 0
return (answer, '%d-%d=%d' % (first,second,answer))
def multiply(first,second):
answer = first * second
if answer < 0:
answer = 0
return (answer, '%d*%d=%d' % (first,second,answer))
def divide(first,second):
if (second == 0) or ( first % second != 0):
answer = 0
else:
answer = first / second
return (answer, '%d/%d=%d' % (first,second,answer))
def pairs(list):
for i in range(len(list)):
for j in range(i+1,len(list)):
yield (list[i],list[j])
class Solver:
def __init__(self, target):
self.target = target
self.operations = (add, subtract, multiply, divide)
def Solve(self, numbers):
if (self.target in numbers) or (self.target == 0):
return True
return self.SolveList(numbers, '')
def SolveList(self, numbers, solutionSoFar):
numbers.sort(reverse=True)
for (first, second) in pairs(numbers):
for func in self.operations:
(newNumber,solution) = func(first,second)
if newNumber == self.target:
print self.target, ':', solutionSoFar + ', ' + solution
return True
elif newNumber:
newList = list(numbers)
newList.remove(first)
newList.remove(second)
newList.append(newNumber)
#print 'retry with', newList
if self.SolveList(newList, solutionSoFar + ', ' + solution):
return True
return False