I tried to solve the soma cube with python

Lottery

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

Introduction

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.

Source code is here

What is Soma Cube?

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.

Past story

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.

Ingenuity To improve processing speed

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.

Use set instead of list

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)

Pruning in the middle of the for statement

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

Difficulties to determine success

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

About algorithm and source code

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)))

(1) The rotation of the "L-shaped" cube is fixed to 1 pattern because 6 directions are counted as 1 pattern.

if idxC == 0:
	lengthL = 1

(2) Translate the rotated cube from 0 to 3 with respect to the x, y, z coordinates to obtain the one that does not protrude.

notOverCubeList = cubeSetter.createNotOverCubeList(cloneC)

(3) Convert to a calculation cube to improve processing speed

calc4Cube = cubeExchanger.convToCalcCube(notOverCube)

(4) Remove overlapping cubes

calc4CubeList = cubeExchanger.removeDupCubes(calc4CubeList, pNum)

Finally

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

I tried to solve the soma cube with python
I tried to solve the problem with Python Vol.1
I tried to solve the ant book beginner's edition with python
I tried to touch the CSV file with Python
I tried to solve AOJ's number theory with Python
I tried to find the entropy of the image with python
I tried to simulate how the infection spreads with Python
I wanted to solve the Panasonic Programming Contest 2020 with Python
I tried to divide the file into folders with Python
I wanted to solve ABC160 with Python
I tried to solve TSP with QAOA
I wanted to solve ABC172 with Python
The 15th offline real-time I tried to solve the problem of how to write with python
I wanted to solve the ABC164 A ~ D problem with Python
I tried to improve the efficiency of daily work with Python
I tried "smoothing" the image with Python + OpenCV
I wanted to solve NOMURA Contest 2020 with Python
I tried "differentiating" the image with Python + OpenCV
I tried to save the data with discord
Try to solve the man-machine chart with Python
I tried to get CloudWatch data with Python
I tried to output LLVM IR with Python
I tried "binarizing" the image with Python + OpenCV
I tried to automate sushi making with python
I want to solve APG4b with Python (Chapter 2)
How to write offline real time I tried to solve the problem of F02 with Python
I tried fp-growth with python
[Python] I tried to visualize the night on the Galactic Railroad with WordCloud!
I tried to refer to the fun rock-paper-scissors poi for beginners with Python
How to write offline real time I tried to solve E11 with python
I tried to get the authentication code of Qiita API with Python.
I tried with the top 100 PyPI packages> I tried to graph the packages installed on Python
I tried to streamline the standard role of new employees with Python
I tried gRPC with Python
I tried scraping with python
I tried to get the movie information of TMDb API with Python
How to write offline real time I tried to solve E12 with python
I tried to solve the first question of the University of Tokyo 2019 math entrance exam with python sympy
I tried to learn the sin function with chainer
I tried to graph the packages installed in Python
Try to solve the programming challenge book with python3
I tried to get started with blender python script_Part 01
Try to solve the internship assignment problem with Python
I tried to draw a route map with Python
I tried to get started with blender python script_Part 02
I tried to implement an artificial perceptron with python
I want to inherit to the back with python dataclass
[Python] I tried to graph the top 10 eyeshadow rankings
I tried to automatically generate a password with Python3
I tried to analyze J League data with Python
I tried hitting the API with echonest's python client
I tried to summarize the string operations of Python
I tried to easily visualize the tweets of JAWS DAYS 2017 with Python + ELK
I tried to automatically send the literature of the new coronavirus to LINE with Python
I tried web scraping with python.
I liked the tweet with python. ..
I want to debug with Python
I tried running prolog with python 3.8.2.
I tried SMTP communication with Python
I tried to move the ball
I tried to estimate the interval.