我的 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。的信。
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 中釋放內存。性釋放全部。
釋放所有儲存的字串
多個函數傳回字串,最終需要釋放它們。這些函數儲存指向這些字串的指標以供以後刪除。
對於測試,以及那些使用鍵盤比使用滑鼠更好的人,終端介面。
它進行了多次重寫,最終在編寫本自述文件時被重寫以與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 語句,但這裡就足夠了。