Mon projet final pour CS50 est un solveur Rubik's Cube « méthode humaine ».
Cela signifie que le Rubik's Cube saisi est résolu en utilisant une méthode qu'un humain pourrait utiliser (CFOP), avec des étapes claires : Cross, F2L, OLL et PLL.
Le produit final se compose d'une application shell, d'une bibliothèque C à utiliser dans d'autres projets et d'une interface Web écrite en Flask, HTML, CSS et Javascript.
J'ai décidé de créer un solveur Rubik's Cube parce que j'ai déjà créé un solveur Sudoku en C++, et le cube était un pas en avant.
Au lieu d'un plan 2D, j'ai un plan semi-3D, avec des bords et des coins.
Parce que les solveurs d'IA ont été créés et que je n'ai pas suffisamment confiance en mes capacités d'écriture d'IA, j'ai décidé de faire en sorte que le programme résolve le cube comme je le fais :
Croix, F2L, OLL et PLL
Téléchargez l'exécutable bin/solver ou construisez à partir des sources en utilisant make solver
Téléchargez ou clonez les fichiers de ce référentiel et construisez à l'aide de make solver
Téléchargez ou clonez les fichiers à l'aide de ce référentiel et compilez tous les fichiers .c dans src à l'exception de solver_library
Exécutez le solveur avec l'une des commandes suivantes :
./solver "Algorithm"
./solver [Up] [Front] [Right] [Back] [Left] [Down]
Remplacer l'algorithme par un algorithme de brouillage (par exemple U R2 FBR B2 R U2 L B2 RU' D' R2 FR' L B2 U2 F2
) ou les faces avec les couleurs sur cette face (par exemple wbwowrwgw gwgogrgyg rwrgrbryr bwbrbobyb owobogoyo ygyoyryby
).
Les couleurs possibles sont les premiers caractères Vert, Rouge, Bleu, Orange, Blanc et Jaune.
Si les fichiers olls.txt et plls.txt ne se trouvent pas dans le même dossier que le binaire, utilisez les options -o et -p, ou -d pour pointer vers les fichiers.
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"
Ajoutez l'option -t
pour imprimer uniquement le texte sans images du cube.
Téléchargez ou clonez les fichiers de ce référentiel.
Installez Flask et Numpy à l'aide de pip.
python3 -m pip -r requirements.txt
Utilisez soit la commande flask run
, soit python3 app.py
pour exécuter le serveur Web.
Accédez à https://127.0.0.1:5000/ pour utiliser le solveur.
Téléchargez ou clonez les fichiers de ce référentiel.
Supprimez la version Linux de bin/libcubesolver.so manuellement ou en utilisant make clean
et recompilez en utilisant make library
.
Installez Flask et Numpy à l'aide de pip :
python3 -m pip -r requirements.txt
Utilisez soit la commande flask run
, soit python3 app.py
pour exécuter le serveur Web.
Accédez à https://127.0.0.1:5000/ pour utiliser le solveur.
Téléchargez ou clonez les fichiers de ce référentiel. Supprimez manuellement la version Linux de bin/libcubesolver.so.
Compilez tous les fichiers .c dans src à l'exception de solver.c dans bin/libcubesolver.so. Ou si vous utilisez un nom différent, modifiez la ligne 19 dans app.py pour refléter cela.
Installez Flask et Numpy à l'aide de pip :
python3 -m pip -r requirements.txt
Utilisez soit la commande flask run
, soit python3 app.py
pour exécuter le serveur Web.
Accédez à https://127.0.0.1:5000/ pour utiliser le solveur.
Compilez tous les fichiers de src à l'exception de solver.c vers libcubesolver.so, libcubesolver.dylib ou libcubesolver.dll et enregistrez-le partout où les bibliothèques sont enregistrées sur votre PC.
Sous Linux, vous pouvez utiliser make library
Copiez libcubesolver.h de src vers l'endroit où les en-têtes sont enregistrés sur votre PC (ex. /usr/include/)
Créez un lien avec -lcubesolver
ou compilez simplement dans l'application comme s'il s'agissait d'un fichier .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 );
Voir la version C pour voir ce que fait chaque fonction.
Parce qu'il est difficile de libérer de la mémoire mal allouée en python, utilisez des versions "sûres" des fonctions de résolution et utilisez free_strings() après utilisation.
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 veut que j'explique ce que fait chaque fichier et ce qu'il contient. Ce fichier Lisez-moi est donc plus long que tous mes fichiers sauf trois.
├── app.py ├── poubelle │ ├── libcubesolver.so │ └── solveur ├── données │ ├── erreurs.txt │ ├── olls.csv │ ├── modèles.csv │ └── plls.csv ├── fichier make ├── LISEZMOI.md ├── exigences.txt ├──src │ ├── croix.c │ ├── croix.h │ ├── cube.c │ ├── cube.h │ ├── cubesolver.h │ ├── f2l.c │ ├── f2l.h │ ├── lastlayer.c │ ├── lastlayer.h │ ├── solveur.c │ ├── solver_library.c │ ├── utils.c │ └── utils.h ├── essai │ ├── tests aléatoires.c │ └── tests aléatoires ├── statique │ ├── cube.css │ ├── cube.js │ ├── cubeinterface.js │ ├── favicon.ico │ ├── suivant.png │ ├── pause.png │ ├── jouer.png │ └── précédent.png ├── modèles │ ├── cube.html │ └── solveur.html
Cube.c et cube.h contiennent tout le code qui contrôle le rubik's cube en mémoire.
Un rubik's cube, en mémoire, est un tableau 2D de 6 x 9. Le tableau extérieur contient les six faces : Avant, Droite, Arrière, Gauche, Haut et Bas.
J'ai choisi cet ordre car les faces haut et bas sont toujours les mêmes, donc je peux les coder en dur, mais les côtés doivent seulement être corrects les uns par rapport aux autres. Cela signifie que je peux utiliser un simple plus ou moins, combiné avec un module pour traduire les tests et les mouvements de tous les côtés. Il en va de même pour les couleurs, qui sont enregistrées sous les numéros 0 à 5, 0 étant la couleur du devant, 1 étant la couleur correcte, etc.
J'ai d'abord choisi l'ordre des couleurs que j'utilise : rouge devant, jaune en haut, vert à droite. Mais plus tard, lors de l'écriture de mon code python et de mon interface, je suis passé au vert "Officiel" devant, blanc en haut donc je n'ai pas besoin de faire pivoter le cube entré par l'utilisateur pour avoir du blanc en bas.
J’ai d’abord créé la fonction color_cube(). Cela applique un motif de votre choix au cube en utilisant 6 cordes.
Il utilise des mathématiques simples pour changer les caractères '0', '1', etc. en entiers et les stocke dans le tableau donné.
Je ne l'utilise qu'une ou deux fois comme une seule ligne pour configurer un cube résolu sur une seule ligne, car lors de la traduction de l'entrée de l'utilisateur en quelque chose que l'ordinateur comprend, vous pourriez aussi bien saisir les entiers individuellement au lieu de les traduire d'abord en chaîne.
Print_cube() génère le cube fourni sur le terminal. Ceci est beaucoup utilisé lors du débogage et pour l’application terminal. J'ai commencé par afficher uniquement les nombres de 0 à 5 sous la forme d'un rubik's cube explosé.
444
444
444
333 000 111 222
333 000 111 222
333 000 111 222
555
555
555
Parce que cela créerait plus de lignes de code pour automatiser cela, j'utilise simplement 9 instructions d'impression contenant chacune jusqu'à 12 variables.
Plus tard, j'ai découvert que je pouvais ajouter des codes shell comme e[42m
pour donner à ces nombres une couleur d'arrière-plan. En les ajoutant, j'ai obtenu 28 variables par impression.
Finalement, j'ai pensé à simplement mettre la lettre ou le chiffre de la couleur dans le tableau contenant les codes shell, en le ramenant à 12. Malheureusement, Windows ne prend pas en charge ce type de codes shell, j'ai donc dû continuer à utiliser uniquement des chiffres, et lettres ultérieures.
Move_cube() est la première fonction permettant de réellement modifier le cube. Il s'agit d'une fonction récursive qui effectue le déplacement demandé le nombre de fois demandé. Il y a 9 mouvements possibles, numérotés de 0 à 8. Les couches avant, droite, arrière, etc. par défaut. Mais aussi les couches du milieu, de l'équateur et debout. À l'aide d'une fonction d'assistance que j'ai écrite dans utils.c appelée Cycle, je change les valeurs de chaque carré sur chaque face correspondant au mouvement dans le sens des aiguilles d'une montre, en répétant si nécessaire.
Parce que les mouvements ne se produisent généralement pas d'eux-mêmes, j'ai ensuite écrit run_algorithm(). Cette fonction utilise move_cube() pour exécuter un algorithme sur le cube fourni en notation de cube standard. Cela me donne la possibilité d'effectuer plusieurs mouvements à la fois (ou dans l'ordre) et d'effectuer des mouvements qui ont un effet sur 2 ou même 3 calques comme x, y et z.
Enfin, validez(). J'ai ajouté cette fonction lors de l'écriture de l'interface Python car vous ne devriez jamais faire confiance à votre utilisateur pour ne pas saisir des choses étranges.
Cette fonction ne vérifie pas si le rubik's cube est résoluble. Il vérifie si les couleurs sont correctes. Par exemple, un cube ne peut pas avoir à la fois des autocollants blancs et jaunes. Et dans le sens des aiguilles d'une montre, il est possible d'avoir des autocollants verts, blancs et rouges dans cet ordre, mais pas l'inverse.
Pour ce faire, j'ai partiellement copié l'idée du tableau verrouillé dans Tideman, en utilisant un tableau booléen 2D pour les coins et un tableau booléen 3D pour les bords pour voir si une combinaison de 2 ou 3 couleurs est possible. Ce code a subi plusieurs itérations. Vérifiez d’abord chaque bord et coin séparément, puis utilisez quelques boucles for.
Cross.c contient tout le code nécessaire pour résoudre la croix du cube. Parce que j'utilise une "méthode humaine" (CFOP) pour résoudre le cube, je suis en quelque sorte obligé d'utiliser d'énormes arbres de décision.
J'ai réécrit presque tout ce fichier à la dernière minute pour ne pas avoir à aligner les bords à chaque étape du processus.
Recherche l'endroit où se trouve le bord jaune-vert sur la couche inférieure, ou se trouverait s'il n'y était pas encore. Cela facilite l'alignement des autres bords en moins d'étapes, car ils peuvent s'aligner sur le "zéro relatif" au lieu du zéro réel.
Ces 6 cas représentent toutes les manières dont un bord peut être orienté, pointant vers le bas, pointant vers le haut, pointant dans les calques haut et bas, pointant vers la gauche et pointant vers la droite sur le calque E.
Les fonctions scannent essentiellement les faces dans l'ordre de 0 à 3 pour rechercher les pièces qui appartiennent à la croix, les font pivoter et stockent les mouvements dans un tableau de caractères à l'aide d'une fonction d'assistance que j'appelle append().
Solve_cross oriente d'abord le cube pour que le jaune soit vers le bas. Ensuite, il fait les 6 cas croisés dans l'ordre. Si l'on renvoie autre chose que null, cela revient au premier cas.
Une fois les 4 arêtes résolues, la fonction renvoie les algorithmes. Si tous les cas sont testés sans succès, ou après 4 boucles la fonction renvoie NULL.
La même idée que cross.c, mais en bien plus grand. 3600 lignes de code pour résoudre tous les cas.
Ces 11 cas comprennent toutes les manières dont un coin peut être orienté, ainsi que certaines combinaisons de coins et de bords.
Pour gagner du temps et des lignes, la plupart des fonctions ne résolvent pas complètement une combinaison coin/bord, mais les amènent à l'état connu le plus court et appellent la fonction correcte.
Les chiffres de 1 à 11 correspondent à l'ordre dans lequel ils ont été écrits, pas toujours à l'ordre dans lequel ils sont exécutés.
Solve_f2l vérifie d'abord que la croix est résolue. Sinon, il renvoie null.
Après cela, solve_f2l parcourt les cas dans l'ordre de l'algorithme moyen le plus court au plus long, en revenant à 1 une fois qu'une fonction renvoie un algorithme. Si tous les cas sont testés sans succès, ou après 4 boucles, la fonction renvoie NULL.
Le dossier dont je suis le plus fier. Pas de boucles géantes, pas d'arbres de décision. Juste des mathématiques intelligentes et des tables de recherche.
Oll et pll sont deux structures destinées à stocker les OLL et les PLL. Ils stockent le nom, le modèle que j'utilise pour les reconnaître et l'algorithme pour les résoudre.
Lors d'une nuit blanche, j'ai compris que je pouvais reconnaître la PLL en utilisant uniquement les couleurs de l'anneau extérieur, et l'OLL par seulement 12 booléens signifiant jaune ou pas jaune.
Load_olls() et load_plls() chargent les noms, modèles et algorithmes à partir d'un fichier CSV. Cela a connu quelques itérations.
J'ai d'abord commencé par getline() pour obtenir chaque ligne des fichiers CSV et en divisant la chaîne en 24 caractères et 36 caractères, en complétant le nom avec des espaces.
Ce n'était pas joli, alors j'ai commencé à chercher les virgules et à diviser les choses à cet endroit.
Finalement, quelqu'un sur Discord m'a dirigé vers sscanf(), me permettant de diviser facilement et proprement chaque chaîne.
Je suis également passé à fgets() pour rendre la ligne plus compatible entre elles.
Les lignes commençant par # sont rejetées car ce sont des commentaires. Les lignes dont le motif n'est pas purement numérique sont reconnues à l'aide d'une fonction d'assistance appelée isNumber() ou isBinary() et sont rejetées. Lors de l'écriture de cette section, j'ai ajouté un test pour voir si le nom, le motif et l'algorithme ont la bonne longueur. arrêter les lectures et écritures hors limites.
Dans le tableau olls et plls, l'espace pour les noms et les algorithmes est créé à l'aide de malloc et les informations sont copiées à l'aide de strncpy. Le modèle est transformé en un tableau de 12 entiers ou booléens.
Find_pll() est ma fierté et ma joie. C'était le premier algorithme que j'ai écrit pour résoudre le cube (avant le cross et le f2l), et il m'est venu dans un rêve. Une façon de vérifier le motif oll pour n'importe quelle combinaison de couleurs avec quelques calculs simples. Je me suis littéralement réveillé et j'ai noté int case[12] containing the outer ring. if (front[0] == (front[1]-(case[0]-case[1])%4))
. Le lendemain, je l'ai simplifié en (faces[0][0] == (faces[0][1] - (plls[i].pattern[0] - plls[i].pattern[1])) % 4)
(fois 12). Puis (faces[j][0] - faces[j][1]) % 4 == (plls[i].pattern[0] - plls[i].pattern[1]) % 4
et enfin après de nombreuses itérations Je me suis retrouvé avec mod((cube[(j + i / 3) % 4][i % 3] + dif[j]), 4)
me permettant d'encoder le motif de la couche supérieure du cube en utilisant un 12 int tableau, pour une comparaison facile avec toutes les valeurs connues.
J'aurais pu encoder le modèle sous la forme d'un entier de 4 octets, mais j'ai décidé de ne pas le faire car je pense qu'il est préférable de rendre les modèles faciles à comprendre plutôt que d'économiser 44 octets (3 408 Ko au total pour les modèles)
Enfin, la fonction renvoie un pair_int_int contenant l'orientation du calque et l'OLL ou PLL correct. Ou deux nombres négatifs si rien n'est trouvé.
Les fonctions solve_oll() et solve_pll() sont relativement simples. Ils appellent find_oll ou pll pour obtenir le bon index et la bonne orientation (nombre de y mouvements). Ensuite, la fonction fait tourner le cube en utilisant un mouvement y, exécute le bon algorithme OLL ou PLL et, dans le cas de solve_pll(), tourne la couche U pour finalement résoudre le cube. Les fonctions renvoient l'algorithme utilisé pour résoudre le cube en notation cubique standard.
Libère la mémoire utilisée pour stocker toutes les OLL et PLL.
Quelques fonctions pour me faciliter un peu la vie.
Parce que le % normal ne fonctionne pas avec des nombres négatifs, c'est « ma propre version » qui le fait. Avec un merci spécial au discord cs50.
Pour certaines fonctions, je souhaite renvoyer plusieurs variables. Celui-ci est basé sur la paire C++
Une fonction qui cycle 4 nombres. Fondamentalement, une avancée par rapport au swap. Utilisé dans cube.c pour faire défiler les bords et les coins pour les mouvements de cube. Il y a une copie de ce mouvement dans cube.js
Une fonction qui ajoute une chaîne à une autre chaîne, en réaffectant si nécessaire. Utilisé partout dans cette application.
Vérifie si une chaîne contient uniquement des chiffres ou uniquement des zéros et des uns. Actuellement utilisé uniquement pour charger des OLL et des PLL.
J'ai d'abord écrit solver.c comme interface pour mon solveur de cube, mais j'ai rapidement décidé que je voulais une interface utilisateur graphique, ce que je ne suis pas prêt à faire en C pur.
C'est pourquoi j'ai commencé à transformer mes cubessolvers en une bibliothèque à utiliser avec tout ce que je veux.
Rassemble tout cela pour résoudre le cube. Il prend un cube et le valide. Tente ensuite de résoudre la croix. S'il échoue, il renvoie une erreur, sinon il stocke la sortie. Ensuite, il résout la croix ou renvoie une erreur. Il résout ensuite l'OLL (ou les retours) et la PLL. Enfin, il renvoie une chaîne contenant la solution complète.
Une interface pour résoudre. Génère un cube à partir d'un brouillage saisi et le transmet à solve()
Juste pour rendre la vie de chacun un peu plus facile. Prend un chemin et charge les olls et plls à partir de ce chemin. Renvoie vrai si cela fonctionne, nettoie après lui-même et renvoie faux si ce n'est pas le cas.
Parce qu'il est difficile de libérer de la mémoire depuis Python, j'ai décidé de le faire depuis C. J'ai des versions "sûres" de chaque fonction que vous pourriez vouloir utiliser en dehors de C. Celles-ci stockent les pointeurs générés qui doivent être libérés vers un tableau global. pour libérer tout d'un coup.
Libérez tous les fichiers stockés
Plusieurs fonctions renvoient des chaînes qui doivent finalement être libérées. Ces fonctions stockent les pointeurs vers ces chaînes pour une suppression ultérieure.
Pour les tests, et pour ceux qui sont meilleurs au clavier qu'à la souris, une interface terminal.
Il a fait l'objet de plusieurs réécritures, pour finalement être réécrit lors de l'écriture de ce fichier Lisez-moi pour fonctionner avec solver_library plutôt que seul.
Ça fait tout ! Il charge les OLL et les PLL en fonction des entrées par défaut ou de l'utilisateur. Ensuite, il génère un cube basé sur la ligne de commande ou sur la saisie de l'utilisateur, et résout enfin le cube étape par étape, en imprimant le cube en cours de route.
Obtient le répertoire à partir d'un chemin de fichier donné (par exemple argv[0]). Écrit pour fonctionner avec les systèmes d'exploitation Windows et Unix.
Change une lettre de couleur en int correspondant. En python, je viens d'utiliser un dictionnaire. Beaucoup plus facile.
Demandez à l'utilisateur les neuf couleurs de la face d'un rubik's cube et placez-les sur un cube.
Interface interactive étape par étape pour saisir les couleurs sur un cube.
Imprime le test d'aide.
Imprime l'utilisation.
Une courte application simple pour tester les bogues. Génère beaucoup de brouillages et les résout. Imprime le premier brouillage qui échoue et s'arrête.
J'ai utilisé ceci pour trouver une seule faute de frappe. Tout le reste, je l'ai fait manuellement.
Utilise memcmp pour comparer le cube craché par l'algorithme de résolution avec l'un des 4 cubes résolus (un par orientation).
Échange un peu de mémoire contre de la vitesse.
Génère des brouillages aléatoires pour le rubik's cube. Gravement. Ne protège pas contre les doubles mouvements et les mouvements inverses. Mais c'est suffisant si la bousculade est suffisamment longue.
Charge les OLL et les PLL à partir de l'entrée utilisateur. Résout le cube autant de fois que l'utilisateur le souhaite, jusqu'à ce qu'il échoue.
Contient une liste des 58 cas OLL (y compris résolus) à utiliser avec ma bibliothèque C
Algorithmes de ma propre mémoire et de https://www.speedsolving.com/wiki/index.php/OLL
Contient une liste des 22 cas PLL (y compris résolus) à utiliser avec ma bibliothèque C
Algorithmes de ma propre mémoire et de https://www.speedsolving.com/wiki/index.php/PLL
Contient quelques modèles pour brouiller le cube de jolies manières. Principalement utilisé pour tester mon javascript, mais c'est une fonctionnalité intéressante pour l'utilisateur.
Espérons qu'il ne contienne rien, mais est destiné à stocker toutes les erreurs lors de la résolution du cube.
Le pont entre mon code C et javascript. Il charge la bibliothèque C et initialise la fonction dont j'ai besoin avec des argtypes et si nécessaire des restypes. Utiliser numpy pour convertir des types python en ctypes.
Une fonction que j'ai "empruntée" à stackoverflow. Il prend un flux d'entrée et génère chaque ligne jusqu'à #
Le véritable solveur. Il prend un cube brouillé et le résout étape par étape, renvoyant les étapes sous forme d'un tableau de dictionnaires (contenant un tableau d'algorithmes). En cas d'exceptions, il stocke le modèle dans data/errors.txt
Utilisé uniquement pour les tests. Prend un algorithme de brouillage de GET, le résout à l'aide de getsteps() et le transmet au modèle de solveur.
Prend un modèle et le brouille à partir de GET ou POST, le résout à l'aide de getsteps et le renvoie au format JSON.
Prend un modèle de GET ou POST et résout le cube, sans se soucier de la solution réelle, mais en vérifiant si et où elle échoue. Renvoie 0 en cas de succès ou le numéro d'erreur.
Affiche le modèle cube.html : l'interface utilisateur réelle. Ce faisant, il prend également les modèles de data/patterns.csv pour les transmettre au modèle, en utilisant un lecteur de dictée et un décommentateur pour supprimer mes commentaires.
Transmet cependant le favicon au navigateur pour le joli symbole de cube.
Libère toute la mémoire de mon code C : les PLL et les chaînes. Enregistré en utilisant atexit pour s'exécuter à la sortie
Toutes les commandes pour compiler mon code C sous Linux ou OS X. Même si ma bibliothèque peut être utilisée comme bibliothèque, j'ai décidé d'utiliser tous les fichiers à compiler afin de pouvoir simplement distribuer un seul exécutable.
Contient les deux bibliothèques que j'ai utilisées et qui doivent être installées séparément. Juste un flacon et un numpy. Rien de spécial.
Demande un brouillage, le transmet à app.py pour résoudre et imprime la solution. Utilisé uniquement pour des tests rapides.
Contient de nombreux divs vides à remplir par mon code javascript et façonnés par CSS. Contient 3 images pour les commandes de lecture, un formulaire pour saisir les brouillages et une brève explication.
Charge enfin le javascript car ce n'est rien sans le dom. Pas très joli, mais très fonctionnel.
Sauf que src contient le code le plus actuel. Du CSS pour que tout semble utilisable et du javascript pour le rendre réellement utilisable. Et quelques images sur lesquelles cliquer.
Modifie le lien des divs aléatoires en quelque chose que vous pouvez voir.
Utilise les grilles CSS par défaut, et moins par défaut.
J'utilise un conteneur externe pour placer les contrôles à gauche, l'animation du solveur en haut à droite et la solution en bas à droite.
En utilisant le positionnement absolu, j'ai placé les commandes de lecture et l'alg actuel sur l'animation.
Tout le reste est tout à fait par défaut, à l'exception du sélecteur de couleurs. Si vous regardez le code HTML, vous voyez simplement un div contenant 6 divs, chacun contenant 9 divs. À l'aide de grilles, je façonne les 6 divs "face" pour qu'ils ressemblent à un cube déplié, chaque face contient une grille de 9 carrés. Cela me permet de rendre la taille variable tandis que la forme reste la même, sans avoir besoin d'utiliser d'images.
J'ai également passé une semaine ou deux à créer une vue mobile, car le texte semblait être de taille variable. J'ai contacté Discord, Facebook et même Stackoverflow. Finalement, j'ai trouvé une ligne html sur stackoverflow : qui a tout résolu en une seule ligne.
Apporte mon algorithme à l'utilisateur. Basé sur three.js
C'est la première fois que j'écris du javascript. Cela n'a pas l'air joli, cela enfreint probablement toutes les règles, mais cela fonctionne.
J'ai réécrit ce fichier plusieurs fois. D'abord itératif, c'était avant que je commence à télécharger sur github, puis fonctionnel, et enfin en quelque sorte orienté objet
Tout d'abord, je déclare toutes les variables qui doivent être utilisées par plusieurs fonctions : les matériaux et la géométrie des objets three.js, les tableaux pour stocker les objets, les axes de rotation, etc.
Il existe également un gigantesque dictionnaire contenant les variables nécessaires pour chaque déplacement légal de cube : les cubes à parcourir ou à échanger, l'axe et les degrés de rotation, et ce qu'il faut afficher. Tout cela aurait pu tenir dans 72 lignes, mais style50 veut une variable par ligne, ce qui fait 649 lignes.
Prend la scène et enregistre une référence, génère les cubes, les contours et les plans dans une boucle, définit les positions des cubes et des contours dans une boucle et les plans manuellement.
Fonction d'assistance. Effectue des cycles de rotations de 4 cubes a > b > c > d > a à l'aide d'un cube temporaire.
Fonction d'assistance. Échange la rotation de 2 cubes à l'aide d'un cube temporaire.
Réinitialisez les cubes à leur position par défaut et réinitialisez la solution div. Cela nécessite plus d’étapes que vous ne le pensez. Nous devons d’abord réinitialiser toutes les animations, les terminer. Réinitialisez tous les mouvements futurs et passés, et enfin réinitialisez la rotation des cubes.
Renvoie vrai ou faux selon que le cube bouge.
Configure l'animation pour un déplacement sur le cube.
Cette fonction était à l'origine composée de 160 lignes d'instructions if/else, mais je l'ai réécrite plus tard pour obtenir les cubes et les mouvements d'un gigantesque dictionnaire. Cela rend cette fonction et la suivante plus faciles à lire, et fait en sorte que si j'écris une version 4x4 ou 5x5, je n'ai qu'à changer le dictionnaire.
Termine l'animation et déplace (fait pivoter) les cubes.
Parce que les cubes ne bougent pas vraiment, l'idée de base est de copier la rotation du cube qui, sur un vrai cube, se déplacerait à sa place, puis de tout faire pivoter.
Cette fonction était à l'origine une monstruosité de 375 lignes d'instructions if/else suivantes. Il a ensuite été réécrit pour fonctionner avec le dictionnaire des mouvements.
Effectue le mouvement suivant dans la liste des mouvements, ou l'algorithme suivant dans la liste des étapes, et affiche l'algorithme actuel à l'écran.
Le mouvement précédent dans la liste des mouvements est-il inversé, ou l'algorithme précédent dans la liste des étapes
Applique les couleurs du sélecteur de couleurs (ou en fait du tableau de faces) au cube en faisant pivoter chaque cube. Contient certaines de ses propres fonctions. Chaque cubie a sa propre ligne avec des modificateurs manuels. Pour moi, c'est trop aléatoire pour être automatisé. Mais si vous voulez essayer, soyez mon invité.
Change le vert en bleu, le rouge en orange, le blanc en jaune et inversement. Maintenant, j'y pense, cela aurait pu être un dicté, mais une ligne mathématique et 3 si fonctionnent aussi.
Fait pivoter un cube en fonction de la couleur avant et gauche, ou de la couleur du haut. Fondamentalement, beaucoup d'instructions if avec des mouvements codés en dur, mais cela suffit ici.