使用python对普通三阶魔方进行建模
今天为了教妹妹玩儿魔方,又拿起了初中学会的几个公式。其实我拼魔方很菜的,只是知道几个公式(准确的说是三个…)突发奇想,能不能用python建立一个存在于电脑中的数字化普通三阶魔方。这篇文章记录了我从初步构思到最终实现的过程(如果真能实现…)
1 整体构想
1.1 建立虚拟魔方
1.1.1 为魔方的26个方块进行定义
想要在电脑中套用公式来还原魔方,或是利用电脑来研究与开发新的魔方公式,首先要做的就是在电脑中构建一个虚拟魔方。构建虚拟魔方的过程,就是对模仿的26个块进行定义的过程。关于如何给魔方的方块进行定义,我选择将魔方的方块分为3类:
- 中心块={颜色,位置}
- 棱块={颜色1,颜色2,位置1,位置2}6
- 角块={颜色1,颜色2,颜色3,位置1,位置2,位置3}
这种定义方式是根据方块的颜色面数和位置来定义的,很容易理解。而且可以知道,每一类方块的颜色属性是不变的。在电脑中,由于在还原过程中不需要对魔方进行转动,可以完成实际中人手所不方便的操作。于是,我们可以认为魔方中的第1类方块(中心块)的位置是不变的。因此可以另不同面上的中心块的颜色来作为某一面的标准颜色,借此得到有关魔方方块属性的完整解释。
颜色:魔方方块所存在的标准颜色(棱块有两种颜色,角块有三种颜色)
位置:魔方方块所存在的标准颜色对应的面(棱块的位置由两个面确定,角块的位置由三个面确定)
1.1.2 利用图形化方式表示魔方
这个还不会...(如果有幸有大佬看到我的文章,还望不吝赐教)
1.2 对魔方进行操作
对普通三阶魔方的操作,简单来说,只有一种——将魔方的某一面围绕中心块旋转。
- 将操作进一步细化可以将旋转操作方法分为两种:顺时针旋转,逆时针旋转。
这里的顺、逆时针是指从魔方外部,直视要被操作的魔方面。此时魔方面围绕对应中心块顺时针旋转即为顺时针旋转魔方,围绕对应中心块逆时针旋转即为逆时针旋转魔方。
- 将旋转操作再进一步细分。显然,模仿的每一面都是可以旋转的。于是对于魔方的操作方法从两种变为了12种。
结合上文中对于魔方的定义,对于虚拟魔方进行旋转操作后,改变了方块的位置属性。
1.3 实现对魔方公式的优化
暂时没思路
2 利用python进行实现
上述的整体思路已经在逻辑上将构建虚拟魔方并完成对应操作的进行了阐述,接下来要做的就是用代码来实现功能,真正在计算机中构建一个可以被操作的魔方。
2.1 利用代码构建虚拟魔方
2.1.1 利用代码定义虚拟魔方的26个方块
为了实现上述逻辑定义中的功能,我暂时选择以下的方法为第一种定义方式
一、利用列表定义26个方块,给出定义如下:
# 定义魔方的26个魔方块的初始状态
centreblock_1 = ["centre", "write", 1]
centreblock_2 = ["centre", "red", 2]
centreblock_3 = ["centre", "blue", 3]
centreblock_4 = ["centre", "orange", 4]
centreblock_5 = ["centre", "green", 5]
centreblock_6 = ["centre", "yellow", 6]
arrisblock_1 = ["arris", "write", "red", 1, 2]
arrisblock_2 = ["arris", "write", "blue", 1, 3]
arrisblock_3 = ["arris", "write", "orange", 1, 4]
arrisblock_4 = ["arris", "write", "green", 1, 5]
arrisblock_5 = ["arris", "red", "yellow", 2, 6]
arrisblock_6 = ["arris", "blue", "yellow", 3, 6]
arrisblock_7 = ["arris", "orange", "yellow", 4, 6]
arrisblock_8 = ["arris", "green", "yellow", 5, 6]
arrisblock_9 = ["arris", "red", "blue", 2, 3]
arrisblock_10 = ["arris", "blue", "orange", 3, 4]
arrisblock_11 = ["arris", "orange", "green", 4, 5]
arrisblock_12 = ["arris", "green", "red", 5, 2]
cornerblock_1 = ["corner", "write", "red", "blue", 1, 2, 3]
cornerblock_2 = ["corner", "write", "blue", "orange", 1, 3, 4]
cornerblock_3 = ["corner", "write", "orange", "green", 1, 4, 5]
cornerblock_4 = ["corner", "write", "green", "red", 1, 5, 2]
cornerblock_5 = ["corner", "yellow", "red", "blue", 6, 2, 3]
cornerblock_6 = ["corner", "yellow", "blue", "orange", 6, 3, 4]
cornerblock_7 = ["corner", "yellow", "orange", "green", 6, 4, 5]
cornerblock_8 = ["corner", "yellow", "green", "red", 6, 5, 2]
# 为了方便之后使用这些方块,现定义一个列表用于储存这些方块
blocklist = [centreblock_1, centreblock_2, centreblock_3, centreblock_4, centreblock_5, centreblock_6,
arrisblock_1, arrisblock_2, arrisblock_3, arrisblock_4, arrisblock_5, arrisblock_6, arrisblock_7, arrisblock_8, arrisblock_9, arrisblock_10, arrisblock_11, arrisblock_12,
cornerblock_1, cornerblock_2, cornerblock_3, cornerblock_4, cornerblock_5, cornerblock_6, cornerblock_7, cornerblock_8]
以上便完成了定义,这里能够看到,这个定义方式还是比较繁琐,做了很多重复的步骤。对定义方式的优化以后再来修改。同时需要注意的是:魔方方块中的颜色位于对应数字所在的面上。
如:
cornerblock_8 = ["corner", "yellow", "green", "red", 6, 5, 2]
中,黄色位于面6,橙色位于面5,红色位于面2,了解这一特点可以帮助了解虚拟魔方的状态。
2.1.2 利用图形化方式表示魔方
暂时没思路
2.2 对魔方进行操作
对虚拟魔方进行操作时,需要明确两个变量:
- 被旋转的面
- 旋转的方向
在确定被旋转的面时,实质上是确定被旋转的面内有哪些方块,并根据旋转的方向确定不同的算法来实现对魔方的旋转。
# 定义魔方面列表,用于储存魔方不同状态时每个面所包含的方块
# 首先对六个面列表进行初始化避免可能的空值报错
face1=[0,0,0,0,0,0,0,0]
face2=[0,0,0,0,0,0,0,0]
face3=[0,0,0,0,0,0,0,0]
face4=[0,0,0,0,0,0,0,0]
face5=[0,0,0,0,0,0,0,0]
face6=[0,0,0,0,0,0,0,0]
# 定义一个用于修正面列表数据的函数
def update_face(n):
face = []
i = 6
while(i != 26):
if(blocklist[i][0] == "arris"):
j = 3
while(j != 5):
if(blocklist[i][j] == n):
face.append(blocklist[i])
j = j+1
elif(blocklist[i][0] == "corner"):
j = 4
while(j != 7):
if(blocklist[i][j] == n):
face.append(blocklist[i])
j = j+1
i = i+1
return face
# 每次进行完操作之后,要应用函数 update_face 来更新魔方的状态
# 下面用于定义一个函数来表示魔方每个面上的颜色状态
# 返回魔方某个面的颜色
# 形参说明:n为要返回颜色的面
def face_color(n):
facecolor = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
face_n = update_face(n)
d = 0
i = 0
if(n == 1):
facecolor[1][1] = "write"
elif(n == 2):
facecolor[1][1] = "red"
elif(n == 3):
facecolor[1][1] = "blue"
elif(n == 4):
facecolor[1][1] = "orange"
elif(n == 5):
facecolor[1][1] = "green"
elif(n == 6):
facecolor[1][1] = "yellow"
while(i != 8):
if(face_n[i][0] == "arris"):
d = 2
elif(face_n[i][0] == "corner"):
d = 3
j = 0
while(j != (d*2+1)):
if(face_n[i][j] == n):
x, y = block_position(n, face_n[i])
facecolor[x][y] = face_n[i][j-d]
j = j+1
i = i+1
return(facecolor)
# 定义函数,用于确定某个方块在平面的相对位置
# 形参说明:n为方块所在的面,block为方块
def block_position(n, block):
x = 0
y = 0
if(block[0] == "arris"):
tempblock = [block[3], block[4]]
i = 0
position = []
while(i != 2):
if(tempblock[i] != n):
position.append(tempblock[i])
i = i+1
if(n == 1):
if(position[0] == 2):
x = 0
y = 1
elif(position[0] == 3):
x = 1
y = 0
elif(position[0] == 4):
x = 2
y = 1
elif(position[0] == 5):
x = 1
y = 2
if(n == 2):
if(position[0] == 6):
x = 0
y = 1
elif(position[0] == 3):
x = 1
y = 0
elif(position[0] == 1):
x = 2
y = 1
elif(position[0] == 5):
x = 1
y = 2
if(n == 3):
if(position[0] == 6):
x = 0
y = 1
elif(position[0] == 4):
x = 1
y = 0
elif(position[0] == 1):
x = 2
y = 1
elif(position[0] == 2):
x = 1
y = 2
if(n == 4):
if(position[0] == 6):
x = 0
y = 1
elif(position[0] == 5):
x = 1
y = 0
elif(position[0] == 1):
x = 2
y = 1
elif(position[0] == 3):
x = 1
y = 2
if(n == 5):
if(position[0] == 6):
x = 0
y = 1
elif(position[0] == 2):
x = 1
y = 0
elif(position[0] == 1):
x = 2
y = 1
elif(position[0] == 4):
x = 1
y = 2
if(n == 6):
if(position[0] == 4):
x = 0
y = 1
elif(position[0] == 3):
x = 1
y = 0
elif(position[0] == 2):
x = 2
y = 1
elif(position[0] == 5):
x = 1
y = 2
if(block[0] == "corner"):
tempblock = [block[4], block[5], block[6]]
i = 0
temp_position = []
while(i != 3):
if(tempblock[i] != n):
temp_position.append(tempblock[i])
i = i+1
position = [min(temp_position), max(temp_position)]
if(n == 1):
if((position[0] == 2) & (position[1] == 3)):
x = 0
y = 0
if((position[0] == 3) & (position[1] == 4)):
x = 2
y = 0
if((position[0] == 4) & (position[1] == 5)):
x = 2
y = 2
if((position[0] == 2) & (position[1] == 5)):
x = 0
y = 2
if(n == 2):
if((position[0] == 3) & (position[1] == 6)):
x = 0
y = 0
if((position[0] == 1) & (position[1] == 3)):
x = 2
y = 0
if((position[0] == 1) & (position[1] == 5)):
x = 2
y = 2
if((position[0] == 5) & (position[1] == 6)):
x = 0
y = 2
if(n == 3):
if((position[0] == 4) & (position[1] == 6)):
x = 0
y = 0
if((position[0] == 1) & (position[1] == 4)):
x = 2
y = 0
if((position[0] == 1) & (position[1] == 2)):
x = 2
y = 2
if((position[0] == 2) & (position[1] == 6)):
x = 0
y = 2
if(n == 4):
if((position[0] == 5) & (position[1] == 6)):
x = 0
y = 0
if((position[0] == 1) & (position[1] == 5)):
x = 2
y = 0
if((position[0] == 1) & (position[1] == 3)):
x = 2
y = 2
if((position[0] == 3) & (position[1] == 6)):
x = 0
y = 2
if(n == 5):
if((position[0] == 2) & (position[1] == 6)):
x = 0
y = 0
if((position[0] == 1) & (position[1] == 2)):
x = 2
y = 0
if((position[0] == 1) & (position[1] == 4)):
x = 2
y = 2
if((position[0] == 4) & (position[1] == 6)):
x = 0
y = 2
if(n == 6):
if((position[0] == 3) & (position[1] == 4)):
x = 0
y = 0
if((position[0] == 2) & (position[1] == 3)):
x = 2
y = 0
if((position[0] == 2) & (position[1] == 5)):
x = 2
y = 2
if((position[0] == 4) & (position[1] == 5)):
x = 0
y = 2
return x, y
上述的函数是一项准备工作,接下来就要实现对于魔方的旋转操作。
- 定义12种旋转方法,因为旋转过程中,棱块和角块除了属性的个数不同,旋转过程中属性值得变化关系是相同的,因此将两者不做区别。
# 定义旋转操作的子函数,方便后面重复调用时出现错误
def rotate_subroutine(tempface, i, j, n, direction):
if(direction == "C"):
if(n == 1):
if(tempface[i][j] == 2):
tempface[i][j] = 5
elif(tempface[i][j] == 5):
tempface[i][j] = 4
elif(tempface[i][j] == 4):
tempface[i][j] = 3
elif(tempface[i][j] == 3):
tempface[i][j] = 2
if(n == 2):
if(tempface[i][j] == 6):
tempface[i][j] = 5
elif(tempface[i][j] == 5):
tempface[i][j] = 1
elif(tempface[i][j] == 1):
tempface[i][j] = 3
elif(tempface[i][j] == 3):
tempface[i][j] = 6
if(n == 3):
if(tempface[i][j] == 2):
tempface[i][j] = 1
elif(tempface[i][j] == 1):
tempface[i][j] = 4
elif(tempface[i][j] == 4):
tempface[i][j] = 6
elif(tempface[i][j] == 6):
tempface[i][j] = 2
if(n == 4):
if(tempface[i][j] == 1):
tempface[i][j] = 5
elif(tempface[i][j] == 5):
tempface[i][j] = 6
elif(tempface[i][j] == 6):
tempface[i][j] = 3
elif(tempface[i][j] == 3):
tempface[i][j] = 1
if(n == 5):
if(tempface[i][j] == 4):
tempface[i][j] = 1
elif(tempface[i][j] == 1):
tempface[i][j] = 2
elif(tempface[i][j] == 2):
tempface[i][j] = 6
elif(tempface[i][j] == 6):
tempface[i][j] = 4
if(n == 6):
if(tempface[i][j] == 4):
tempface[i][j] = 5
elif(tempface[i][j] == 5):
tempface[i][j] = 2
elif(tempface[i][j] == 2):
tempface[i][j] = 3
elif(tempface[i][j] == 3):
tempface[i][j] = 4
elif(direction == "AC"):
if(n == 1):
if(tempface[i][j] == 5):
tempface[i][j] = 2
elif(tempface[i][j] == 4):
tempface[i][j] = 5
elif(tempface[i][j] == 3):
tempface[i][j] = 4
elif(tempface[i][j] == 2):
tempface[i][j] = 3
if(n == 2):
if(tempface[i][j] == 5):
tempface[i][j] = 6
elif(tempface[i][j] == 1):
tempface[i][j] = 5
elif(tempface[i][j] == 3):
tempface[i][j] = 1
elif(tempface[i][j] == 6):
tempface[i][j] = 3
if(n == 3):
if(tempface[i][j] == 1):
tempface[i][j] = 2
elif(tempface[i][j] == 4):
tempface[i][j] = 1
elif(tempface[i][j] == 6):
tempface[i][j] = 4
elif(tempface[i][j] == 2):
tempface[i][j] = 6
if(n == 4):
if(tempface[i][j] == 5):
tempface[i][j] = 1
elif(tempface[i][j] == 6):
tempface[i][j] = 5
elif(tempface[i][j] == 3):
tempface[i][j] = 6
elif(tempface[i][j] == 1):
tempface[i][j] = 3
if(n == 5):
if(tempface[i][j] == 1):
tempface[i][j] = 4
elif(tempface[i][j] == 2):
tempface[i][j] = 1
elif(tempface[i][j] == 6):
tempface[i][j] = 2
elif(tempface[i][j] == 4):
tempface[i][j] = 6
if(n == 6):
if(tempface[i][j] == 5):
tempface[i][j] = 4
elif(tempface[i][j] == 2):
tempface[i][j] = 5
elif(tempface[i][j] == 3):
tempface[i][j] = 2
elif(tempface[i][j] == 4):
tempface[i][j] = 3
- 结合要旋转的面进行操作
# 定义旋转操作函数
# 形参说明:n为要进行旋转的面,direction为旋转的方向(C表示顺时针,AC表示逆时针)
def rotate(n, direction):
tempface = update_face(n)
i = 0
while(i != 8):
if(tempface[i][0] == "arris"):
j = 3
while(j != 5):
rotate_subroutine(tempface, i, j, n, direction)
j = j+1
elif(tempface[i][0] == "corner"):
j = 4
while(j != 7):
rotate_subroutine(tempface, i, j, n, direction)
j = j+1
i=i+1
通过上述的两种方式,已经可以实现拼魔方的操作了。现在要做的就是考虑如何通过26个方块的属性参数来判断魔方当前的形态。并通过将这中当前的形态同预先设计好的图案比较,并执行相应的拼魔方方法。
2.3 识别魔方的状态
识别魔方的状态,在于魔方每个方块的自由度不仅仅是平移的情况。在魔方还没有复原时,魔方方块的也是可以围绕本身的位置进行旋转的。因此要确定魔方的状态,需要知道魔方目标方块的位置和方向。如前述对于方块的定义中,可以根据属性参数来确定位置,同时方块的方向也可以一同确定。
现在的问题在于如何分析魔方中每个属性参数,并将这些参数综合起来一起考虑。