Making a puzzle solver for the "Four fours" puzzle


import itertools
import math
import time
from time import time
from math import factorial
from math import sqrt

def pretty(string):
    return string

def test(n):
    for i in range(0,n+1):
            print("Failed: "+str(i))
    return("nFailed "+str(fails)+" out of "+str(n)+"nnTotal time: "+str(time()-start)       [:4]+"nAverage time: "+str((time()-start)/n)[:4])

def fours(goal):
    operators = ['-', '/', '+', '*', '-sqrt', '-^', '-factorial', '-.', '/sqrt', '/^',   '/factorial',  '/.', '+sqrt', '+^', '+factorial', '+.', '*sqrt', '*^', '*factorial', '*.']
    brackets = ["{0}{1}{0}{2}{0}{3}{0}",
    for combination in itertools.product(operators, repeat=3):
        for bracket in brackets:
                formula = bracket.format("(4)", *combination).replace(".(4","(.4")
            except ValueError:
                if eval(formula)==goal:
                    return(pretty((formula + " = " + str(int(eval(formula))))))


Here is the code for a solver for the “Four Fours” puzzle.

It mostly works, but it is far to slow with bigger inputs.

What I want to do, is make a smaller list of operators, like this.


But to do this, I would need to make it so the program would have a way to put two operators in a row
so that it could come up with results like this.

4/4+factorial(4)/sqrt(4) = 13

Note that they factorial comes right after they + sign.

I asked how to do this earlier, and someone suggested that I make mutually exclusive lists, so that operators that aren’t in the same lists can be put one after the other.

The problem is that I can’t seem to find a functioning way to do that without drastically rewriting my code.

If any one has a good way to do this(or a better way of accomplishing the same thing) please let me know.

Addidtionally, I want the program to be able to put two fours next to each other to make numbers like 44, but doing that gives me much the same problem.

Examples:When I do 0 through 20 I get

(4-(4-4))-4 = 0
(4-(4-4))/4 = 1
(4-(4-4))-sqrt(4) = 2
4-((4-sqrt(4))/sqrt(4)) = 3
4-((4-4)/4) = 4
4-((4/4)-sqrt(4)) = 5
4-((4-4)-sqrt(4)) = 6
4-((4/4)-4) = 7
4-((4-4)-4) = 8
4-((4-factorial(4))/4) = 9
(4-(4-4))/(.4) = 10

But when I do it with the system I would like to use I get

(4-(4-4))-4 = 0
(4-(4-4))/4 = 1
4-((4+4)/4) = 2
(4+4+4)/4 = 3
4-((4-4)/4) = 4
(4+4*4)/4 = 5
(4+4)/4+4 = 6
4-((4/4)-4) = 7
4-((4-4)-4) = 8
4/4+4+4 = 9
Failed: 10

Number 10 fails because it can’t put the decimal after they division symbol, and number 9 is different because factorial can’t come after –

Asked By: David Greydanus



Your program takes too long for larger goals because it does things like tries to take the factorial of 3628804. Python will chew on that for quite a while. That happens during the evaluation of:

  • (4)-factorial((4)+factorial((4)/(.4)))
  • = 4 – factorial( 4+factorial( 10 ) )
  • = 4 – factorial( 3628804 )

If it ever makes it through that, it has harder things to do after that. It’s not going to finish in your lifetime.

You need to find a way to avoid such calculations. Avoiding composition of functions (like factorial(factorial(x)) might be adequate. An easy way to avoid the problems is to write an intermediate function, say “lfact”, to limit how big a factorial is calculated. lfact would test the argument, and if too big raise an exception or return None, otherwise call math.factorial. You would invoke your substitute lfact function in your eval’d formula.

Answered By: mgkrebbs

Here is the easiest source code of the four-fours puzzle by using operators and parenthesis. To use factorial import math library.

import math
print ("Zero is", 4+4-4-4)
print ("One is", 4//4+4-4)
print ("One is", 4*4//4//4)
print ("Two is",4-((4+4)//4))
print ("Three is", 44//4-4-4)
print ("Four is", (4//4**4)+4)
print ("Five is",(4+4*4)//4)
print ("Six is", math.factorial(4)//4+4-4)
print ("Seven is", 4+4-4//4)
print ("Eight is", 4*4-4-4)
print ("Nine is", 4+4+4//4)
Answered By: Gulshari Jamil
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.