1.First of all 2. What is Soma Cube? 3. Past story 4. Ingenuity To improve processing speed 5. Difficulties To determine success 6. About algorithm and source code 7. Finally
I tried to solve the Soma Cube with python. As a result, we were able to solve 480 different cube creation patterns. Although it was a puzzle, I was able to learn various things at the stage of implementing the puzzle. I will leave the know-how at that time in the article. I hope this article helps someone.
Soma Cube is a three-dimensional puzzle that combines seven three-dimensional puzzles to create a cube. See below for details.
[Soma Cube](https://ja.wikipedia.org/wiki/%E3%82%BD%E3%83%BC%E3%83%9E%E3%82%AD%E3%83%A5%E3% 83% BC% E3% 83% 96)
You can create various 3D figures other than cubes, but this time we made a 3x3 cube as a completed form. Obviously, the appearance of the cube does not change even if it is rotated. Since it can be rotated by 6 directions, these 6 patterns are counted as 1 pattern.
Actually, this is not the first time to solve the Soma Cube programmatically. The first time was when I was a student who didn't even know the "Iroha" of programming, about 13 years ago. At that time, while having the teacher teach me how to program, the explanation is also ant I remember doing it over several months. I was able to solve it in the end, but the processing time was about 2 hours. I think it took. I wonder if I honestly understood it. The second time was about a year ago. I got tired of this on the way and quit.
As mentioned above, I remember having trouble with processing time in the past, so this time about processing speed I was more concerned than usual.
Python also has a java-like set. It's much faster than list. (There is no guarantee of the order) This time, the piece object was treated as a list halfway, and as a set from the middle.
# ==================================================
# CUBE
# ==================================================
class Cube:
def __init__(self, label, pList):
# label
self.label = label
# pieseList
self.pList = pList
# ==================================================
# COORDINATE
# ==================================================
class XYZ:
def __init__(self, x, y, z):
# x
self.x = x
# y
self.y = y
# z
self.z = z
# ==================================================
# Cube for calc
# ==================================================
class Cube4Calc:
def __init__(self, label, pList):
# label
self.label = label
# piese list sets
self.pList = set(pList)
# ==================================================
# convertToCalcCube
# ==================================================
def convToCalcCube(cube):
calcCube = cc.Cube4Calc(cube.label, [])
for p in cube.pList:
calcCube.pList.add(convPToPIdx(p))
return calcCube
# ==================================================
# convertPieseToPieseIndex
# ==================================================
def convPToPIdx(p):
return p.x + (p.z * 3) + (p.y * 9)
Trying all combinations of all seven pieces requires a tremendous amount of processing. I tried the combinations in order from the first one, and if it was NG, I wrote that the subsequent processing would not be performed.
# ==================================================
# getResolveCubeList
# ==================================================
def getResolveCubeList(cube4CalcLList, cube4CalcZList, cube4CalcTList, cube4CalcAList, cube4CalcBList, cube4CalcPList, cube4CalcVList):
resolveCubeList = []
for cube4CalcL in cube4CalcLList:
for cube4CalcZ in cube4CalcZList:
sumCube4Calc_LZ = createSumCube4Calc(cube4CalcL, cube4CalcZ, 8)
if not sumCube4Calc_LZ is None:
for cube4CalcT in cube4CalcTList:
sumCube4Calc_LZT = createSumCube4Calc(sumCube4Calc_LZ, cube4CalcT, 12)
if not sumCube4Calc_LZT is None:
for cube4CalcA in cube4CalcAList:
sumCube4Calc_LZTA = createSumCube4Calc(sumCube4Calc_LZT, cube4CalcA, 16)
if not sumCube4Calc_LZTA is None:
for cube4CalcB in cube4CalcBList:
sumCube4Calc_LZTAB = createSumCube4Calc(sumCube4Calc_LZTA, cube4CalcB, 20)
if not sumCube4Calc_LZTAB is None:
for cube4CalcP in cube4CalcPList:
sumCube4Calc_LZTABP = createSumCube4Calc(sumCube4Calc_LZTAB, cube4CalcP, 24)
if not sumCube4Calc_LZTABP is None:
for cube4CalcV in cube4CalcVList:
sumCube4Calc_LZTABPV = createSumCube4Calc(sumCube4Calc_LZTABP, cube4CalcV, 27)
if not sumCube4Calc_LZTABPV is None:
resolveCubeList.append(sumCube4Calc_LZTABPV)
return resolveCubeList
# ==================================================
# createSumCube4Calc
# ==================================================
def createSumCube4Calc(cube4Calc_1, cube4Calc_2, length):
sumPList = cube4Calc_1.pList | cube4Calc_2.pList
if len(sumPList) == length:
# print(">>> END createSumCube4Calc")
return cc.Cube4Calc(cube4Calc_1.label + cube4Calc_2.label, sumPList)
return None
For example, when you rotate a puzzle, it is difficult to tell whether it is rotating as you expected by just displaying the xyz coordinates. We prepared a method to display pieces as an image in ASCII art style, and used this when conducting method-based tests.
# ==================================================
# printGraphicCube
# ==================================================
def printGraphicCube(cube):
# create 2*2 list
graphic = [[0] * 23 for i in range(27)]
for idxZ in range(0, 3)[::-1]:
for p in cube.pList:
if p.z == idxZ:
printGraphicPiese(p.x, p.y, p.z, graphic)
for y in range(23)[::-1]:
rowStr = ""
for x in range(27):
rowStr = rowStr + str(graphic[x][y])
print(rowStr)
# ==================================================
# printGraphicPiese
# ==================================================
def printGraphicPiese(x, y, z, graphic):
baseX = x * 5 + z * 2
baseY = y * 3 + z * 2
printRow(graphic, baseX, baseY + 5, 2, "+----+")
printRow(graphic, baseX, baseY + 4, 1, "/ /|")
printRow(graphic, baseX, baseY + 3, 0, "+----+ |")
printRow(graphic, baseX, baseY + 2, 0, "| | /")
printRow(graphic, baseX, baseY + 1, 0, "| |/")
printRow(graphic, baseX, baseY + 0, 0, "+----+")
# ==================================================
# printRow
# ==================================================
def printRow(graphic, x, y, shiftX, graphicStr):
startX = x + shiftX
graphicStrList = list(graphicStr)
cntStr = 0
for posX in range(startX, startX + len(graphicStr)):
graphic[posX][y] = graphicStrList[cntStr]
cntStr = cntStr + 1
Let me briefly explain the points of the main processing.
# ==================================================
# resolve
# ==================================================
def resolve():
calc4CubeListList = []
for idxC in range(7):
cube = cubeCreater.createBasicCube(idxC)
calc4CubeList = []
# lotationLength = vectol * latation = 6 * 4 = 24
lengthL = 24
# fix pattern first cube (1)
if idxC == 0:
lengthL = 1
for idxL in range(lengthL):
cloneC = copy.deepcopy(cube)
cubeRotationer.lotation(cloneC, idxL)
notOverCubeList = cubeSetter.createNotOverCubeList(cloneC) (2)
for notOverCube in notOverCubeList:
calc4Cube = cubeExchanger.convToCalcCube(notOverCube) (3)
calc4CubeList.append(calc4Cube)
pNum = 4
# only cubeV's pieseNum is 3
if idxC == 6:
pNum = 3
calc4CubeList = cubeExchanger.removeDupCubes(calc4CubeList, pNum) (4)
calc4CubeListList.append(calc4CubeList)
resolveCubeList = cubeResolver.getResolveCubeList(calc4CubeListList[0], calc4CubeListList[1], calc4CubeListList[2], calc4CubeListList[3], calc4CubeListList[4], calc4CubeListList[5], calc4CubeListList[6])
print("finish. ptn = " + str(len(resolveCubeList)))
if idxC == 0:
lengthL = 1
notOverCubeList = cubeSetter.createNotOverCubeList(cloneC)
calc4Cube = cubeExchanger.convToCalcCube(notOverCube)
calc4CubeList = cubeExchanger.removeDupCubes(calc4CubeList, pNum)
This time I tried to solve the Soma Cube. I was able to learn various things at the stage of implementation, which was a learning experience. After all, the best harvest was a lot of fun.
I hope this article helps someone.
Recommended Posts