Mein letztes Projekt für CS50 ist ein Zauberwürfellöser mit „menschlicher Methode“.
Dies bedeutet, dass der eingegebene Zauberwürfel mit einer Methode gelöst wird, die ein Mensch verwenden könnte (CFOP), mit klaren Schritten: Cross, F2L, OLL und PLL.
Das Endprodukt besteht aus einer Shell-Anwendung, einer C-Bibliothek zur Verwendung in anderen Projekten und einer in Flask, HTML, CSS und Javascript geschriebenen Weboberfläche.
Ich habe mich entschieden, einen Zauberwürfel-Löser zu erstellen, weil ich zuvor einen Sudoku-Löser in C++ erstellt habe und der Würfel eine Weiterentwicklung darstellte.
Anstelle einer 2D-Ebene habe ich eine Semi-3D-Ebene mit Kanten und Ecken.
Da es bereits KI-Löser gibt und ich nicht genug Vertrauen in meine KI-Schreibfähigkeiten habe, habe ich beschlossen, das Programm so zu gestalten, dass es den Würfel so löst, wie ich es tue:
Cross, F2L, OLL und PLL
Laden Sie die ausführbare Bin/Solver-Datei herunter oder erstellen Sie sie mit make solver
aus der Quelle
Laden Sie die Dateien aus diesem Repository herunter oder klonen Sie sie und erstellen Sie sie mit make solver
Laden Sie die Dateien mit diesem Repository herunter oder klonen Sie sie und kompilieren Sie alle .c-Dateien in src mit Ausnahme von „solver_library“.
Führen Sie den Solver mit einem der folgenden Befehle aus:
./solver "Algorithm"
./solver [Up] [Front] [Right] [Back] [Left] [Down]
Ersetzen des Algorithmus durch einen Scramble-Algorithmus (z. B. U R2 FBR B2 R U2 L B2 RU' D' R2 FR' L B2 U2 F2
) oder die Gesichter mit den Farben auf diesem Gesicht (z. B. wbwowrwgw gwgogrgyg rwrgrbryr bwbrbobyb owobogoyo ygyoyryby
).
Die möglichen Farben sind die ersten Zeichen Grün, Rot, Blau, Orange, Weiß und Gelb.
Wenn sich die Dateien olls.txt und plls.txt nicht im selben Ordner wie die Binärdatei befinden, verwenden Sie die Optionen -o und -p oder -d, um auf die Dateien zu verweisen
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"
Fügen Sie die Option -t
hinzu, um nur den Text ohne Bilder des Würfels zu drucken.
Laden Sie die Dateien aus diesem Repository herunter oder klonen Sie sie.
Installieren Sie Flask und Numpy mit pip.
python3 -m pip -r requirements.txt
Verwenden Sie entweder den Befehl flask run
oder python3 app.py
um den Webserver auszuführen.
Gehen Sie zu https://127.0.0.1:5000/, um den Solver zu verwenden.
Laden Sie die Dateien aus diesem Repository herunter oder klonen Sie sie.
Entfernen Sie die Linux-Version von bin/libcubesolver.so manuell oder mit make clean
und kompilieren Sie sie mit make library
neu.
Installieren Sie Flask und Numpy mit pip:
python3 -m pip -r requirements.txt
Verwenden Sie entweder den Befehl flask run
oder python3 app.py
um den Webserver auszuführen.
Gehen Sie zu https://127.0.0.1:5000/, um den Solver zu verwenden.
Laden Sie die Dateien aus diesem Repository herunter oder klonen Sie sie. Entfernen Sie die Linux-Version von bin/libcubesolver.so manuell.
Kompilieren Sie alle .c-Dateien in src mit Ausnahme von „solver.c“ nach „bin/libcubesolver.so“. Wenn Sie einen anderen Namen verwenden, ändern Sie Zeile 19 in app.py entsprechend.
Installieren Sie Flask und Numpy mit pip:
python3 -m pip -r requirements.txt
Verwenden Sie entweder den Befehl flask run
oder python3 app.py
um den Webserver auszuführen.
Gehen Sie zu https://127.0.0.1:5000/, um den Solver zu verwenden.
Kompilieren Sie alle Dateien in src mit Ausnahme vonsolver.c zu libcubesolver.so, libcubesolver.dylib oder libcubesolver.dll und speichern Sie sie überall dort, wo Bibliotheken auf Ihrem PC gespeichert sind.
Unter Linux können Sie make library
verwenden
Kopieren Sie libcubesolver.h von src dorthin, wo Header auf Ihrem PC gespeichert sind (z. B. /usr/include/).
Verknüpfen Sie mit -lcubesolver
oder kompilieren Sie es einfach in die Anwendung, als wäre es eine .o-Datei
#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 );
Sehen Sie sich die C-Version an, um zu sehen, was die einzelnen Funktionen bewirken.
Da es in Python schwierig ist, mallocierten Speicher freizugeben, verwenden Sie „sichere“ Versionen der Lösungsfunktionen und verwenden Sie nach der Verwendung 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 möchte, dass ich erkläre, was jede Datei bewirkt und was darin enthalten ist. Daher ist diese Readme-Datei länger als alle bis auf drei meiner Dateien.
├── app.py ├── Behälter │ ├── libcubesolver.so │ └── Löser ├── Daten │ ├── Fehler.txt │ ├── olls.csv │ ├── patterns.csv │ └── plls.csv ├── Makefile ├── README.md ├── Anforderungen.txt ├── src │ ├── kreuz.c │ ├── kreuz.h │ ├── Cube.c │ ├── Cube.h │ ├── Cubesolver.h │ ├── f2l.c │ ├── f2l.h │ ├── lastlayer.c │ ├── lastlayer.h │ ├── Solver.c │ ├──solver_library.c │ ├── utils.c │ └── utils.h ├── Test │ ├── randomtests.c │ └── Zufallstests ├── statisch │ ├── Cube.css │ ├── Cube.js │ ├── Cubeinterface.js │ ├── favicon.ico │ ├── next.png │ ├── pause.png │ ├── play.png │ └── prev.png ├── Vorlagen │ ├── Cube.html │ └── Solver.html
Cube.c und Cube.h enthalten den gesamten Code, der den Zauberwürfel im Speicher steuert.
Ein Zauberwürfel ist im Speicher ein 6 x 9 großes 2D-Array. Das äußere Array enthält die sechs Flächen: Vorne, Rechts, Hinten, Links, Oben und Unten.
Ich habe diese Reihenfolge gewählt, weil die oberen und unteren Flächen immer gleich sind, sodass ich sie fest codieren kann, aber die Seiten müssen nur im Verhältnis zueinander korrekt sein. Das bedeutet, dass ich einfaches Plus oder Minus in Kombination mit Modul verwenden kann, um die Tests und Bewegungen in alle Richtungen zu übersetzen. Das Gleiche gilt für Farben, die als Nummern von 0 bis 5 gespeichert werden, wobei 0 die vordere Farbe, 1 die richtige Farbe usw. ist.
Ich habe zunächst die Farbreihenfolge gewählt, die ich verwende: Rot vorne, Gelb oben, Grün rechts. Aber später, als ich meinen Python-Code und meine Python-Schnittstelle schrieb, wechselte ich zum „offiziellen“ Grün vorne und Weiß oben, sodass ich den vom Benutzer eingegebenen Würfel nicht drehen muss, um Weiß unten zu haben.
Zuerst habe ich die Funktion color_cube() erstellt. Dadurch wird ein Muster Ihrer Wahl mithilfe von 6 Schnüren auf den Würfel angewendet.
Es verwendet einfache Zeichenmathematik, um die Zeichen „0“, „1“ usw. in Ganzzahlen zu ändern und sie im angegebenen Array zu speichern.
Ich verwende dies nur ein- oder zweimal als Einzeiler, um einen gelösten Würfel in einer Zeile einzurichten, denn wenn Sie Benutzereingaben in etwas übersetzen, das der Computer versteht, können Sie die Ints genauso gut einzeln eingeben, anstatt sie zuerst in eine Zeichenfolge zu übersetzen.
Print_cube() gibt den bereitgestellten Cube an das Terminal aus. Dies wird häufig beim Debuggen und für die Terminalanwendung verwendet. Ich begann damit, nur die Zahlen 0 bis 5 in Form eines explodierten Zauberwürfels auszugeben.
444
444
444
333 000 111 222
333 000 111 222
333 000 111 222
555
555
555
Da es mehr Codezeilen erfordern würde, dies zu automatisieren, verwende ich einfach 9 Druckanweisungen, die jeweils bis zu 12 Variablen enthalten.
Später fand ich heraus, dass ich Shell-Codes wie e[42m
hinzufügen konnte, um diesen Zahlen eine Hintergrundfarbe zu geben. Wenn ich diese hinzufüge, erhalte ich 28 Variablen pro Ausdruck.
Schließlich dachte ich daran, einfach den Buchstaben oder die Zahl für die Farbe in das Array einzufügen, das die Shell-Codes enthält, und es wieder auf 12 zu bringen. Leider unterstützt Windows diese Art von Shell-Codes nicht, also musste ich weiterhin nur Zahlen verwenden, und spätere Briefe.
Move_cube() ist die erste Funktion zum tatsächlichen Ändern des Würfels. Dies ist eine rekursive Funktion, die die angeforderte Bewegung so oft ausführt wie gewünscht. Es gibt 9 mögliche Bewegungen, nummeriert von 0 bis 8. Die Standardwerte sind „vorne“, „rechts“, „hinten“ usw., aber auch die mittlere, der Äquator- und die stehende Ebene. Mit einer Hilfsfunktion namens Cycle, die ich in utils.c geschrieben habe, ändere ich die Werte jedes Quadrats auf jeder Seite entsprechend der Bewegung im Uhrzeigersinn und wiederhole den Vorgang bei Bedarf.
Da Bewegungen normalerweise nicht von selbst erfolgen, habe ich als Nächstes run_algorithm() geschrieben. Diese Funktion verwendet move_cube(), um einen Algorithmus für den in der Standard-Cube-Notation bereitgestellten Cube auszuführen. Dies gibt mir die Möglichkeit, mehrere Bewegungen gleichzeitig (oder tatsächlich der Reihe nach) auszuführen und Bewegungen auszuführen, die sich auf zwei oder sogar drei Ebenen wie x, y und z auswirken.
Zum Schluss validieren(). Ich habe diese Funktion beim Schreiben der Python-Schnittstelle hinzugefügt, weil Sie Ihrem Benutzer niemals vertrauen sollten, dass er keine seltsamen Dinge eingibt.
Diese Funktion prüft nicht, ob der Zauberwürfel lösbar ist. Es prüft, ob die Farben korrekt sind. Beispielsweise kann ein Würfel nicht sowohl weiße als auch gelbe Aufkleber haben. Und im Uhrzeigersinn ist es möglich, grüne, weiße und rote Aufkleber in dieser Reihenfolge zu haben, aber nicht umgekehrt.
Zu diesem Zweck habe ich die Idee des gesperrten Arrays in Tideman teilweise kopiert und dabei ein 2D-Boolesches Array für die Ecken und ein 3D-Boolesches Array für die Kanten verwendet, um zu sehen, ob eine Kombination aus 2 oder 3 Farben möglich ist. Dieser Code durchlief mehrere Iterationen. Überprüfen Sie zunächst jede Kante und Ecke einzeln und verwenden Sie später einige for-Schleifen.
Cross.c enthält den gesamten Code, der zum Lösen des Würfelkreuzes erforderlich ist. Da ich zum Lösen des Würfels eine „menschliche Methode“ (CFOP) verwende, bin ich irgendwie gezwungen, riesige Entscheidungsbäume zu verwenden.
Ich habe fast die gesamte Datei in letzter Minute umgeschrieben, damit die Kanten nicht bei jedem Schritt ausgerichtet werden müssen.
Findet heraus, wo auf der unteren Ebene sich der gelbgrüne Rand befindet oder wo er sein würde, wenn er noch nicht da ist. Dadurch ist es einfacher, die anderen Kanten in weniger Schritten auszurichten, da sie auf den „relativen Nullpunkt“ statt auf den tatsächlichen Nullpunkt ausgerichtet werden können.
Diese 6 Fälle beschreiben jede Art und Weise, wie eine Kante ausgerichtet werden kann: nach unten zeigend, nach oben zeigend, in den oberen und unteren Schichten zeigend, nach links zeigend und nach rechts zeigend auf der E-Ebene.
Die Funktionen scannen grundsätzlich die Flächen in der Reihenfolge 0 bis 3, um nach Teilen zu suchen, die zum Kreuz gehören, drehen sie an ihren Platz und speichern die Bewegungen mithilfe einer Hilfsfunktion, die ich append() nenne, in einem char-Array.
Solve_cross richtet den Würfel zunächst so aus, dass Gelb unten ist. Dann werden die 6 Kreuzfälle der Reihe nach ausgeführt. Wenn man etwas anderes als Null zurückgibt, kehrt man zum ersten Fall zurück.
Sobald alle 4 Kanten gelöst sind, gibt die Funktion die Algorithmen zurück. Wenn alle Fälle erfolglos getestet werden oder nach 4 Schleifen die Funktion NULL zurückgibt.
Die gleiche Idee wie cross.c, nur viel größer. 3600 Zeilen Code zur Lösung aller Fälle.
Diese 11 Fälle umfassen alle Möglichkeiten, wie eine Ecke ausgerichtet werden kann, sowie einige Kombinationen von Ecken und Kanten.
Um Zeit und Linien zu sparen, lösen die meisten Funktionen eine Ecke/Kante-Kombination nicht vollständig auf, sondern bringen sie in den kürzesten bekannten Zustand und rufen die richtige Funktion auf.
Die Zahlen 1 bis 11 sind in der Reihenfolge angegeben, in der sie geschrieben wurden, nicht immer in der Reihenfolge, in der sie ausgeführt werden.
Solve_f2l überprüft zunächst, ob das Kreuz gelöst ist. Wenn nicht, wird null zurückgegeben.
Danach durchläuftsolve_f2l die Fälle in der Reihenfolge vom kürzesten bis zum längsten Durchschnittsalgorithmus und geht zurück auf 1, sobald eine Funktion einen Algorithmus zurückgibt. Wenn alle Fälle erfolglos getestet werden oder nach 4 Schleifen die Funktion NULL zurückgibt.
Die Datei, auf die ich am meisten stolz bin. Keine riesigen Schleifen, keine Entscheidungsbäume. Nur intelligente Mathematik und Nachschlagetabellen.
Oll und pll sind zwei Strukturen zum Speichern von OLL und PLLs. Sie speichern den Namen, das Muster, das ich verwende, um sie zu erkennen, und den Algorithmus, um sie zu lösen.
In einer schlaflosen Nacht fand ich heraus, dass ich PLL allein an den Farben des äußeren Rings erkennen konnte, und das OLL an nur 12 booleschen Werten, was Gelb oder nicht Gelb bedeutet.
Load_olls() und load_plls() laden die Namen, Muster und Algorithmen aus einer CSV-Datei. Dies durchlief einige Iterationen.
Ich begann zunächst mit getline(), um jede Zeile der CSV-Dateien abzurufen, teilte die Zeichenfolge in 24 Zeichen und 36 Zeichen auf und füllte den Namen mit Leerzeichen auf.
Das war nicht schön, also fing ich an, nach Kommas zu suchen und die Dinge dort aufzuteilen.
Schließlich hat mich jemand im Discord zu sscanf() verwiesen, sodass ich jede Zeichenfolge einfach und sauber aufteilen konnte.
Ich habe auch auf fgets() umgestellt, um die Kompatibilität der Zeile zu verbessern.
Zeilen, die mit # beginnen, werden abgelehnt, da es sich um Kommentare handelt. Zeilen, bei denen das Muster nicht rein numerisch ist, werden mit einer Hilfsfunktion namens isNumber() oder isBinary() erkannt und zurückgewiesen. Beim Schreiben dieses Abschnitts habe ich einen Test hinzugefügt, um zu sehen, ob Name, Muster und Algorithmus die richtige Länge haben Stoppen Sie Lese- und Schreibvorgänge außerhalb der Grenzen.
Im olls- und plls-Array wird mit malloc Platz für die Namen und Algorithmen geschaffen und die Informationen werden mit strncpy hineinkopiert. Das Muster wird in ein Array von 12 Ints oder Booleschen Werten umgewandelt.
Find_pll() ist mein ganzer Stolz. Es war der erste Algorithmus, den ich zum Lösen des Würfels geschrieben habe (vor dem Kreuz und f2l), und er kam mir in einem Traum. Eine Möglichkeit, das Gesamtmuster mit ein paar einfachen Berechnungen auf jede Farbkombination zu überprüfen. Ich bin buchstäblich aufgewacht und habe int case[12] containing the outer ring. if (front[0] == (front[1]-(case[0]-case[1])%4))
. Am nächsten Tag habe ich es vereinfacht auf (faces[0][0] == (faces[0][1] - (plls[i].pattern[0] - plls[i].pattern[1])) % 4)
(mal 12). Dann (faces[j][0] - faces[j][1]) % 4 == (plls[i].pattern[0] - plls[i].pattern[1]) % 4
und schließlich nach vielen Iterationen Am Ende hatte ich mod((cube[(j + i / 3) % 4][i % 3] + dif[j]), 4)
und konnte damit das Muster der obersten Schicht des Würfels mit einer 12 kodieren int-Array, zum einfachen Vergleich mit allen bekannten Werten.
Ich hätte das Muster als 4-Byte-Int kodieren können, habe mich aber dagegen entschieden, weil ich denke, dass es besser ist, die Muster leichter verständlich zu machen, als 44 Bytes einzusparen (insgesamt 3408 KB für Muster).
Schließlich gibt die Funktion ein „pair_int_int“ zurück, das die Ausrichtung der Ebene und die richtige OLL oder PLL enthält. Oder zwei negative Zahlen, wenn nichts gefunden wird.
Die Funktionen „solve_oll()“ und „solve_pll()“ sind relativ einfach. Sie rufen find_oll oder pll auf, um den richtigen Index und die richtige Ausrichtung (Anzahl der y-Bewegungen) zu erhalten. Dann dreht die Funktion den Würfel mit einer Y-Bewegung, führt den richtigen OLL- oder PLL-Algorithmus aus und dreht im Fall vonsolve_pll() die U-Ebene, um den Würfel schließlich zu lösen. Die Funktionen geben den zum Lösen des Würfels verwendeten Algorithmus in der Standardwürfelnotation zurück.
Gibt den Speicher frei, der zum Speichern aller OLLs und PLLs verwendet wird.
Einige Funktionen, die mir das Leben ein wenig erleichtern sollen.
Da der normale %-Wert nicht mit negativen Zahlen funktioniert, ist dies „meine eigene Version“, die dies tut. Mit besonderem Dank an den CS50-Discord.
Für einige Funktionen möchte ich mehr als eine Variable zurückgeben. Dieses basiert auf dem C++-Paar
Eine Funktion, die 4 Zahlen durchläuft. Im Grunde ein Fortschritt gegenüber dem Tausch. Wird in Cube.c verwendet, um die Kanten und Ecken für Würfelbewegungen zu wechseln. Es gibt eine Kopie dieser Bewegung in Cube.js
Eine Funktion, die eine Zeichenfolge zu einer anderen Zeichenfolge hinzufügt und bei Bedarf neu zuordnet. Wird überall in dieser Anwendung verwendet.
Überprüft, ob eine Zeichenfolge nur Ziffern oder nur Nullen und Einsen enthält. Wird derzeit nur zum Laden von OLLs und PLLs verwendet.
Ich habe zuerst „solver.c“ als Schnittstelle für meinen Würfellöser geschrieben, mich dann aber schnell für eine grafische Benutzeroberfläche entschieden, wozu ich in reinem C noch nicht bereit bin.
Deshalb habe ich angefangen, meine Cubesolver in eine Bibliothek umzuwandeln, die ich mit allem verwenden kann, was ich will.
Fügt alles zusammen, um den Würfel zu lösen. Es nimmt einen Würfel und validiert ihn. Dann wird versucht, das Kreuz zu lösen. Wenn dies fehlschlägt, wird ein Fehler zurückgegeben, andernfalls wird die Ausgabe gespeichert. Dann löst es das Kreuz oder gibt einen Fehler zurück. Anschließend löst es das OLL (oder die Rückgabe) und das PLL. Schließlich wird eine Zeichenfolge zurückgegeben, die die vollständige Lösung enthält.
Eine Schnittstelle zum Lösen. Erzeugt einen Würfel aus einem eingegebenen Scramble und übergibt ihn ansolve()
Nur um das Leben aller ein wenig einfacher zu machen. Nimmt einen Pfad und lädt die OLLs und PLLs von diesem Pfad. Gibt „true“ zurück, wenn es funktioniert, bereinigt sich selbst und gibt „false“ zurück, wenn es nicht funktioniert.
Da es schwierig ist, in Python Speicher freizugeben, habe ich mich dafür entschieden, dies in C zu tun. Ich habe „sichere“ Versionen jeder Funktion, die Sie möglicherweise außerhalb von C verwenden möchten. Diese speichern die generierten Zeiger, die in einem globalen Array freigegeben werden müssen für die Befreiung auf einmal.
Geben Sie alle gespeicherten Zeichenfolgen frei
Mehrere Funktionen geben Zeichenfolgen zurück, die am Ende freigegeben werden müssen. Diese Funktionen speichern die Zeiger auf diese Zeichenfolgen zum späteren Löschen.
Zum Testen und für diejenigen, die besser mit der Tastatur als mit der Maus umgehen können, eine Terminalschnittstelle.
Es wurde mehrmals neu geschrieben und schließlich beim Schreiben dieser Readme-Datei neu geschrieben, damit es mit der Solver_Library und nicht allein funktioniert.
Es macht alles! Es lädt die OLLs und PLLs basierend auf Standard- oder Benutzereingaben. Anschließend wird auf der Grundlage der Befehlszeile oder der Benutzereingabe ein Würfel generiert, der den Würfel schließlich Schritt für Schritt löst und dabei den Würfel ausdruckt.
Ruft das Verzeichnis aus einem bestimmten Dateipfad ab (z. B. argv[0]). Für die Arbeit mit Windows- und Unix-basierten Betriebssystemen geschrieben.
Ändert einen Farbbuchstaben in den entsprechenden int. In Python habe ich einfach ein Wörterbuch verwendet. Viel einfacher.
Fragen Sie den Benutzer nach den neun Farben der Oberfläche eines Zauberwürfels und tragen Sie diese auf einen Würfel auf.
Schritt-für-Schritt-interaktive Schnittstelle zum Eingeben der Farben in einen Würfel.
Druckt den Hilfetest.
Druckt die Nutzung.
Eine einfache kurze Anwendung zum Testen von Fehlern. Erzeugt viele Rätsel und löst sie. Druckt den ersten Scramble, der fehlschlägt und stoppt.
Ich habe dies verwendet, um einen einzigen Tippfehler zu finden. Alles andere habe ich manuell gemacht.
Verwendet memcmp, um den vom Lösungsalgorithmus ausgegebenen Würfel mit einem von 4 gelösten Würfeln (einem pro Ausrichtung) zu vergleichen.
Tauscht etwas Speicher gegen Geschwindigkeit ein.
Erzeugt zufällige Rätsel um den Zauberwürfel. Schlecht. Schützt nicht vor Doppelbewegungen und Rückwärtsbewegungen. Aber es ist gut genug, wenn das Gerangel lang genug ist.
Lädt die OLLs und PLLs aus Benutzereingaben. Löst den Würfel so oft, wie der Benutzer möchte, bis er fehlschlägt.
Enthält eine Liste aller 58 OLL-Fälle (einschließlich gelöster) zur Verwendung mit meiner C-Bibliothek
Algorithmen aus meiner eigenen Erinnerung und von https://www.speedsolving.com/wiki/index.php/OLL
Enthält eine Liste aller 22 PLL-Fälle (einschließlich gelöster Fälle) zur Verwendung mit meiner C-Bibliothek
Algorithmen aus meiner eigenen Erinnerung und von https://www.speedsolving.com/wiki/index.php/PLL
Enthält einige Muster, um den Würfel auf hübsche Weise zu verschlüsseln. Wird hauptsächlich zum Testen meines Javascripts verwendet, ist aber eine nette Funktion für den Benutzer.
Enthält hoffentlich nichts, soll aber alle Fehler beim Lösen des Würfels speichern.
Die Brücke zwischen meinem C-Code und Javascript. Es lädt die C-Bibliothek und initialisiert die von mir benötigte Funktion mit Argtypes und ggf. Restypes. Verwenden von Numpy zum Konvertieren von Python-Typen in C-Typen.
Eine Funktion, die ich von Stackoverflow „geliehen“ habe. Es nimmt einen Eingabestream und gibt jede Zeile bis # aus
Der eigentliche Löser. Es nimmt einen verschlüsselten Würfel und löst ihn Schritt für Schritt, wobei die Schritte als Array von Wörterbüchern zurückgegeben werden (das ein Array mit Algorithmen enthält). Im Ausnahmefall speichert es das Muster in data/errors.txt
Wird nur zum Testen verwendet. Nimmt einen Scramble-Algorithmus von GET, löst ihn mit getsteps() und übergibt ihn an die Solver-Vorlage.
Nimmt ein Muster und ein Scramble von GET oder POST, löst es mit getsteps und gibt es als JSON zurück.
Nimmt ein Muster von GET oder POST und löst den Würfel, ohne sich um die tatsächliche Lösung zu kümmern, sondern um zu prüfen, ob und wo sie fehlschlägt. Gibt 0 für Erfolg oder die Fehlernummer zurück.
Zeigt die Vorlage „cube.html“ an: die eigentliche Benutzeroberfläche. Dabei übernimmt es auch die Muster aus data/patterns.csv, um sie der Vorlage zu übergeben, und verwendet einen Diktreader und eine Dekommentierung, um meine Kommentare zu entfernen.
Übergibt das Favicon jedoch an den Browser für das schöne Würfelsymbol.
Gibt den gesamten Speicher meines C-Codes frei: die PLLs und die Strings. Registriert mit atexit, um beim Beenden ausgeführt zu werden
Alle Befehle zum Kompilieren meines C-Codes unter Linux oder OS
Enthält die beiden von mir verwendeten Bibliotheken, die separat installiert werden müssen. Einfach Flasche und Numpy. Nichts Besonderes.
Fordert ein Scramble an, übergibt es zur Lösung an app.py und gibt die Lösung aus. Wird nur zum schnellen Testen verwendet.
Enthält viele leere Divs, die durch meinen Javascript-Code gefüllt und durch CSS geformt werden. Enthält 3 Bilder für die Wiedergabesteuerung, ein Formular zur Eingabe von Scrambles und eine kurze Erklärung.
Lädt schließlich das Javascript, da es ohne den Dom nichts ist. Nicht so schön, aber sehr funktional.
Mit Ausnahme von src enthält es den aktuellsten Code. Etwas CSS, damit alles benutzbar aussieht, und Javascript, damit es tatsächlich benutzbar ist. Und einige Bilder zum Anklicken.
Ändert den Link zufälliger Divs in etwas, das Sie sehen können.
Verwendet CSS-Gitter standardmäßig und auf weniger standardmäßige Weise.
Ich verwende einen äußeren Container, um die Steuerelemente links, die Solver-Animation oben rechts und die Lösung unten rechts zu platzieren.
Mithilfe der absoluten Positionierung habe ich die Wiedergabesteuerung und den aktuellen Alg über die Animation gelegt.
Bis auf den Farbwähler ist alles andere Standard. Wenn Sie sich den HTML-Code ansehen, sehen Sie nur ein Div mit 6 Divs, von denen jedes 9 Divs enthält. Mithilfe von Gittern forme ich die 6 „Gesichts“-Divs so, dass sie wie ein aufgeklappter Würfel aussehen. Jede Seite enthält ein Gitter aus 9 Quadraten. Dadurch kann ich die Größe variabel gestalten, während die Form gleich bleibt, ohne dass ich Bilder verwenden muss.
Ich habe auch ein oder zwei Wochen damit verbracht, eine mobile Ansicht zu erstellen, da der Text anscheinend eine variable Größe hatte. Ich habe Discord, Facebook und sogar Stackoverflow kontaktiert. Schließlich habe ich auf stackoverflow eine HTML-Zeile gefunden: die alles in einer Zeile gelöst hat.
Bringt meinen Algorithmus zum Benutzer. Basierend auf three.js
Dies ist das erste Mal, dass ich tatsächlich Javascript schreibe. Es sieht nicht schön aus, verstößt wahrscheinlich gegen alle Regeln, aber es funktioniert.
Ich habe diese Datei mehrmals umgeschrieben. Zuerst iterativ, das war, bevor ich mit dem Hochladen auf Github begann, dann funktional und schließlich irgendwie objektorientiert
Zuerst deklariere ich alle Variablen, die von mehr als einer Funktion verwendet werden müssen: Die Materialien und die Geometrie für die Three.js-Objekte, Arrays zum Speichern der Objekte, die Rotationsachsen und mehr.
Es gibt auch ein riesiges Wörterbuch, das die notwendigen Variablen für jede legale Würfelbewegung enthält: Die Würfel, die um- oder ausgetauscht werden sollen, die Achse und die Rotationsgrade und was angezeigt werden soll. Das hätte alles in 72 Zeilen passen können, aber style50 möchte eine Variable pro Zeile, was 649 Zeilen ergibt.
Nimmt die Szene und speichert eine Referenz, generiert die Würfel, Umrisse und Ebenen in einer Schleife, legt die Positionen der Würfel und Umrisse in einer Schleife fest und legt die Ebenen manuell fest.
Hilfsfunktion. Wechselt die Rotationen von 4 Würfeln a > b > c > d > a mithilfe eines temporären Würfels.
Hilfsfunktion. Vertauscht die Rotation von 2 Würfeln mit einem temporären Würfel.
Setzt die Würfel auf ihre Standardposition zurück und setzt die Lösungsteilung zurück. Dafür sind mehr Schritte erforderlich, als Sie denken. Zuerst müssen wir alle Animationen zurücksetzen und beenden. Setzen Sie alle zukünftigen und vergangenen Bewegungen zurück und setzen Sie schließlich die Drehung der Würfel zurück.
Gibt „true“ oder „false“ zurück, je nachdem, ob sich der Würfel bewegt.
Richtet die Animation für eine Bewegung auf dem Würfel ein.
Diese Funktion bestand ursprünglich aus 160 Zeilen mit if/else-Anweisungen, aber ich habe sie später umgeschrieben, um die Würfel und Züge aus einem riesigen Wörterbuch zu erhalten. Dadurch sind diese und die nächste Funktion leichter lesbar und wenn ich eine 4x4- oder 5x5-Version schreibe, muss ich nur das Wörterbuch ändern.
Vervollständigt die Animation und bewegt (dreht) tatsächlich die Würfel.
Da sich die Würfel nicht wirklich bewegen, besteht die Grundidee darin, die Drehung des Würfels zu kopieren, der sich bei einem echten Würfel an seinen Platz bewegen würde, und dann alles zu drehen.
Diese Funktion war ursprünglich eine 375-zeilige Monstrosität aneinandergereihter if/else-Anweisungen. Es wurde später umgeschrieben, um mit dem Bewegungswörterbuch zu arbeiten.
Führt den nächsten Zug in der Zugliste oder den nächsten Algorithmus in der Schrittliste aus und zeigt den aktuellen Algorithmus auf dem Bildschirm an.
Führt den vorherigen Zug in der Zugliste in umgekehrter Reihenfolge aus, oder den vorherigen Algorithmus in der Schrittliste
Wendet die Farben aus dem Farbwähler (oder eigentlich dem Flächenarray) auf den Würfel an, indem jeder Würfel gedreht wird. Enthält einige eigene Funktionen. Jeder Cubie verfügt über eine eigene Zeile mit manuellen Modifikatoren. Für mich ist es zu zufällig, um es zu automatisieren. Aber wenn Sie es versuchen wollen, seien Sie mein Gast.
Ändert Grün zu Blau, Rot zu Orange, Weiß zu Gelb und umgekehrt. Jetzt denke ich darüber nach, das hätte ein Diktat sein können, aber eine Zeile Mathematik und drei Wenns funktionieren auch.
Dreht einen Würfel basierend auf der vorderen und linken Farbe oder der oberen Farbe. Grundsätzlich viele if-Anweisungen mit fest codierten Bewegungen, aber das reicht hier aus.