a program for solving ten puzzles with python

Asked 2 years ago, Updated 2 years ago, 431 views

From Wikipedia

A ten puzzle (10 puzzles) is a game in which a four-digit number is regarded as four single-digit numbers, and 10 is made by using four rules of operation or the like.It is also called make ten.It was used for short-term play with ticket numbers and car license plates, and was introduced in the Nihon Keizai Shimbun as a way to kill time in case of traffic jams.
As a general rule, the use of only the four operations is permitted, and the sorting of numbers is permitted, but the combination of numbers is not permitted.In the case of general rules, 10 can be made out of 552 out of 715 combinations (8147 out of 10,000 rearranged).Find even one solution and you'll get it right, but if there's a number you don't use, you won't get it right.

The following code has the disadvantage of duplicating the same solution, but somehow the answer comes out.
However, [1,1,5,8] which is said to be a difficult problem is not solved.
[1,1,5,8] Where should I improve to solve things like that?
It's a poor program that I made in a day, but I appreciate your cooperation.
I used python 3.8.
Here's the code.

import it tools
a=["1", "2", "3", "4"]
   
b = ["+", "-", "*", "/" ]

for cintertools.permutations(a,4):
    for inintertools.permutations(b,3):  
             try:
                                           
                                                                                        
                                            
                                              
                 
                                                  s1="("+c[0]+d[0]+c[1]+")"+d[1]+"("+c[2]+d[2]+c[3]+")"       
                                                  s2=c[0]+d[0]+"("+c[1]+d[1]+"("+c[2]+d[2]+c[3]+")"+")"
                                                  s3=c[0]+d[0]+"("+"("+c[1]+d[1]+c[2]+")"+d[2]+c[3]+")"
                                                  s4="("+c[0]+d[0]+"("+c[1]+d[1]+c[2]+")"+d[2]+c[3]
                                                  s5="("+"("+c[0]+d[0]+c[1]+")"+d[1]+c[2]+d[2]+c[3]+")"
                                                  s6=c[0]+d[0]+c[1]+d[1]+c[2]+d[2]+c[3]
                                                  
                                                   
                                                 
                                                       
                                                      
                                                      

                                                  sss=eval(s1)
                                                  ssss=eval(s2)
                                                  sssss=eval(s3)
                                                  ssssssss=eval(s4)
                                                  sssssss=eval(s5)
                                                  
                                                      
                                                  if sss == 10:
                                       
                                                     print(s1+"=10")
                                                  if ssss==10:
                                                     print(s2+"=10")
                                                 
                                                  ifssssss==10:
                                                     print(s3+"=10")
                                                  ifsssssss==10:
                                                     print(s4+"=10")
                                                  ifssssssss==10:
                                                     print(s5+"=10")
                                                  
                           
             exceptionValueError:
                 pass
             except ZeroDivisionError:
                 pass

python python3 algorithm

2022-09-30 22:04

2 Answers

Program to solve ten puzzles
This script applies operations in the order in which they are applied regardless of operator precedence.
The pattern is

  • Apply from left to right
  • After applying left and right, apply the operation to both results
  • Applies from one side to the other, but the values change according to the order of the operatives such as subtraction and division (the values are the same in addition and multiplication)

(From left to right: it's reversed in the image)
left to right
(from the far left and far right)
left and right

import it tools
import numpy as np

explicit=[np.add, np.subtract, np.multiplely, np.true_divide ]
def calc10(nums):
    arr=np.array(list(set(itertools.permutations(nums))), dtype=np.int16)#9*9*9*9 but does not exceed
    for ops intertools.product(oplist, repeat=3):
        with np.errstate(divide='ignore', invalid='ignore'):
            res=res2=arr[:,0]#res2=res.copy()
            forop,cin zip(ops,range(1,4)):
                res=op(res,arr[:,c])
                res2 = op(arr[:,c],res2)
        if np.any(res==10) or np.any(res2==10):
            return ar, ops, res, res2

        with np.errstate(divide='ignore', invalid='ignore'):
            res=ops[2](ops[0](arr[:,0],arr[:,1]),ops[1](arr[:,2],arr[:,3]))
        if np.any(res==10):
            return True

    return False

🟡 Unsolvable number

noten_list=[]
for num in range (10000):
    dec=list(format(num, '04d')))
    if dec!= sorted(dec): continue
    if not calc10(map(int,dec)):
        noten_list.append(num)
print(len(noten_list))#Unsolvable number is 163 

For 1158, there are 12 combinations of numbers. According to the calculation procedure shown in the result, it's 10.The numerical order is [5,1,1,8]
The divisor of the "pattern" shown above, the order of the divisors to be divisors, that makes the difference between 10 and 10

If everything else works well, maybe I should add this pattern?

 num=1158
dec=list(format(num, '04d')))
calc10(map(int,dec))

#(array([1,1,8,5],
#        [1, 8, 1, 5],
#        [1, 1, 5, 8],
#        [1, 5, 1, 8],
#        [5, 8, 1, 1],
#        [8, 1, 5, 1],
#        [8, 5, 1, 1],
#        [5, 1, 1, 8],
#        [1, 5, 8, 1],
#        [1, 8, 5, 1],
#        [5, 1, 8, 1],
#        [8,1,1,5]], dtype=int16),
# (<ufunc 'true_divide'>, <ufunc 'subtract'>, <ufunc 'true_divide'>),
# array([-1.4, -0.175, -0.5, -0.1, -0.375, 3., 0.6, 0.5,
#        -7.8  , -4.875, -3.   ,  1.4  ]),
# array([0.71428571, -0.71428571, 2., -2, -1.666667,
#         0.20512821,  2.66666667, 10.        ,  0.33333333, -0.33333333,
#         0.12820513,  5.71428571]))


2022-09-30 22:04

The questionnaire program allows you to search for the order in which you want to use the given number, but not the order in which you want to allow the operators to overlap and in which order you want to apply them to the numbers.

Also, since it is a floating point number when dividing, you need to consider the error.

Let's take a look one by one.

Allow duplicates and permutate operators

For example, if a=[1,2,3,4], the answer 1+(2+(3+4)=10 could not be searched because + can only be used once at most.

In this case, each operator is used only three times at most, so you can create a non-overlapping permutation of length 3 from a list of +-*/ three times each.

b=["+", "-", "*", "/"]*3

However, if you do this, many of the same solutions will come out.To avoid this, seriously create a duplicate permutation.

Complete the order in which the numbers are calculated

In this case, there is a omission in the part where case division is written by hand.There is no first form shown in the list below. Defined as s5 is a misimplementation.

Suppose
#★ is some kind of operator...
((a★b)★c)★d
(a★b)★(c★d)
(a★(b★c))★d
a★(b★c)★d)
a★(b★(c★d))

In addition, there is a problem with writing try...except in this program.This writing ignores everything from s1 to s5 if an error occurs anywhere from s1 to s5.You should check for errors every time.For example, you can define a method that does try:eval(s)exception:None and call it every time.

More importantly, if there are only 4 like this problem, I can cover it by hand, but if there are more, I can feel more comfortable thinking about how to cover it mechanically.It would be nice if we could cover the possible abstract syntax tree shapes, so it would be good to consider covering the bifurcated tree shapes with N leaves.If you push it this way, you can implement it without using eval (which tends to produce unintended behavior and is often avoided).

Worry about floating point error

This calculation won't affect you that much, but it's better to consider error when performing operations that include floating point numbers.

In other words, instead of checking whether 10 is equal, you should check to allow a little error.Alternatively, you may want to use fractions to treat it as a fraction rather than a floating point number.


2022-09-30 22:04

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.