Mi proyecto final para CS50 es un solucionador de cubos de Rubik de "método humano".
Esto significa que el Cubo de Rubik ingresado se resuelve utilizando un método que un humano podría usar (CFOP), con pasos claros: Cross, F2L, OLL y PLL.
El producto final consta de una aplicación shell, una biblioteca C para usar en otros proyectos y una interfaz web escrita en Flask, HTML, CSS y Javascript.
Decidí crear un solucionador de cubos de Rubik porque ya había hecho un solucionador de Sudoku en C++ y el cubo era un paso adelante.
En lugar de un plano 2D, tengo un plano semi-3D, con bordes y esquinas.
Debido a que se han creado solucionadores de IA y no tengo suficiente confianza en mis habilidades de escritura de IA, decidí hacer que el programa resuelva el cubo de la manera que lo hago yo:
Cruz, F2L, OLL y PLL
Descargue el ejecutable bin/solver o compílelo desde el código fuente usando make solver
Descargue o clone los archivos de este repositorio y compílelos usando make solver
Descargue o clone los archivos usando este repositorio y compile todos los archivos .c en src excepto solver_library
Ejecute el solucionador con uno de los siguientes comandos:
./solver "Algorithm"
./solver [Up] [Front] [Right] [Back] [Left] [Down]
Reemplazar el algoritmo con un algoritmo de codificación (por ejemplo, U R2 FBR B2 R U2 L B2 RU' D' R2 FR' L B2 U2 F2
) o las caras con los colores de esa cara (por ejemplo, wbwowrwgw gwgogrgyg rwrgrbryr bwbrbobyb owobogoyo ygyoyryby
).
Los colores posibles son los primeros caracteres de Verde, Rojo, Azul, Naranja, Blanco y Amarillo.
Si los archivos olls.txt y plls.txt no están en la misma carpeta que el binario, use las opciones -o y -p, o -d para señalar los archivos
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"
Agregue la opción -t
para imprimir solo el texto sin imágenes del cubo.
Descargue o clone los archivos de este repositorio.
Instale Flask y Numpy usando pip.
python3 -m pip -r requirements.txt
Utilice el comando flask run
o python3 app.py
para ejecutar el servidor web.
Vaya a https://127.0.0.1:5000/ para usar el solucionador.
Descargue o clone los archivos de este repositorio.
Elimine la versión de Linux de bin/libcubessolver.so manualmente o usando make clean
y vuelva a compilar usando make library
.
Instale Flask y Numpy usando pip:
python3 -m pip -r requirements.txt
Utilice el comando flask run
o python3 app.py
para ejecutar el servidor web.
Vaya a https://127.0.0.1:5000/ para usar el solucionador.
Descargue o clone los archivos de este repositorio. Elimine la versión de Linux de bin/libcubessolver.so manualmente.
Compile todos los archivos .c en src excepto solver.c en bin/libcubesolver.so. O si usa un nombre diferente, cambie la línea 19 en app.py para reflejarlo.
Instale Flask y Numpy usando pip:
python3 -m pip -r requirements.txt
Utilice el comando flask run
o python3 app.py
para ejecutar el servidor web.
Vaya a https://127.0.0.1:5000/ para usar el solucionador.
Compile todos los archivos en src, excepto solver.c, en libcubesolver.so, libcubesolver.dylib o libcubesolver.dll y guárdelos dondequiera que estén guardadas las bibliotecas en su PC.
En Linux puedes usar make library
Copie libcubesolver.h desde src a cualquier lugar donde se guarden los encabezados en su PC (por ejemplo, /usr/include/)
Vincule con -lcubesolver
o simplemente compílelo en la aplicación como si fuera un archivo .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 );
Consulte la versión C para ver qué hace cada función.
Debido a que es difícil liberar memoria mal asignada en Python, use versiones "seguras" de las funciones de resolución y use free_strings() después de su uso.
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 quiere que le explique qué hace cada archivo y qué contiene. Entonces, este archivo Léame es más largo que todos mis archivos excepto 3.
├── aplicación.py ├── papelera │ ├── libcubesolver.so │ └── solucionador ├── datos │ ├── errores.txt │ ├── olls.csv │ ├── patrones.csv │ └── plls.csv ├── archivo MAKE ├── LÉAME.md ├── requisitos.txt ├── src │ ├── cruz.c │ ├── cruz.h │ ├── cubo.c │ ├── cubo.h │ ├── cubosolver.h │ ├── f2l.c │ ├── f2l.h │ ├── última capa.c │ ├── última capa.h │ ├── solucionador.c │ ├── solver_library.c │ ├── utils.c │ └── utilidades.h ├── prueba │ ├── pruebas aleatorias.c │ └── pruebas aleatorias ├── estático │ ├── cubo.css │ ├── cubo.js │ ├── interfazcube.js │ ├── favicon.ico │ ├── siguiente.png │ ├── pausa.png │ ├── jugar.png │ └── anterior.png ├── plantillas │ ├── cubo.html │ └── solucionador.html
Cube.c y cube.h contienen todo el código que controla el cubo de Rubik en la memoria.
Un cubo de Rubik, en la memoria, es una matriz 2D de 6 por 9. La matriz exterior contiene las seis caras: Frente, Derecha, Atrás, Izquierda, Arriba y Abajo.
Elegí este orden porque las caras arriba y abajo son siempre las mismas, por lo que puedo codificarlas, pero los lados solo necesitan ser correctos entre sí. Esto significa que puedo usar más o menos simples, combinados con módulos para traducir las pruebas y los movimientos a todos los lados. Lo mismo ocurre con los colores, que se guardan como números del 0 al 5, siendo 0 el color frontal, 1 el derecho, etc.
Primero elegí el orden de los colores que uso: rojo al frente, amarillo arriba, verde a la derecha. Pero más tarde, cuando escribí mi código Python y mi interfaz, cambié al verde "Oficial" al frente y al blanco en la parte superior, por lo que no necesito rotar el cubo ingresado por el usuario para tener el blanco en la parte inferior.
Primero hice la función color_cube(). Esto aplica un patrón de su elección al cubo usando 6 cuerdas.
Utiliza matemáticas de caracteres simples para cambiar los caracteres '0', '1', etc. a enteros y los almacena en la matriz dada.
Solo uso esto una o dos veces como una sola línea para configurar un cubo resuelto en una línea, porque al traducir la entrada del usuario a algo que la computadora entiende, también puedes ingresar las entradas individualmente en lugar de traducirlas primero a una cadena.
Print_cube() envía el cubo suministrado al terminal. Esto se usa mucho durante la depuración y para la aplicación de terminal. Comencé generando solo los números del 0 al 5 en la forma de un cubo de Rubik explotado.
444
444
444
333 000 111 222
333 000 111 222
333 000 111 222
555
555
555
Debido a que se necesitarían más líneas de código para automatizar esto, simplemente uso 9 declaraciones de impresión, cada una de las cuales contiene hasta 12 variables.
Más tarde descubrí que podía agregar códigos de shell como e[42m
para darles un color de fondo a estos números. Agregarlos me dio 28 variables por impresión.
Finalmente pensé en simplemente poner la letra o el número del color en la matriz que contiene los códigos de shell, devolviéndolo a 12. Desafortunadamente, Windows no admite este tipo de códigos de shell, así que tuve que seguir usando solo números, y cartas posteriores.
Move_cube() es la primera función para cambiar el cubo. Esta es una función recursiva que realiza el movimiento solicitado la cantidad de veces solicitada. Hay 9 movimientos posibles, numerados del 0 al 8. El frente, la derecha, la espalda, etc. predeterminados, pero también el medio, el ecuador y la capa vertical. Usando una función auxiliar que escribí en utils.c llamada Ciclo, cambio los valores de cada cuadrado en cada cara correspondiente al movimiento en el sentido de las agujas del reloj, repitiendo si es necesario.
Debido a que los movimientos generalmente no ocurren por sí solos, a continuación escribí run_algorithm(). Esta función utiliza move_cube() para ejecutar un algoritmo en el cubo suministrado en notación de cubo estándar. Esto me da la capacidad de hacer múltiples movimientos a la vez (o en orden) y hacer movimientos que tienen efecto en 2 o incluso 3 capas como x, y y z.
Finalmente validar(). Agregué esta función mientras escribía la interfaz de Python porque nunca debes confiar en que tu usuario no ingrese cosas raras.
Esta función no comprueba si el cubo de Rubik tiene solución. Comprueba si los colores son correctos. Por ejemplo, un cubo no puede tener pegatinas blancas y amarillas al mismo tiempo. Y yendo en el sentido de las agujas del reloj es posible tener pegatinas verdes, blancas y rojas en ese orden, pero no al revés.
Para hacer esto copié parcialmente la idea del arreglo bloqueado en Tideman, usando un arreglo booleano 2d para las esquinas y un arreglo booleano 3d para los bordes para ver si es posible una combinación de 2 o 3 colores. Este código pasó por múltiples iteraciones. Primero revisa cada borde y esquina por separado y luego usa algunos bucles for.
Cross.c contiene todo el código necesario para resolver la cruz del cubo. Como utilizo un "método humano" (CFOP) para resolver el cubo, me veo obligado a utilizar árboles de decisión enormes.
Reescribí casi todo este archivo en el último minuto para que no tenga que alinear los bordes en cada paso del camino.
Encuentra dónde está el borde amarillo verdoso en la capa inferior, o dónde estaría si aún no está allí. Esto hace que sea más fácil alinear los otros bordes en menos pasos porque pueden alinearse al "cero relativo" en lugar del cero real.
Estos 6 casos son todas las formas en que se puede orientar un borde: apuntando hacia abajo, apuntando hacia arriba, apuntando en las capas superior e inferior, apuntando hacia la izquierda y apuntando hacia la derecha en la capa E.
Básicamente, las funciones escanean las caras en orden de 0 a 3 para buscar piezas que pertenezcan a la cruz, rotarlas a su lugar y almacenar los movimientos en una matriz de caracteres usando una función auxiliar que llamo append().
Solve_cross primero orienta el cubo de modo que el amarillo quede hacia abajo. Luego hace los 6 casos cruzados en orden. Si uno devuelve algo distinto de nulo, vuelve al primer caso.
Una vez que se resuelven los 4 bordes, la función devuelve los algoritmos. Si todos los casos se prueban sin éxito, o después de 4 bucles, la función devuelve NULL.
La misma idea que cross.c, excepto que es mucho más grande. 3600 líneas de código para resolver todos los casos.
Estos 11 casos constan de todas las formas en que se puede orientar una esquina y algunas combinaciones de esquinas y bordes.
Para ahorrar tiempo y líneas, la mayoría de las funciones no resuelven completamente una combinación de esquina/arista, sino que las llevan al estado más corto conocido y llaman a la función correcta.
Los números del 1 al 11 son el orden en que fueron escritos, no siempre el orden en que se ejecutan.
Solve_f2l primero verifica que el cruce esté resuelto. Si no, devuelve nulo.
Después de eso, solve_f2l pasa por los casos en orden del algoritmo promedio más corto al más largo, volviendo a 1 una vez que una función devuelve un algoritmo. Si todos los casos se prueban sin éxito, o después de 4 bucles, la función devuelve NULL.
El archivo del que estoy más orgulloso. Sin bucles gigantes ni árboles de decisión. Sólo matemáticas inteligentes y tablas de búsqueda.
Oll y pll son dos estructuras destinadas a almacenar OLL y PLL. Almacenan el nombre, el patrón que uso para reconocerlos y el algoritmo para resolverlos.
En una noche de insomnio descubrí que podía reconocer PLL usando solo los colores del anillo exterior y OLL por solo 12 booleanos que significan amarillo o no amarillo.
Load_olls() y load_plls() cargan los nombres, patrones y algoritmos de un archivo CSV. Esto pasó por algunas iteraciones.
Primero comencé con getline() para obtener cada línea de los archivos CSV y dividí la cadena en 24 caracteres y 36 caracteres, rellenando el nombre con espacios.
Esto no era bonito, así que comencé a buscar las comas y a dividir las cosas allí.
Finalmente alguien en Discord me dirigió a sscanf() permitiéndome dividir fácil y limpiamente cada cadena.
También cambié a fgets() para que la línea fuera más compatible.
Las líneas que comienzan con # se rechazan porque son comentarios. Las líneas donde el patrón no es puramente numérico se reconocen usando una función auxiliar llamada isNumber() o isBinary() y se rechazan, y mientras escribía esta sección agregué una prueba para ver si el nombre, el patrón y el algoritmo tienen la longitud correcta para detener lecturas y escrituras fuera de límites.
En la matriz olls y plls, el espacio para los nombres y algoritmos se crea usando malloc y la información se copia usando strncpy. El patrón se convierte en una matriz de 12 entradas o valores booleanos.
Find_pll() es mi orgullo y alegría. Fue el primer algoritmo que escribí para resolver el cubo (antes de la cruz y f2l), y se me ocurrió en un sueño. Una forma de comprobar el patrón de oll para detectar cualquier combinación de colores con algunos cálculos simples. Literalmente me desperté y escribí int case[12] containing the outer ring. if (front[0] == (front[1]-(case[0]-case[1])%4))
. Al día siguiente lo simplifiqué a (faces[0][0] == (faces[0][1] - (plls[i].pattern[0] - plls[i].pattern[1])) % 4)
(multiplicado por 12). Luego (faces[j][0] - faces[j][1]) % 4 == (plls[i].pattern[0] - plls[i].pattern[1]) % 4
y finalmente después de muchas iteraciones Terminé con mod((cube[(j + i / 3) % 4][i % 3] + dif[j]), 4)
que me permite codificar el patrón de la capa superior del cubo usando un 12 int matriz, para fácil comparación con todos los valores conocidos.
Podría haber codificado el patrón como un int de 4 bytes, pero decidí no hacerlo porque creo que es mejor hacer que los patrones sean fáciles de entender que guardar 44 bytes (3408 kb en total para patrones).
Finalmente, la función devuelve un pair_int_int que contiene la orientación de la capa y el OLL o PLL correcto. O dos números negativos si no se encuentra nada.
Las funciones solve_oll() y solve_pll() son relativamente simples. Llaman a find_oll o pll para obtener el índice y la orientación correctos (cantidad de movimientos y). Luego, la función gira el cubo usando cualquier movimiento y, ejecuta el algoritmo OLL o PLL correcto y, en el caso de solve_pll(), gira la capa U para finalmente resolver el cubo. Las funciones devuelven el algoritmo utilizado para resolver el cubo en notación de cubo estándar.
Libera la memoria utilizada para almacenar todos los OLL y PLL.
Algunas funciones para hacerme la vida un poco más fácil.
Debido a que el % normal no funciona con números negativos, esta es "mi propia versión" que sí funciona. Un agradecimiento especial al discord cs50.
Para algunas funciones quiero devolver más de una variable. Éste se basa en el par C++
Una función que cicla 4 números. Básicamente, un paso adelante respecto al intercambio. Se utiliza en cube.c para recorrer los bordes y esquinas de los movimientos del cubo. Hay una copia de ese movimiento en cube.js.
Una función que agrega una cadena a otra cadena, reasignándola si es necesario. Se utiliza en todas partes en esta aplicación.
Comprueba si una cadena contiene sólo dígitos o sólo ceros y unos. Actualmente solo se utiliza para cargar OLL y PLL.
Primero escribí solver.c como una interfaz para mi solucionador de cubos, pero rápidamente decidí que quería una interfaz de usuario gráfica, algo que no estoy preparado para hacer en C puro.
Es por eso que comencé a convertir mis cubesolvers en una biblioteca para usar con lo que quiera.
Lo junta todo para resolver el cubo. Toma un cubo y lo valida. Luego intenta resolver la cruz. Si falla, devuelve un error; de lo contrario, almacena la salida. Luego resuelve la cruz o devuelve un error. Luego resuelve el OLL (o devoluciones) y el PLL. Finalmente devuelve una cadena que contiene la solución completa.
Una interfaz para resolver. Genera un cubo a partir de una mezcla ingresada y lo pasa a solve()
Sólo para hacer la vida de todos un poco más fácil. Toma un camino y carga los olls y plls de ese camino. Devuelve verdadero si funciona, se limpia después de sí mismo y devuelve falso si no funciona.
Debido a que es difícil liberar memoria de Python, decidí hacerlo desde C. Tengo versiones "seguras" de cada función que quieras usar desde fuera de C. Estas almacenan los punteros generados que deben liberarse en una matriz global. para liberarlos todos a la vez.
Libera todos los trrings almacenados
Múltiples funciones devuelven cadenas, que al final deben liberarse. Estas funciones almacenan los punteros a estas cadenas para su posterior eliminación.
Para pruebas, y los que se les da mejor con teclado que con ratón, una interfaz de terminal.
Se reescribió varias veces y finalmente se reescribió mientras escribía este archivo Léame para que funcione con solver_library en lugar de hacerlo solo.
¡Hace de todo! Carga los OLL y PLL según la entrada predeterminada o del usuario. Luego genera un cubo basado en la línea de comando o en la entrada del usuario y finalmente resuelve el cubo paso a paso, imprimiendo el cubo a lo largo del camino.
Obtiene el directorio de una ruta de archivo determinada (por ejemplo, argv[0]). Escrito para funcionar con sistemas operativos basados en Windows y Unix.
Cambia una letra de color al int correspondiente. En Python solo usé un diccionario. Mucho más fácil.
Pregunta al usuario los nueve colores de la cara de un cubo de Rubik y los coloca en un cubo.
Interfaz interactiva paso a paso para ingresar los colores en un cubo.
Imprime la prueba de ayuda.
Imprime el uso.
Una aplicación breve y sencilla para probar errores. Genera muchos problemas y los resuelve. Imprime el primer código codificado que falla y se detiene.
Usé esto para encontrar un solo error tipográfico. Todo lo demás lo hice manualmente.
Utiliza memcmp para comparar el cubo arrojado por el algoritmo de resolución con uno de los 4 cubos resueltos (uno por orientación).
Cambia un poco de memoria por velocidad.
Genera revueltas aleatorias para el cubo de Rubik. Gravemente. No protege contra movimientos dobles y movimientos inversos. Pero es suficientemente bueno si la lucha es lo suficientemente larga.
Carga los OLL y PLL a partir de la entrada del usuario. Resuelve el cubo tantas veces como el usuario quiera, hasta que falla.
Contiene una lista de los 58 casos OLL (incluidos los resueltos) para usar con mi biblioteca C
Algoritmos de mi propia memoria y de https://www.speedsolving.com/wiki/index.php/OLL
Contiene una lista de los 22 casos de PLL (incluidos los resueltos) para usar con mi biblioteca C
Algoritmos de mi propia memoria y de https://www.speedsolving.com/wiki/index.php/PLL
Contiene algunos patrones para mezclar el cubo de maneras bonitas. Se utiliza principalmente para probar mi javascript, pero es una característica interesante para el usuario.
Es de esperar que no contenga nada, pero está destinado a almacenar todos y cada uno de los errores al resolver el cubo.
El puente entre mi código C y javascript. Carga la biblioteca C e inicializa la función que necesito con tipos de argumento y, si es necesario, vuelve a escribir. Usando numpy para convertir de tipos de Python a ctypes.
Una función que "tomé prestada" de stackoverflow. Toma un flujo de entrada y genera cada línea hasta #
El solucionador real. Toma un cubo revuelto y lo resuelve paso a paso, devolviendo los pasos como una matriz de diccionarios (que contiene una matriz con algoritmos). En caso de excepciones, almacena el patrón en data/errors.txt.
Solo usado para pruebas. Toma un algoritmo de codificación de GET, lo resuelve usando getsteps() y lo pasa a la plantilla de resolución.
Toma un patrón y lo codifica desde GET o POST, lo resuelve usando getsteps y lo devuelve como JSON.
Toma un patrón de GET o POST y resuelve el cubo, sin importarle la solución real, pero verificando si falla y dónde. Devuelve 0 para el éxito o el número de error.
Muestra la plantilla cube.html: la interfaz de usuario real. Mientras lo hace, también toma los patrones de data/patterns.csv para dárselos a la plantilla, usando un dictreader y decomment para eliminar mis comentarios.
Pasa el favicon al navegador para ver el bonito símbolo del cubo.
Libera toda la memoria de mi código C: los PLL y las cadenas. Registrado usando atexit para ejecutar al salir
Todos los comandos para compilar mi código C en Linux u OS X. Aunque mi biblioteca se puede usar como biblioteca, decidí usar todos los archivos para compilar para poder entregar solo un ejecutable.
Contiene las dos bibliotecas que utilicé y que deben instalarse por separado. Sólo matraz y numpy. Nada especial.
Solicita una codificación, la pasa a app.py para resolverla e imprime la solución. Sólo se utiliza para pruebas rápidas.
Contiene muchos divs vacíos que deben llenarse con mi código javascript y darles forma con CSS. Contiene 3 imágenes para los controles de reproducción, un formulario para ingresar códigos codificados y una breve explicación.
Finalmente carga el javascript porque no es nada sin el dom. No tan bonito, pero sí muy funcional.
Excepto que src contiene el código más actual. Algo de CSS para que todo parezca utilizable y javascript para que sea realmente utilizable. Y algunas imágenes para hacer clic.
Cambia el enlace de divs aleatorios a algo que puedas ver.
Utiliza cuadrículas CSS de forma predeterminada y menos predeterminada.
Utilizo un contenedor exterior para colocar los controles a la izquierda, la animación del solucionador arriba a la derecha y la solución abajo a la derecha.
Usando posicionamiento absoluto, coloco los controles de reproducción y el algoritmo actual sobre la animación.
Todo lo demás es bastante predeterminado excepto el selector de color. Si observa el código html, verá un div que contiene 6 divs, cada uno de los cuales contiene 9 divs. Usando cuadrículas, le doy forma a los 6 divs de "cara" para que parezcan un cubo desplegado, cada cara contiene una cuadrícula de 9 cuadrados. Esto me permite hacer que el tamaño sea variable mientras la forma permanece igual, sin necesidad de usar imágenes.
También pasé una semana o dos haciendo una vista móvil, porque el texto parecía tener un tamaño variable. Me comuniqué con Discord, Facebook e incluso Stackoverflow. Finalmente encontré una línea html en stackoverflow: que resolvió todo en una sola línea.
Trae mi algoritmo al usuario. Basado en tres.js
Esta es la primera vez que escribo javascript. No se ve bonito, probablemente rompe todas las reglas, pero funciona.
Reescribí este archivo varias veces. Primero iterativo, eso fue antes de comenzar a cargar en github, luego funcional y finalmente orientado a objetos.
Primero, declaro todas las variables que deben ser utilizadas por más de una función: los materiales y la geometría de los objetos three.js, las matrices para almacenar los objetos, los ejes de rotación y más.
También hay un diccionario gigantesco que contiene las variables necesarias para cada movimiento de cubo legal: los cubos para ciclar o intercambiar, el eje y los grados de rotación, y qué mostrar. Todo esto podría haber cabido en 72 líneas, pero style50 quiere una variable por línea, lo que hace 649 líneas.
Toma la escena y guarda una referencia, genera los cubos, los contornos y planos en un bucle, establece las posiciones de los cubos y contornos en un bucle y los planos manualmente.
Función auxiliar. Realiza un ciclo de rotaciones de 4 cubos a > b > c > d > a usando un cubo temporal.
Función auxiliar. Intercambia la rotación de 2 cubos usando un cubo temporal.
Restablece los cubos a su posición predeterminada y restablece el div de solución. Esto necesita más pasos de los que piensas. Primero necesitamos restablecer todas las animaciones y terminarlas. Restablece todos los movimientos pasados y futuros y finalmente restablece la rotación de los cubos.
Devuelve verdadero o falso dependiendo de si el cubo se está moviendo.
Configura la animación para un movimiento en el cubo.
Esta función originalmente tenía 160 líneas de declaraciones if/else, pero luego la reescribí para obtener los cubitos y los movimientos de un diccionario gigantesco. Esto hace que esta y la siguiente función sean más fáciles de leer, y hace que si escribo una versión 4x4 o 5x5 solo tenga que cambiar el diccionario.
Completa la animación y realmente mueve (rota) los cubos.
Debido a que los cubos realmente no se mueven, la idea básica es copiar la rotación del cubo que en un cubo real se movería a su lugar, y luego rotar todo.
Esta función era originalmente una monstruosidad de 375 líneas de declaraciones if/else adjuntas. Posteriormente fue reescrito para funcionar con el diccionario de movimientos.
Realiza el siguiente movimiento en la lista de movimientos, o el siguiente algoritmo en la lista de pasos, y muestra el algoritmo actual en la pantalla.
¿El movimiento anterior en la lista de movimientos se realiza al revés, o el algoritmo anterior en la lista de pasos?
Aplica los colores del selector de colores (o en realidad la matriz de caras) al cubo girando cada cubo. Contiene algunas de sus propias funciones. Cada cubo tiene su propia línea con modificadores manuales. Para mí es demasiado aleatorio para automatizarlo. Pero si quieres intentarlo, sé mi invitado.
Cambia de verde a azul, de rojo a naranja, de blanco a amarillo y viceversa. Ahora que lo pienso, esto podría haber sido un dictado, pero una línea de matemáticas y 3 si también funcionan.
Gira un cubo según el color frontal e izquierdo, o el color superior. Básicamente, muchas declaraciones if con movimientos codificados, pero eso es suficiente aquí.