Meu projeto final para CS50 é um solucionador de cubo de Rubik de "método humano".
Isso significa que o Cubo de Rubik inserido é resolvido usando um método que um ser humano poderia usar (CFOP), com etapas claras: Cross, F2L, OLL e PLL.
O produto final consiste em uma aplicação shell, uma biblioteca C para uso em outros projetos e uma interface web escrita em Flask, HTML, CSS e Javascript.
Decidi criar um solucionador de Cubo de Rubik porque já fiz um solucionador de Sudoku antes em C++, e o cubo foi um avanço.
Em vez de um plano 2D, tenho um plano semi-3D, com arestas e cantos.
Como os solucionadores de IA foram concluídos e não estou confiante o suficiente em minhas habilidades de escrita de IA, decidi fazer o programa resolver o cubo da maneira que faço:
Cruzado, F2L, OLL e PLL
Baixe o executável bin/solver ou construa a partir do código-fonte usando make solver
Baixe ou clone os arquivos deste repositório e construa usando make solver
Baixe ou clone os arquivos usando este repositório e compile todos os arquivos .c em src, exceto solver_library
Execute o solucionador com um dos seguintes comandos:
./solver "Algorithm"
./solver [Up] [Front] [Right] [Back] [Left] [Down]
Substituir o algoritmo por um algoritmo de embaralhamento (por exemplo, U R2 FBR B2 R U2 L B2 RU' D' R2 FR' L B2 U2 F2
) ou as faces com as cores dessa face (por exemplo, wbwowrwgw gwgogrgyg rwrgrbryr bwbrbobyb owobogoyo ygyoyryby
).
As cores possíveis são os primeiros caracteres Verde, Vermelho, Azul, Laranja, Branco e Amarelo.
Se os arquivos olls.txt e plls.txt não estiverem na mesma pasta que o binário, use as opções -o e -p ou -d para apontar para os arquivos
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"
Adicione a opção -t
para imprimir apenas o texto sem imagens do cubo.
Baixe ou clone os arquivos deste repositório.
Instale Flask e Numpy usando pip.
python3 -m pip -r requirements.txt
Use o comando flask run
ou python3 app.py
para executar o servidor web.
Acesse https://127.0.0.1:5000/ para usar o solucionador.
Baixe ou clone os arquivos deste repositório.
Remova a versão Linux de bin/libcubesolver.so manualmente ou usando make clean
e recompile usando make library
.
Instale Flask e Numpy usando pip:
python3 -m pip -r requirements.txt
Use o comando flask run
ou python3 app.py
para executar o servidor web.
Acesse https://127.0.0.1:5000/ para usar o solucionador.
Baixe ou clone os arquivos deste repositório. Remova a versão Linux de bin/libcubesolver.so manualmente.
Compile todos os arquivos .c em src, exceto solver.c, em bin/libcubesolver.so. Ou se estiver usando um nome diferente, altere a linha 19 em app.py para refletir isso.
Instale Flask e Numpy usando pip:
python3 -m pip -r requirements.txt
Use o comando flask run
ou python3 app.py
para executar o servidor web.
Acesse https://127.0.0.1:5000/ para usar o solucionador.
Compile todos os arquivos em src, exceto solver.c, em libcubesolver.so, libcubesolver.dylib ou libcubesolver.dll e salve-os onde quer que as bibliotecas sejam salvas em seu PC.
No Linux você pode usar make library
Copie libcubesolver.h de src para onde quer que os cabeçalhos sejam salvos em seu PC (ex. /usr/include/)
Vincule com -lcubesolver
ou apenas compile no aplicativo como se fosse um arquivo .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 a versão C para ver o que cada função faz.
Como é difícil liberar memória mallocada em python, use versões "seguras" das funções de resolução e use free_strings() após o 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 quer que eu explique o que cada arquivo faz e o que ele contém. Portanto, este leia-me é mais longo do que todos os meus arquivos, exceto três.
├──app.py ├── caixa │ ├── libcubesolver.so │ └── solucionador ├── dados │ ├── erros.txt │ ├── olls.csv │ ├── padrões.csv │ └── plls.csv ├── makefile ├── README.md ├── requisitos.txt ├──src │ ├── cruz.c │ ├── cruz.h │ ├── cubo.c │ ├── cubo.h │ ├── cubesolver.h │ ├── f2l.c │ ├── f2l.h │ ├── última camada.c │ ├── última camada.h │ ├── solucionador.c │ ├── solver_library.c │ ├── utilitários.c │ └── utilitários.h ├── teste │ ├── testes aleatórios.c │ └── testes aleatórios ├── estático │ ├── cubo.css │ ├── cubo.js │ ├── cubeinterface.js │ ├── favicon.ico │ ├── próximo.png │ ├── pausa.png │ ├── play.png │ └── anterior.png ├── modelos │ ├── cubo.html │ └── solucionador.html
Cube.c e cube.h contêm todo o código que controla o cubo de Rubik na memória.
O cubo de Rubik, na memória, é uma matriz 2d de 6 por 9. A matriz externa contém as seis faces: Frente, Direita, Trás, Esquerda, Cima e Baixo.
Escolhi essa ordem porque as faces para cima e para baixo são sempre iguais, então posso codificá-las, mas os lados só precisam estar corretos entre si. Isso significa que posso usar mais ou menos simples, combinado com módulo para traduzir os testes e movimentos para todos os lados. O mesmo vale para as cores, que são salvas como números de 0 a 5, sendo 0 a cor frontal, 1 a direita, etc.
Escolhi primeiro a ordem das cores que uso: vermelho na frente, amarelo em cima, verde à direita. Porém, mais tarde, ao escrever meu código e interface python, mudei para o verde "Oficial" na frente e branco na parte superior, para não precisar girar o cubo inserido pelo usuário para ter branco na parte inferior.
Primeiro fiz a função color_cube(). Isso aplica um padrão de sua escolha ao cubo usando 6 cordas.
Ele usa matemática de caracteres simples para alterar os caracteres '0', '1', etc. para ints e os armazena no array fornecido.
Eu só uso isso uma ou duas vezes como uma linha para configurar um cubo resolvido em uma linha, porque ao traduzir a entrada do usuário para algo que o computador entende, você também pode inserir os inteiros individualmente em vez de primeiro traduzi-los para uma string.
Print_cube() envia o cubo fornecido para o terminal. Isso é muito usado durante a depuração e para o aplicativo de terminal. Comecei exibindo apenas os números de 0 a 5 na forma de um cubo de Rubik explodido.
444
444
444
333 000 111 222
333 000 111 222
333 000 111 222
555
555
555
Como seriam necessárias mais linhas de código para automatizar isso, simplesmente uso 9 instruções de impressão, cada uma contendo até 12 variáveis.
Mais tarde, descobri que poderia adicionar códigos de shell como e[42m
para dar a esses números uma cor de fundo. Adicioná-los me deu 28 variáveis por impressão.
Finalmente pensei em simplesmente colocar a letra ou o número da cor na matriz que contém os códigos do shell, trazendo-o de volta para 12. Infelizmente o Windows não suporta esse tipo de código do shell, então tive que continuar usando apenas números, e cartas posteriores.
Move_cube() é a primeira função para realmente alterar o cubo. Esta é uma função recursiva que faz o movimento solicitado a quantidade solicitada de vezes. Existem 9 movimentos possíveis, numerados de 0 a 8. O padrão frontal, direito, traseiro, etc. Mas também o meio, o equador e a camada permanente. Usando uma função auxiliar que escrevi em utils.c chamada Cycle, troco os valores de cada quadrado em cada face correspondendo ao movimento no sentido horário, repetindo se necessário.
Como os movimentos geralmente não acontecem por conta própria, em seguida escrevi run_algorithm(). Esta função usa move_cube() para executar um algoritmo no cubo fornecido na notação de cubo padrão. Isso me dá a capacidade de fazer vários movimentos ao mesmo tempo (ou na verdade em ordem) e fazer movimentos que tenham efeito em 2 ou até 3 camadas, como x, y e z.
Finalmente, validar(). Eu adicionei esta função enquanto escrevia a interface Python porque você nunca deve confiar que seu usuário não inserirá coisas estranhas.
Esta função não verifica se o cubo de Rubik pode ser resolvido. Ele verifica se as cores estão corretas. Por exemplo, um cubinho não pode ter adesivos brancos e amarelos. E indo no sentido horário é possível ter adesivos verdes, brancos e vermelhos nessa ordem, mas não o contrário.
Para fazer isso copiei parcialmente a ideia do array bloqueado no Tideman, usando um array booleano 2d para os cantos e um array booleano 3d para as bordas para ver se uma combinação de 2 ou 3 cores é possível. Este código passou por várias iterações. Primeiro verificando cada borda e canto separadamente e depois usando alguns loops for.
Cross.c contém todo o código necessário para resolver a cruz do cubo. Como uso um "método humano" (CFOP) para resolver o cubo, sou forçado a usar enormes árvores de decisão.
Reescrevi quase todo esse arquivo no último minuto para que não seja necessário alinhar as bordas em cada etapa do processo.
Encontra onde na camada inferior está a borda verde-amarelada, ou onde estaria se ainda não estivesse lá. Isso torna mais fácil alinhar as outras arestas em menos etapas porque elas podem alinhar ao "zero relativo" em vez do zero real.
Esses 6 casos são todas as maneiras pelas quais uma aresta pode ser orientada: apontando para baixo, apontando para cima, apontando para cima e para baixo nas camadas, apontando para a esquerda e apontando para a direita na camada E.
As funções basicamente examinam as faces na ordem de 0 a 3 para procurar peças que pertencem à cruz, giram-nas no lugar e armazenam os movimentos em um array char usando uma função auxiliar que chamo de append().
Solve_cross primeiro orienta o cubo para que o amarelo fique para baixo. Então ele faz os 6 casos cruzados em ordem. Se alguém retornar algo diferente de nulo, ele volta ao primeiro caso.
Depois que todas as 4 arestas forem resolvidas, a função retornará os algoritmos. Se todos os casos forem testados sem sucesso ou após 4 loops a função retornará NULL.
A mesma ideia do cross.c, só que bem maior. 3600 linhas de código para resolver todos os casos.
Esses 11 casos consistem em todas as maneiras pelas quais um canto pode ser orientado e em algumas combinações de cantos e arestas.
Para economizar tempo e linhas, a maioria das funções não resolve completamente uma combinação de canto/aresta, mas traz-as para o estado mais curto conhecido e chama a função correta.
Os números de 1 a 11 são a ordem em que foram escritos, nem sempre a ordem em que são executados.
Solve_f2l primeiro verifica se o cruzamento foi resolvido. Caso contrário, retorna nulo.
Depois disso, solve_f2l percorre os casos na ordem do algoritmo médio mais curto para o mais longo, voltando para 1 quando uma função retorna um algoritmo. Se todos os casos forem testados sem sucesso, ou após 4 loops a função retornará NULL.
O arquivo do qual mais me orgulho. Sem loops gigantes, sem árvores de decisão. Apenas matemática inteligente e tabelas de pesquisa.
Oll e pll são duas estruturas destinadas a armazenar OLL e PLLs. Eles armazenam o nome, o padrão que uso para reconhecê-los e o algoritmo para resolvê-los.
Em uma noite sem dormir, descobri que poderia reconhecer o PLL usando apenas as cores do anel externo e o OLL por apenas 12 booleanos, significando amarelo ou não amarelo.
Load_olls() e load_plls() carregam os nomes, padrões e algoritmos de um arquivo CSV. Isso passou por algumas iterações.
Comecei com getline() para obter cada linha dos arquivos CSV e dividi a string em 24 caracteres e 36 caracteres, preenchendo o nome com espaços.
Isso não era bonito, então comecei a procurar as vírgulas e dividir as coisas ali.
Finalmente, alguém no discord me direcionou para sscanf(), permitindo-me dividir cada string de maneira fácil e limpa.
Também mudei para fgets() para tornar a linha mais compatível.
Linhas que começam com # são rejeitadas porque são comentários. Linhas onde o padrão não é puramente numérico são reconhecidas usando uma função auxiliar chamada isNumber() ou isBinary() e são rejeitadas, e enquanto escrevia esta seção adicionei um teste para ver se o nome, o padrão e o algoritmo têm o comprimento certo para interrompa leituras e gravações fora do limite.
No array olls e plls, o espaço para os nomes e algoritmos é criado usando malloc e as informações são copiadas usando strncpy. O padrão é transformado em um array de 12 inteiros ou booleanos.
Find_pll() é meu orgulho e alegria. Foi o primeiro algoritmo que escrevi para resolver o cubo (antes do cruzamento e f2l), e me ocorreu em um sonho. Uma maneira de verificar o padrão antigo para qualquer combinação de cores com algumas contas simples. Eu literalmente acordei e anotei int case[12] containing the outer ring. if (front[0] == (front[1]-(case[0]-case[1])%4))
. No dia seguinte simplifiquei para (faces[0][0] == (faces[0][1] - (plls[i].pattern[0] - plls[i].pattern[1])) % 4)
(vezes 12). Então (faces[j][0] - faces[j][1]) % 4 == (plls[i].pattern[0] - plls[i].pattern[1]) % 4
e finalmente depois de muitas iterações Acabei com mod((cube[(j + i / 3) % 4][i % 3] + dif[j]), 4)
me permitindo codificar o padrão da camada superior do cubo usando um 12 int matriz, para fácil comparação com todos os valores conhecidos.
Eu poderia ter codificado o padrão como um int de 4 bytes, mas decidi não fazê-lo porque acho melhor tornar os padrões fáceis de entender do que economizar 44 bytes (3408 kb no total para padrões)
Finalmente a função retorna um pair_int_int contendo a orientação da camada e o OLL ou PLL correto. Ou dois números negativos se nada for encontrado.
As funções solve_oll() e solve_pll() são relativamente simples. Eles chamam find_oll ou pll para obter o índice e a orientação corretos (quantidade de movimentos y). Em seguida, a função gira o cubo usando um movimento y, executa o algoritmo OLL ou PLL correto e, no caso de solve_pll(), gira a camada U para finalmente resolver o cubo. As funções retornam o algoritmo usado para resolver o cubo na notação de cubo padrão.
Libera a memória usada para armazenar todos os OLLs e PLLs.
Algumas funções para tornar minha vida um pouco mais fácil.
Como o% normal não funciona com números negativos, esta é a "minha própria versão" que funciona. Com agradecimentos especiais ao discord do cs50.
Para algumas funções quero retornar mais de uma variável. Este é baseado no par C++
Uma função que alterna 4 números. Basicamente, um avanço em relação à troca. Usado em cube.c para percorrer as arestas e cantos dos movimentos do cubo. Há uma cópia dessa movimentação em cube.js
Uma função que adiciona uma string a outra string, realocando se necessário. Usado em todos os lugares deste aplicativo.
Verifica se uma string contém apenas dígitos ou apenas zeros e uns. Atualmente usado apenas para carregar OLLs e PLLs.
Escrevi primeiro o solver.c como uma interface para meu solucionador de cubos, mas rapidamente decidi que queria uma UI gráfica, o que não estou pronto para fazer em C puro.
É por isso que comecei a transformar meus cubesolvers em uma biblioteca para usar com o que eu quiser.
Junta tudo para resolver o cubo. Ele pega um cubo e o valida. Em seguida, tenta resolver a cruz. Se falhar, retorna um erro, caso contrário, armazena a saída. Depois resolve o cruzamento ou retorna um erro. Em seguida, resolve o OLL (ou retornos) e o PLL. Finalmente ele retorna uma string contendo a solução completa.
Uma interface para resolver. Gera um cubo a partir de um embaralhamento inserido e passa-o para solve()
Só para facilitar um pouco a vida de todos. Pega um caminho e carrega os olls e plls desse caminho. Retorna verdadeiro se funcionar, limpa depois de si mesmo e retorna falso se não funcionar.
Como é difícil liberar memória do Python, decidi fazê-lo em C. Tenho versões "seguras" de todas as funções que você pode querer usar fora de C. Elas armazenam os ponteiros gerados que precisam ser liberados para um array global por libertar tudo de uma vez.
Liberte todas as coisas armazenadas
Múltiplas funções retornam strings, que precisam ser liberadas no final. Essas funções armazenam os ponteiros para essas strings para exclusão posterior.
Para testes, e para quem é melhor com teclado do que com mouse, uma interface de terminal.
Ele foi reescrito várias vezes, sendo finalmente reescrito enquanto escrevia este leia-me para funcionar com o solver_library em vez de sozinho.
Faz tudo! Ele carrega os OLLs e PLLs com base no padrão ou na entrada do usuário. Em seguida, ele gera um cubo com base na linha de comando ou na entrada do usuário e, finalmente, resolve o cubo passo a passo, imprimindo o cubo ao longo do caminho.
Obtém o diretório de um determinado caminho de arquivo (por exemplo, argv[0]). Escrito para funcionar com sistemas operacionais baseados em Windows e Unix.
Altera uma letra colorida para o int correspondente. Em python, usei apenas um dicionário. Muito mais fácil.
Pergunte ao usuário as nove cores da face de um cubo de Rubik e coloque-as em um cubo.
Interface interativa passo a passo para inserir as cores em um cubo.
Imprime o teste de ajuda.
Imprime o uso.
Um aplicativo simples e curto para teste de bugs. Gera muitas confusões e as resolve. Imprime o primeiro embaralhamento que falha e para.
Usei isso para encontrar um único erro de digitação. Todo o resto eu fiz manualmente.
Usa memcmp para comparar o cubo gerado pelo algoritmo de resolução com um dos 4 cubos resolvidos (um por orientação).
Troca um pouco de memória por velocidade.
Gera embaralhamentos aleatórios para o cubo de Rubik. Seriamente. Não protege contra movimentos duplos e movimentos reversos. Mas é bom o suficiente se a corrida for longa o suficiente.
Carrega os OLLs e PLLs da entrada do usuário. Resolve o cubo quantas vezes o usuário quiser, até falhar.
Contém uma lista de todos os 58 casos OLL (incluindo resolvidos) para uso com minha biblioteca C
Algoritmos de minha própria memória e de https://www.speedsolving.com/wiki/index.php/OLL
Contém uma lista de todos os 22 casos de PLL (incluindo resolvidos) para uso com minha biblioteca C
Algoritmos de minha própria memória e de https://www.speedsolving.com/wiki/index.php/PLL
Contém alguns padrões para embaralhar o cubo de maneiras bonitas. Usado principalmente para testar meu javascript, mas é um recurso interessante para o usuário.
Esperançosamente, não contém nada, mas destina-se a armazenar todo e qualquer erro na resolução do cubo.
A ponte entre meu código C e javascript. Ele carrega a biblioteca C e inicializa a função necessária com argtypes e, se necessário, redigita. Usando numpy para converter de tipos python em ctypes.
Uma função que "peguei emprestada" do stackoverflow. Ele pega um fluxo de entrada e gera cada linha até #
O solucionador real. Ele pega um cubo embaralhado e o resolve passo a passo, retornando as etapas como um array de dicionários (contendo um array com algoritmos). Em caso de exceções ele armazena o padrão em data/errors.txt
Usado apenas para testes. Pega um algoritmo embaralhado do GET, resolve-o usando getsteps() e passa-o para o modelo do solucionador.
Pega um padrão e embaralha-o de GET ou POST, resolve-o usando getsteps e retorna-o como JSON.
Pega um padrão de GET ou POST e resolve o cubo, sem se importar com a solução real, mas verificando se e onde ela falha. Retorna 0 para sucesso ou o número do erro.
Mostra o modelo cube.html: a UI real. Ao fazer isso, também são necessários os padrões de data/patterns.csv para fornecer ao modelo, usando um dictreader e decomment para remover meus comentários.
Passa o favicon para o navegador para o belo símbolo do cubo.
Libera toda a memória do meu código C: os PLLs e as strings. Registrado usando atexit para executar na saída
Todos os comandos para compilar meu código C no Linux ou OS X. Mesmo que minha biblioteca possa ser usada como uma biblioteca, decidi usar todos os arquivos para compilar, para poder distribuir apenas um executável.
Contém as duas bibliotecas que usei e que precisam ser instaladas separadamente. Apenas frasco e numpy. Nada de especial.
Pede um embaralhamento, passa para app.py resolver e imprime a solução. Usado apenas para testes rápidos.
Contém muitos divs vazios para serem preenchidos pelo meu código javascript e moldados por css. Contém 3 imagens para os controles de reprodução, um formulário para inserir embaralhamentos e uma breve explicação.
Finalmente carrega o javascript porque não há nada sem o dom. Não é tão bonito, mas muito funcional.
Exceto que src contém o código mais real. Algum CSS para fazer tudo parecer utilizável e javascript para torná-lo realmente utilizável. E algumas imagens para clicar.
Altera o link de divs aleatórios para algo que você possa ver.
Usa grades CSS por padrão e menos formas padrão.
Eu uso um contêiner externo para colocar os controles à esquerda, a animação do solucionador no canto superior direito e a solução no canto inferior direito.
Usando posicionamento absoluto coloquei os controles de reprodução e o algoritmo atual sobre a animação.
Todo o resto é padrão, exceto o seletor de cores. Se você olhar o código HTML, verá apenas uma div contendo 6 divs, cada uma contendo 9 divs. Usando grades, moldei os 6 divs de "faces" para parecerem um cubo desdobrado, cada face contém uma grade de 9 quadrados. Isso me permite tornar o tamanho variável enquanto a forma permanece a mesma, sem a necessidade de usar imagens.
Também passei uma ou duas semanas fazendo uma visualização mobile, porque o texto parecia ter tamanho variável. Entrei em contato com o discord, facebook e até stackoverflow. Finalmente encontrei uma linha html no stackoverflow: que resolveu tudo em uma linha.
Traz meu algoritmo para o usuário. Baseado em três.js
Esta é a primeira vez que escrevo javascript. Não parece bonito, provavelmente quebra todas as regras, mas funciona.
Eu reescrevi este arquivo várias vezes. Primeiro iterativo, isso foi antes de eu começar a fazer upload para o github, depois funcional e, finalmente, meio orientado a objetos
Primeiro, declaro todas as variáveis que precisam ser usadas por mais de uma função: os materiais e a geometria dos objetos three.js, arrays para armazenar os objetos, os eixos de rotação e muito mais.
Há também um dicionário gigantesco contendo as variáveis necessárias para cada movimento legal do cubo: os cubos para alternar ou trocar, o eixo e os graus de rotação e o que mostrar. Tudo isso poderia caber em 72 linhas, mas style50 quer uma variável por linha, totalizando 649 linhas.
Pega a cena e salva uma referência, gera os cubos, os contornos e planos em loop, define as posições dos cubos e contornos em loop e os planos manualmente.
Função auxiliar. Alterna as rotações de 4 cubinhos a > b > c > d > a usando um cubinho temporário.
Função auxiliar. Troca a rotação de 2 cubinhos usando um cubinho temporário.
Redefina os cubies para sua posição padrão e redefina o div da solução. Isso precisa de mais etapas do que você imagina. Primeiro precisamos redefinir toda a animação, finalizá-la. Redefina todos os movimentos futuros e passados e, finalmente, redefina a rotação dos cubos.
Retorna verdadeiro ou falso dependendo se o cubo está se movendo.
Configura a animação para um movimento no cubo.
Esta função tinha originalmente 160 linhas de instruções if/else, mas mais tarde eu a reescrevi para obter os cubinhos e movimentos de um dicionário gigantesco. Isso torna esta e a próxima função mais fáceis de ler e faz com que, se eu escrever uma versão 4x4 ou 5x5, só precise alterar o dicionário.
Completa a animação e move (gira) os cubos.
Como os cubinhos não se movem realmente, a ideia básica é copiar a rotação do cubinho que em um cubo real se moveria para seu lugar e depois girar tudo.
Esta função era originalmente uma monstruosidade de 375 linhas de instruções if/else seguidas. Posteriormente, foi reescrito para funcionar com o dicionário de movimentos.
Executa o próximo movimento na lista de movimentos ou o próximo algoritmo na lista de etapas e mostra o algoritmo atual na tela.
O movimento anterior move a lista ao contrário ou o algoritmo anterior na lista de etapas
Aplica as cores do seletor de cores (ou na verdade da matriz de faces) ao cubo girando cada cubinho. Contém algumas de suas próprias funções Cada cubie tem sua própria linha com modificadores manuais. Para mim, é muito aleatório para automatizar. Mas se você quiser tentar, fique à vontade.
Muda verde para azul, vermelho para laranja, branco para amarelo e vice-versa. Agora que penso nisso, isso poderia ter sido um ditado, mas uma linha de matemática e 3 ifs também funcionam.
Gira um cubinho com base na cor frontal e esquerda ou na cor superior. Basicamente, muitas instruções if com movimentos codificados, mas isso é o suficiente aqui.