我的 CS50 最终项目是“人法”魔方解算器。
这意味着输入的魔方是用人类可以使用的方法(CFOP)求解的,步骤明确:Cross、F2L、OLL和PLL。
最终产品由一个 shell 应用程序、一个用于其他项目的 C 库以及一个用 Flask、HTML、CSS 和 Javascript 编写的 Web 界面组成。
我决定创建一个魔方解算器,因为我之前用 C++ 制作过一个数独解算器,而魔方是一个进步。
我有一个带有边缘和拐角的半 3d 平面,而不是 2d 平面。
因为AI解算器已经完成了,而且我对自己的AI写作能力没有足够的信心,所以我决定让程序按照我的方式解决立方体:
交叉、F2L、OLL 和 PLL
下载 bin/solver 可执行文件或使用make solver
从源代码构建
从该存储库下载或克隆文件并使用make solver
进行构建
使用此存储库下载或克隆文件并编译 src 中除solver_library 之外的所有 .c 文件
使用以下命令之一执行求解器:
./solver "Algorithm"
./solver [Up] [Front] [Right] [Back] [Left] [Down]
将算法替换为打乱算法(例如U R2 FBR B2 R U2 L B2 RU' D' R2 FR' L B2 U2 F2
)或带有该脸部颜色的脸部(例如wbwowrwgw gwgogrgyg rwrgrbryr bwbrbobyb owobogoyo ygyoyryby
)。
可能的颜色是绿色、红色、蓝色、橙色、白色和黄色的第一个字符。
如果 olls.txt 和 plls.txt 文件与二进制文件不在同一文件夹中,请使用选项 -o 和 -p 或 -d 指向这些文件
bin/solver -d data/ "U R2 F B R B2 R U2 L B2 R U' D' R2 F R' L B2 U2 F2"
bin/solver -o data/olls.csv -p data/plls.csv "U R2 F B R B2 R U2 L B2 R U' D' R2 F R' L B2 U2 F2"
添加选项-t
以仅打印文本而不打印立方体的图像。
从此存储库下载或克隆文件。
使用 pip 安装 Flask 和 Numpy。
python3 -m pip -r requirements.txt
使用命令flask run
或python3 app.py
来运行网络服务器。
转至 https://127.0.0.1:5000/ 使用求解器。
从此存储库下载或克隆文件。
手动或使用make clean
删除 Linux 版本的 bin/libcubesolver.so 并使用make library
重新编译。
使用 pip 安装 Flask 和 Numpy:
python3 -m pip -r requirements.txt
使用命令flask run
或python3 app.py
来运行网络服务器。
转至 https://127.0.0.1:5000/ 使用求解器。
从此存储库下载或克隆文件。手动删除 Linux 版本的 bin/libcubesolver.so。
将src中除solver.c之外的所有.c文件编译到bin/libcubesolver.so。或者,如果使用不同的名称,请更改 app.py 中的第 19 行以反映这一点。
使用 pip 安装 Flask 和 Numpy:
python3 -m pip -r requirements.txt
使用命令flask run
或python3 app.py
来运行网络服务器。
转至 https://127.0.0.1:5000/ 使用求解器。
将 src 中除solver.c 之外的所有文件编译为 libcubesolver.so、libcubesolver.dylib 或 libcubesolver.dll,并将其保存在电脑上保存库的任何位置。
在 Linux 上你可以使用make library
将 libcubesolver.h 从 src 复制到电脑上保存标头的位置(例如 /usr/include/)
使用-lcubesolver
链接或只是编译到应用程序中,就好像它是 .o 文件一样
#include
#include
//Use either setup, or both load_olls and load_plls.
//Load all OLLs and PLLs into memory. Path is the folder where the olls.csv and plls.csv file are located.
//Returns indicating for succes or failure.
setup ( path );
//Loads the OLLs from a CSV file. Returns bool indicating success or failure.
load_olls ( filename );
//Loads the PLLs from a CSV file. Returns bool indicating success or failure.
load_plls ( filename );
//Create an array to hold the cube. 6 faces, 9 squares per face.
//The faces in order are Front, Right, Back, Left, Up and Down.
//The "Colors" are saved as the numbers 0 to 5.
int cube [ 6 ][ 9 ];
//Add the "colors" of the cube to the array as 9 character strings containing numbers 0 to 5.
color_cube ( cube , front , right , back , left , up , down );
//Run a multiple move algorithm on the cube, using standard cube notation. (Useful for scrambling)
run_algorithm ( cube , "Algorithm" );
/*
A function that prints the sides of the cube in an exploded fashion. Uses colors when in linux or OS X
Uses Green for 0, Red for 1, Blue for 2, Orange for 3, White for 4, Yellow for 5
WWW
WWW
WWW
OOO GGG RRR BBB
OOO GGG RRR BBB
OOO GGG RRR BBB
YYY
YYY
YYY
*/
print_cube ( cube );
//Validate the colors on the cube for impossible cubies. This does not check if the scramble is solvable.
//Returns bool.
if (! validate ( cube ))
{
return "Invalid color combinaton" ;
}
//returns pointer to string containing all algorithms used to solve the cube, separated by newlines,
//and the names of the steps. (eg. Cross, F2L, OLL: Sune, PLL: Y perm)
//Modifies the array to its solved position
char * solution = solve ( cube );
// Returns:
/*
Cross
(R D' F D)
(y) (R D' F D)
(y) (R D' F D)
(y) (R D' F D)
F2L
(y) R U R' U R U' R') (d' L U L')
(y2) (L' U' L) (y') (U' F' U F) (R' F R F')
(d2 R' U2 R2 U R2 U R)
(y) (U R U R' U2) (d R' U2 R) (U' R B' R' B)
OLL: Kite
(y2) (R U R' U') R' F R2 U R' U' F'
PLL: G perm c
(y2) L' R' U2 L R (y) L U' R U2 L' U R'
*/
//Generates a cube from an algorithm and returns pointer to its solution, or an error if unsolvable.
char * solution2 = solve_scramble ( "scramble" );
//Solves the (yellow) cross.
//Returns a pointer to a string containing the solve algorithm, with each solved edge separated by newlines.
//May also return an error if unsolvable.
//Modifies the cube array to have a solved cross.
char * cross = solve_cross ( cube );
//Solves the first two layers of the cube assuming a solved cross.
//Returns a string containing the solve algorithm, with each solved pair separated by newlines.
//Modifies the cube array to solve its f2l.
//Returns null if the cube is unsolvable.
char * f2l = solve_f2l ( cube );
//Looks up the right OLL algorithm and runs it, assuming a solved F2L.
//Returns the name of the OLL, and the algorithm, separated by a newline.
//Returns null if the cube is unsolvable.
char * oll = solve_oll ( cube );
//Looks up the right PLL algorithm and runs it, assuming a solved OLL.
//Returns the name of the PLL, and the algorithm, separated by a newline.
//Returns null if the cube is unsolvable.
char * pll = solve_pll ( cube );
请参阅 C 版本以了解每个函数的作用。
因为在 python 中很难释放分配的内存,所以请使用求解函数的“安全”版本,并在使用后使用 free_strings() 。
import numpy
import ctypes
# Most functions will need a c 2d int array. Let's give it a name so it's easy to use.
array_2d_int = numpy . ctypeslib . ndpointer (
dtype = ctypes . c_int , ndim = 2 , flags = 'CONTIGUOUS' )
# First load the solver library using ctypes CDLL.
solver = ctypes . CDLL ( "/path/to/libcubesolver.so" )
# Then we set up all functions we might want to use.
# Setup, load_olls and load_plls require a string.
solver . setup . argtypes = [ ctypes . c_char_p ]
solver . load_olls . argtypes = [ ctypes . c_char_p ]
solver . load_olls . argtypes = [ ctypes . c_char_p ]
# Run_algorithm requires a 2d array for the cube, and a string.
solver . run_algorithm . argtypes = [ array_2d_int , ctypes . c_char_p ]
# All other functions require just the 2d array.
solver . print_cube . argtypes = [ array_2d_int ]
solver . validate . argtypes = [ array_2d_int ]
solver . solve_safe . argtypes = [ array_2d_int ]
solver . solve_cross_safe . argtypes = [ array_2d_int ]
solver . solve_f2l_safe . argtypes = [ array_2d_int ]
solver . solve_oll_safe . argtypes = [ array_2d_int ]
solver . solve_pll_safe . argtypes = [ array_2d_int ]
# For functions that return something other than an int (or bool) we also need to set the response type.
solver . solve_safe . restype = ctypes . c_char_p
solver . solve_cross_safe . restype = ctypes . c_char_p
solver . solve_f2l_safe . restype = ctypes . c_char_p
solver . solve_oll_safe . restype = ctypes . c_char_p
solver . solve_pll_safe . restype = ctypes . c_char_p
# Load the olls and plls csvs. in my case they're in the data folder.
# Use .encode('utf-8') to convert the python string to a c string.
solver . setup ( "data" . encode ( 'utf-8' ))
# Set up a cube. The inner lists, in order, are Front, Right, Back, Left, Up and Down.
# By default 0 = green, 1 = red, 2 = blue, 3 = orange, 4 = white, 5 = yellow.
solvedcube = [[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ],
[ 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ],
[ 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 ],
[ 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 ],
[ 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 ]]
# Turn it into a C 2d array.
cube = numpy . array ( solvedcube ). astype ( ctypes . c_int )
# Run a scramble on the array. Use .encode('utf-8') to change the python string to a c string.
solver . run_algorithm (
cube , "U R2 F B R B2 R U2 L B2 R U' D' R2 F R' L B2 U2 F2" . encode ( 'utf-8' ))
# Print the cube to the shell.
solver . print_cube ( cube )
# Validate the color comibinations on the cube.
if not validate ( cube ):
return "Invalid color combinaton"
# Solve the cube using solver.solve.
# Note: running the function also modifies the cube array.
solution = solver . solve_safe ( cube ). decode ( "utf-8" )
# Returns:
"""
Cross
(R D' F D)
(y) (R D' F D)
(y) (R D' F D)
(y) (R D' F D)
F2L
(y) R U R' U R U' R') (d' L U L')
(y2) (L' U' L) (y') (U' F' U F) (R' F R F')
(d2 R' U2 R2 U R2 U R)
(y) (U R U R' U2) (d R' U2 R) (U' R B' R' B)
OLL: Kite
(y2) (R U R' U') R' F R2 U R' U' F'
PLL: G perm c
(y2) L' R' U2 L R (y) L U' R U2 L' U R'
"""
# Or returns an error if the cube is unsolvable.
# You can also do the steps separately.
# solve the cross.
cross = solver . solve_cross_safe ( cube ). decode ( "utf-8" )
# returns
'''
(R D' F D)
(y) (R D' F D)
(y) (R D' F D)
(y) (R D' F D)
'''
# solve the F2L.
f2l = solver . solve_f2l_safe ( cube ). decode ( "utf-8" )
# returns
'''
(y) R U R' U R U' R') (d' L U L')
(y2) (L' U' L) (y') (U' F' U F) (R' F R F')
(d2 R' U2 R2 U R2 U R)
(y) (U R U R' U2) (d R' U2 R) (U' R B' R' B)
'''
# solve the OLL.
oll = solver . solve_oll_safe ( cube ). decode ( "utf-8" )
# returns
'''
Kite
(y2) (R U R' U') R' F R2 U R' U' F'
'''
# solve the PLL.
pll = solver . solve_pll_safe ( cube ). decode ( "utf-8" )
# returns
'''
G perm c
(y2) L' R' U2 L R (y) L U' R U2 L' U R'
'''
# If a step is already solved, the functions return an empty string.
# With an unsolvable cube, these functions return NULL, which causes an
# AttributeError when trying to decode. You may want to verify before decoding. (or use try)
# Clean up the memory for the solution strings.
solver . free_strings ()
# Finally clean up the loaded OLLs or PLLs to prevent memory leaks
solver . cleanup_last_layer ()
CS50 希望我解释每个文件的作用以及其中的内容。所以这个自述文件比我的除 3 个文件之外的所有文件都长。
├── 应用程序.py ├── 垃圾箱 │ ├── libcubesolver.so │ └── 解算器 ├── 数据 │ ├── 错误.txt │ ├── olls.csv │ ├── 模式.csv │ └── plls.csv ├── 生成文件 ├── 自述文件.md ├── 需求.txt ├── 来源 │ ├── 交叉.c │ ├── cross.h │ ├── 立方体.c │ ├── 立方体.h │ ├── Cubesolver.h │ ├── f2l.c │ ├── f2l.h │ ├──lastlayer.c │ ├── Lastlayer.h │ ├── 求解器.c │ ├──solver_library.c │ ├── utils.c │ └── utils.h ├── 测试 │ ├──随机测试.c │ └──随机测试 ├── 静态 │ ├── 立方体.css │ ├── 立方体.js │ ├── cubeinterface.js │ ├── favicon.ico │ ├── 下一个.png │ ├── 暂停.png │ ├── 播放.png │ └── 上一页.png ├── 模板 │ ├── 立方体.html │ └── 求解器.html
Cube.c 和cube.h 包含控制内存中魔方的所有代码。
魔方在内存中是一个 6 x 9 的二维数组。外部数组包含六个面:前、右、后、左、上和下。
我选择这个顺序是因为上下面总是相同的,所以我可以对它们进行硬编码,但侧面只需要彼此相对正确。这意味着我可以使用简单的加号或减号,结合模数来转换测试并移动到每一侧。颜色也是如此,保存为数字0到5,0是前面的颜色,1是右边的颜色,等等。
我首先选择了我使用的颜色顺序:红色在前面,黄色在上面,绿色在右边。但后来在编写我的Python代码和界面时,我切换到“官方”绿色在前面,白色在上面,所以我不需要旋转用户输入的立方体以在底部有白色。
首先我创建了 color_cube() 函数。这将使用 6 个字符串将您选择的模式应用于立方体。
它使用简单的字符数学将字符“0”、“1”等更改为整数并将它们存储到给定的数组中。
我只使用它一次或两次作为一个衬垫来在一行中设置一个已解决的立方体,因为当将用户输入转换为计算机可以理解的内容时,您最好单独输入整数,而不是首先将其转换为字符串。
Print_cube() 将提供的立方体输出到终端。这在调试和终端应用程序中经常使用。我首先以爆炸魔方的形状输出数字 0 到 5。
444
444
444
333 000 111 222
333 000 111 222
333 000 111 222
555
555
555
因为它会生成更多行代码来自动执行此操作,所以我只使用 9 个打印语句,每个语句最多包含 12 个变量。
后来我发现我可以添加像e[42m
这样的 shell 代码来为这些数字提供背景颜色。添加这些后,每次打印就有 28 个变量。
最后我想到简单地将颜色的字母或数字放入保存 shell 代码的数组中,将其恢复为 12。不幸的是 Windows 不支持这种类型的 shell 代码,所以我不得不继续仅使用数字,并且后来的信。
Move_cube() 是第一个实际更改立方体的函数。这是一个递归函数,它执行所请求的移动所请求的次数。有 9 种可能的移动,编号为 0 到 8。默认的前、右、后等。还有中间、赤道和站立层。使用我在 utils.c 中编写的名为 Cycle 的辅助函数,我切换每个面上每个方块的值,与顺时针移动相对应,如有必要,可重复。
因为移动通常不会自行发生,所以接下来我编写了 run_algorithm()。此函数使用 move_cube() 对以标准立方体表示法提供的立方体运行算法。这使我能够一次(或实际上按顺序)执行多个移动,并且执行对 2 甚至 3 层(如 x、y 和 z)有影响的移动。
最后,验证()。我在编写 Python 界面时添加了这个函数,因为你永远不应该相信你的用户不会输入奇怪的东西。
此函数不会检查魔方是否可解。它检查颜色是否正确。例如,一个立方体不能同时贴有白色和黄色贴纸。顺时针方向,可以按顺序贴上绿色、白色和红色贴纸,但反之则不行。
为了做到这一点,我部分复制了 Tideman 中锁定数组的想法,对角使用 2d 布尔数组,对边缘使用 3d 布尔数组,以查看是否可以组合 2 种或 3 种颜色。这段代码经历了多次迭代。首先分别检查每个边缘和角,然后使用一些 for 循环。
Cross.c 包含解决立方体交叉所需的所有代码。因为我使用“人类方法”(CFOP)来解决立方体,所以我被迫使用巨大的决策树。
我在最后一刻重写了几乎整个文件,因此不必每一步都对齐边缘。
查找黄绿色边缘在底层的位置,或者如果尚不存在则将在该位置。这样可以更轻松地以更少的步骤对齐其他边缘,因为它们可以对齐到“相对零”而不是实际零。
这 6 种情况分别是边的定向方式:在上层和下层中指向下、指向上、指向左、在 E 层上指向右。
这些函数基本上按从 0 到 3 的顺序扫描面,以查找属于十字的棋子,将它们旋转到位,并使用我称为 append() 的辅助函数将移动存储在 char 数组中。
Solve_cross 首先调整立方体的方向,因此黄色向下。然后按顺序执行 6 个交叉案例。如果返回 null 以外的值,则返回到第一种情况。
一旦解决了所有 4 个边,函数就会返回算法。如果所有情况都测试不成功,或者在 4 次循环之后,函数返回 NULL。
与 cross.c 的想法相同,只是更大。 3600行代码解决所有案例。
这 11 种情况包括角的各种定向方式,以及角和边的一些组合。
为了节省时间和线条,大多数函数不会完全解决角/边组合,而是将它们带到最短的已知状态并调用正确的函数。
数字 1 到 11 是它们的写入顺序,并不总是它们的运行顺序。
Solve_f2l 首先验证交叉是否已解决。如果不是,则返回 null。
之后,solve_f2l 按照从最短平均算法到最长的顺序遍历案例,一旦函数返回算法,就返回到 1。如果所有情况都测试不成功,或者在 4 次循环之后函数返回 NULL。
我最自豪的文件。没有巨大的循环,没有决策树。只是智能数学和查找表。
Oll 和 pll 是两个用于存储 OLL 和 PLL 的结构。它们存储名称、我用来识别它们的模式以及解决它们的算法。
在一个不眠之夜,我发现我可以仅使用外环的颜色来识别 PLL,而仅使用 12 个布尔值(表示黄色或非黄色)即可识别 OLL。
Load_olls() 和 load_plls() 从 CSV 文件加载名称、模式和算法。这经历了一些迭代。
我首先通过 getline() 获取 CSV 文件的每一行,并将字符串分割为 24 个字符和 36 个字符,并用空格填充名称。
这不太漂亮,所以我开始寻找逗号并在那里分割东西。
最后,discord 上的某人指导我使用 sscanf(),让我可以轻松、干净地分割每个字符串。
我还切换到 fgets() 以使线路更加交叉兼容。
以 # 开头的行将被拒绝,因为它们是注释。使用名为 isNumber() 或 isBinary() 的辅助函数来识别模式不是纯数字的行并被拒绝,在编写本节时,我添加了一个测试来查看名称、模式和算法的长度是否正确停止越界读取和写入。
在 olls 和 plls 数组中,使用 malloc 创建名称和算法的空间,并使用 strncpy 复制信息。该模式将转换为 12 个整数或布尔值的数组。
Find_pll() 是我的骄傲和喜悦。这是我为求解立方体而编写的第一个算法(在 cross 和 f2l 之前),它是在我的梦中出现的。一种通过一些简单的数学检查任何颜色组合的 oll 图案的方法。我真的醒了,写下了int case[12] containing the outer ring. if (front[0] == (front[1]-(case[0]-case[1])%4))
。第二天我将其简化为(faces[0][0] == (faces[0][1] - (plls[i].pattern[0] - plls[i].pattern[1])) % 4)
(乘以 12)。然后(faces[j][0] - faces[j][1]) % 4 == (plls[i].pattern[0] - plls[i].pattern[1]) % 4
最后经过多次迭代我最终得到了mod((cube[(j + i / 3) % 4][i % 3] + dif[j]), 4)
允许我使用以下方法对立方体顶层的图案进行编码一个 12 int 数组,以便于与所有已知值进行比较。
我本可以将模式编码为 4 字节 int,但我决定不这样做,因为我认为让模式易于理解比节省 44 字节(模式总共 3408 kb)更好
最后,该函数返回一个pair_int_int,其中包含层的方向以及正确的OLL或PLL。如果没有找到的话,或者两个负数。
函数solve_oll()和solve_pll()相对简单。他们调用 find_oll 或 pll 来获取正确的索引和方向(y 移动量)。然后该函数使用 y 方向移动来转动立方体,运行正确的 OLL 或 PLL 算法,并且在使用solve_pll() 的情况下转动 U 层以最终求解立方体。这些函数返回用于以标准立方体表示法求解立方体的算法。
释放用于存储所有 OLL 和 PLL 的内存。
有些功能可以让我的生活更轻松一些。
因为正常的 % 不适用于负数,所以这是“我自己的版本”。特别感谢 cs50 discord。
对于某些函数,我想返回多个变量。这个基于 C++pair
循环 4 个数字的函数。基本上是交换的一个进步。在cube.c 中用于循环立方体移动的边和角。 cube.js 中有该动作的副本
将一个字符串添加到另一个字符串的函数,并在必要时重新分配。在此应用程序中随处使用。
检查字符串是否仅包含数字,或仅包含零和一。目前仅用于加载 OLL 和 PLL。
我首先编写了solver.c作为我的立方体解算器的接口,但很快就决定我想要一个图形UI,我还没有准备好用纯C语言来做。
这就是为什么我开始将我的立方体求解器制作成一个库,以便与我想要的任何东西一起使用。
将它们放在一起来解决立方体。它需要一个立方体并验证它。然后尝试解决交叉问题。如果失败,则返回错误,否则存储输出。然后它解决交叉或返回错误。然后它求解 OLL(或返回)和 PLL。最后它返回一个包含完整解决方案的字符串。
解决问题的接口。从输入的打乱生成一个立方体并将其传递给solve()
只为让大家的生活更轻松一点。获取一条路径,并从该路径加载 ols 和 plls。如果有效则返回 true,自行清理,如果无效则返回 false。
因为很难从 Python 中释放内存,所以我决定从 C 中释放内存。我有您可能想要从 C 外部使用的每个函数的“安全”版本。它们存储需要释放到全局数组的生成的指针一次性释放全部。
释放所有存储的字符串
多个函数返回字符串,最终需要释放它们。这些函数存储指向这些字符串的指针以供以后删除。
对于测试,以及那些使用键盘比使用鼠标更好的人,终端接口。
它进行了多次重写,最终在编写本自述文件时被重写以与solver_library一起使用而不是单独使用。
它无所不能!它根据默认或用户输入加载 OLL 和 PLL。然后它根据命令行或用户输入生成一个立方体,最后逐步求解该立方体,并一路打印该立方体。
从给定的文件路径(例如argv[0])获取目录。编写用于基于 Windows 和 Unix 的操作系统。
将颜色字母更改为相应的整数。在 python 中我只使用了字典。容易多了。
询问用户魔方面的九种颜色,并将它们放在魔方上。
用于将颜色输入到立方体的分步交互界面。
打印帮助测试。
打印用法。
用于错误测试的简单简短应用程序。产生大量的混乱并解决它们。打印第一个失败并停止的扰码。
我用它来发现一个拼写错误。其他一切都是我手动完成的。
使用 memcmp 将求解算法生成的立方体与 4 个已求解立方体之一(每个方向一个)进行比较。
用一点内存来换取速度。
为魔方生成随机打乱。糟糕的是。不能防止双重移动和反向移动。但如果争夺时间足够长就足够了。
从用户输入加载 OLL 和 PLL。根据用户的需要多次解算立方体,直到失败。
包含与我的 C 库一起使用的所有 58 个 OLL 案例(包括已解决的案例)的列表
算法来自我自己的记忆和https://www.speedsolving.com/wiki/index.php/OLL
包含与我的 C 库一起使用的所有 22 个 PLL 案例(包括已解决的案例)的列表
来自我自己记忆和 https://www.speedsolving.com/wiki/index.php/PLL 的算法
包含一些以漂亮的方式打乱立方体的模式。主要用于测试我的 javascript,但对于用户来说这是一个很好的功能。
希望不包含任何内容,但旨在存储求解立方体时的所有错误。
我的 C 代码和 javascript 之间的桥梁。它加载 C 库,并使用 argtypes 和必要时的 restypes 初始化我需要的函数。使用 numpy 将 python 类型转换为 ctypes。
我从 stackoverflow“借用”的一个函数。它需要一个输入流并输出直到 # 的每一行
实际的求解器。它需要一个打乱的立方体并逐步解决它,以字典数组的形式返回步骤(包含带有算法的数组)。如果出现异常,它将模式存储在 data/errors.txt 中
仅用于测试。从 GET 中获取置乱算法,使用 getsteps() 对其进行求解,并将其传递给求解器模板。
从 GET 或 POST 中获取模式并打乱,使用 getsteps 解决它并将其作为 JSON 返回。
从 GET 或 POST 中获取模式并解决多维数据集,不关心实际的解决方案,而是检查它是否失败以及在哪里失败。返回 0 表示成功或错误编号。
显示cube.html 模板:实际的UI。在这样做的同时,它还会将 data/patterns.csv 中的模式提供给模板,使用听写器和 decomment 来删除我的评论。
将网站图标传递到浏览器以获取漂亮的立方体符号。
释放我的 C 代码中的所有内存:PLL 和字符串。使用atexit注册以在退出时运行
用于在 Linux 或 OS X 上编译 C 代码的所有命令。尽管我的库可以用作库,但我决定使用所有文件进行编译,这样我就可以只给出一个可执行文件。
包含我使用的两个需要单独安装的库。只需烧瓶和 numpy。没什么特别的。
请求打乱,将其传递给 app.py 进行求解并打印解决方案。仅用于快速测试。
包含许多空的 div,由我的 javascript 代码填充,并由 css 塑造。包含 3 个用于播放控制的图像、一个用于输入打乱的表格以及一个简短的说明。
最后加载 javascript,因为没有 dom 就什么都不是。不是那么漂亮,但是非常实用。
除了 src 包含最实际的代码之外。一些 CSS 使所有东西看起来都可用,而 javascript 则使其实际上可用。还有一些可供点击的图像。
将随机 div 的链接更改为您可以看到的内容。
以默认和较少默认的方式使用 CSS 网格。
我使用外部容器将控件放置在左侧,将解算器动画放置在右上方,将解决方案放置在右下方。
使用绝对定位,我将播放控件和当前算法放在动画上。
除了颜色选择器之外,其他一切都是默认的。如果您查看 html 代码,您只会看到一个包含 6 个 div 的 div,每个 div 包含 9 个 div。我使用网格将 6 个“面”div 塑造成一个展开的立方体,每个面包含 9 个正方形的网格。这允许我在形状保持不变的情况下使大小可变,而不需要使用图像。
我还花了一两周时间制作移动视图,因为文本的大小似乎是可变的。我联系了discord、facebook,甚至stackoverflow。最后我在stackoverflow上找到了一行html: 它在一行中解决了所有问题。
将我的算法带给用户。基于三个.js
这是我第一次真正编写javascript。它看起来不太漂亮,可能违反了所有规则,但它确实有效。
我多次重写了这个文件。第一次迭代,是在我开始上传到 github 之前,然后是功能性的,最后是面向对象的
首先,我声明需要由多个函数使用的所有变量: Three.js 对象的材质和几何形状、存储对象的数组、旋转轴等等。
还有一个巨大的字典,其中包含每个合法立方体移动所需的变量:要循环或交换的立方体、旋转的轴和角度以及要显示的内容。这些都可以容纳在 72 行中,但是 style50 需要每行一个变量,因此有 649 行。
获取场景并保存参考,循环生成立方体、轮廓和平面,循环设置立方体和轮廓以及手动平面的位置。
辅助功能。使用临时立方体循环旋转 4 个立方体 a > b > c > d > a。
辅助功能。使用临时立方体交换 2 个立方体的旋转。
将立方体重置为其默认位置并重置解决方案 div。这需要比您想象的更多的步骤。首先我们需要重置所有动画,完成它。重置所有未来和过去的移动,最后重置立方体的旋转。
根据立方体是否移动返回 true 或 false。
设置在立方体上移动的动画。
这个函数最初是 160 行 if/else 语句,但我后来重写了它,从一个巨大的字典中获取立方体和移动。这使得这个函数和下一个函数更容易阅读,并且使得如果我编写 4x4 或 5x5 版本我只需要更改字典。
完成动画,并实际移动(旋转)立方体。
因为立方体并不真正移动,所以基本思想是复制立方体的旋转,在真实的立方体上会移动到它的位置,然后旋转所有东西。
这个函数最初是一个 375 行的 if/else 语句的庞然大物。后来它被重写以与动作字典一起使用。
执行移动列表中的下一个移动,或步骤列表中的下一个算法,并在屏幕上显示当前算法。
前一个动作是在相反的移动列表中,还是步骤列表中的前一个算法
通过旋转每个立方体,将颜色选择器(或实际上是面数组)中的颜色应用到立方体。包含一些自己的功能每个立方体都有自己的带有手动修改器的线路。对我来说,这太随机了,无法自动化。但如果你想尝试,请成为我的客人。
将绿色变为蓝色,将红色变为橙色,将白色变为黄色,反之亦然。现在我想起来了,这可能是一个指令,但一行数学和 3 个 if 也可以。
根据前面和左侧的颜色或向上的颜色旋转立方体。基本上有很多带有硬编码动作的 if 语句,但这里就足够了。