Resolver o cubo de bolso (Rubiks)

16

Sua tarefa

.. é fazer o que Brian Fantana aparentemente não poderia fazer e resolver o cubo de Rubik 2x2x2.

cubo de bolso - pivot

O layout

- -   A B   - -   - -
- -   C D   - -   - -

E F   G H   I J   K L
M N   O P   Q R   S T

- -   U V   - -   - -
- -   W X   - -   - -

E será fornecido a você via stdin ou pela linha de comando (sua escolha - especifique na sua resposta) no formato:

ABCDEFGHIJKLMNOPQRSTUVWX

Observe que o AD compõe a face em U (para cima), o EFMN compõe a face em L (esquerda), o GHOP compõe a face em F (frente), o IJQR compõe a face em R (direita), o KLST compõe a A face B (traseira) e o UX compõem a face D (baixa).

Haverá seis caracteres únicos representando cada cor, mas eles podem variar, portanto, prepare-se para que qualquer combinação de 6 caracteres ASCII seja usada para cada cor.

Especificações

  • Seu código deve gerar uma solução usando apenas as faces Direita (R), Superior (U) e Dianteira (F) e deve usar a notação padrão: R, R ', R2, U, U', U2, F, F ', F2. Você pode encontrar mais informações aqui . A restrição ao subconjunto RUF é padrão para o cubo 2x2 (Dica: trate o canto inferior traseiro esquerdo como uma base fixa para trabalhar).
  • Seu código deve ser capaz de resolver todas as permutações possíveis do cubo de bolso.
  • Cada solução deve levar menos de 30 segundos para ser concluída.
  • Cada solução deve ter menos de 30 movimentos.
  • Um bônus de -20% será concedido para soluções sempre com respostas inferiores a 20 movimentos (anuncie-o na sua resposta para que eu possa fazer uma verificação completa)
  • Um bônus de -50% será concedido para o código que sempre fornece uma solução ideal. - Novamente, anuncie em sua resposta
  • As soluções não devem conter dois movimentos consecutivos na mesma face, porque eles podem ser facilmente combinados em um movimento.
  • Opcionalmente, as soluções podem conter um único espaço - e apenas um único espaço - entre cada movimento.
  • A seqüência completa da solução, se necessário, pode estar entre parênteses, aspas, chaves, colchetes ou circunflexos, mas nenhuma outra saída estranha é permitida.
  • Forneça uma versão brevemente comentada do seu código ou uma explicação completa do seu código.
  • Sem uso de arquivos externos. Isso inclui internet, tabelas de dados e bibliotecas / pacotes criados para esse tipo de problema.
  • O código mais curto pela contagem de bytes vence.
  • O vencedor foi escolhido quarta-feira (30 de julho de 2014).
Kyle McCormick
fonte
20
Temos 2x2, 3x3 e 4x4 , mas ainda estou esperando o desafio 1x1 pela minha chance de brilhar. Eu tenho um algoritmo perfeito!
Maçaneta
Aqui é um solucionador carácter ~ 500 em K, que gera solução ainda o ideal (= mais curto): speedsolving.com/forum/...
Jakube
30 segundos devem ser suficientes para força bruta usando Dijkstra: existem apenas 3674160 posições.
Peter Taylor
2
1. Suponho que não haja restrições de espaço em branco na saída 2. Para ser objetivo, você deve definir o bônus para soluções com menos de 20 movimentos, em vez de deixá-lo como "discricionário".
Level River St
@steveverrill Corrigido. Também foi adicionada a especificação de espaço em branco. Obrigado!
Kyle McCormick

Respostas:

11

Python 2.7: 544 bytes -50% = 272 bytes **

import sys;o=''.join;r=range;a=sys.argv[1];a=o([(' ',x)[x in a[12]+a[19]+a[22]] for x in a]);v={a:''};w={' '*4+(a[12]*2+' '*4+a[19]*2)*2+a[22]*4:''}
m=lambda a,k:o([a[([0x55a5498531bb9ac58d10a98a4788e0,0xbdab49ca307b9ac2916a4a0e608c02,0xbd9109ca233beac5a92233a842b420][k]>>5*i)%32] for i in r(24)])
def z(d,h):
 t={}
 for s in d[0]:
  if s in d[1]:print d[h][s]+d[1-h][s];exit()
  n=[d[0][s],'']
  for k in r(3):
   for j in r(3):s=m(s,k);t[s]=n[h]+'RUF'[k]+" 2'"[(j,2-j)[h]]+n[1-h]
   s=m(s,k)
 d[0]=t;return d
while 1:v,w=z([v,w],0);w,v=z([w,v],1)

O Stackexchange substitui as guias por vários espaços em branco. Tão técnica que esta versão possui 549 bytes. Apenas substitua os dois primeiros espaços nas linhas 6-10 por um tabulador.

Idéia por trás do meu programa: minha primeira idéia foi uma primeira busca de respiração. Mas isso levou muito tempo. Cerca de 2 minutos para uma luta difícil (11 movimentos ideais). Então, decidi abordar o problema de ambos os lados. Eu uso dois conjuntos. Eu gero seqüencialmente todos os estados com distância 1,2,3, ... para a corrida e os salvo no conjunto1, e ao mesmo tempo todos os estados com distância 1,2,3, ... para o estado resolvido e os salvamos no conjunto2. A primeira vez que um estado está nos dois conjuntos, encontramos a solução.

Para isso, preciso das cores do cubo resolvido, que não são conhecidas. Os caracteres 13, 20 e 23 definem a cor esquerda, traseira e inferior. Mas essas três cores são suficientes para representar o cubo. Simplesmente substituo as outras 3 cores por espaços em branco e posso representar meu estado resolvido como '____ll____bbll____dddd'.

Ah, e para encurtar as permutações, usei uma ideia de /codegolf//a/34651/29577

Versão não destruída:

import sys

#define permutations for R,U,F
permutation = [[0,7,2,15,4,5,6,21,16,8,3,11,12,13,14,23,17,9,1,19,20,18,22,10],
            [2,0,3,1,6,7,8,9,10,11,4,5,12,13,14,15,16,17,18,19,20,21,22,23],
            [0,1,13,5,4,20,14,6,2,9,10,11,12,21,15,7,3,17,18,19,16,8,22,23]]

def applyMove(state, move):
    return ''.join([state[i] for i in permutation[move]])

scramble = sys.argv[1]
#remove up,front,rigth colors
scramble = ''.join([(' ', x)[x in scramble[12]+scramble[19]+scramble[22]] for x in scramble])
solved = ' '*4+scramble[12]*2+' '*4+scramble[19]*2+scramble[12]*2+' '*4+scramble[19]*2+scramble[22]*4

dict1 = {scramble: ''} #stores states with dist 0,1,2,... from the scramble
dict2 = {solved: ''} #stores states with dist 0,1,2,... from the solved state

moveName = 'RUF'
turnName = " 2'"

for i in range(6):
    tmp = {}
    for state in dict1:
        if state in dict2:
            #solution found
            print dict1[state] + dict2[state]
            exit()
        moveString = dict1[state]
        #do all 9 moves
        for move in range(3):
            for turn in range(3):
                state = applyMove(state, move)
                tmp[state] = moveString + moveName[move] + turnName[turn]
            state = applyMove(state, move)
    dict1 = tmp
    tmp = {}
    for state in dict2:
        if state in dict1:
            #solution found
            print dict1[state] + dict2[state]
            exit()
        moveString = dict2[state]
        #do all 9 moves
        for move in range(3):
            for turn in range(3):
                state = applyMove(state, move)
                tmp[state] = moveName[move] + turnName[2 - turn] + moveString
            state = applyMove(state, move)
    dict2 = tmp

Estou muito feliz com o resultado, porque sou bastante novo no Python. Este é um dos meus primeiros programas python.

editar: meio ano depois: 427 - 50% = 213.5

Tem um pouco mais de experiência em Python e no golfe. Revisei meu código original e pude salvar mais de 100 caracteres.

import sys;o=''.join;a=sys.argv[1];d=[{o((' ',x)[x in a[12]+a[19]+a[22]]for x in a):[]},{' '*4+(a[12]*2+' '*4+a[19]*2)*2+a[22]*4:[]}]
for h in[0,1]*6:
 for s,x in d[h].items():
  for y in range(12):
   d[h][s]=x+[y-[1,-1,1,3][h*y%4]];
   if s in d[1-h]:print o('RUF'[x/4]+" 2'"[x%4]for x in d[0][s]+d[1][s][::-1]);exit()
   s=o(s[ord(c)-97]for c in'acahabcdnpbfegefhugiovjgqkciljdeklflmmmnnvoopxphrqdjrrbsstttuuqsviwwwkxx'[y/4::3])

Basicamente, uso exatamente a mesma abordagem. A maior mudança é que eu não defino mais uma função. Ao invés de

def z(d,h):
 for s in d[0]:
  if s in d[1]:...
while 1:v,w=z([v,w],0);w,v=z([w,v],1)

eu posso fazer

for h in[0,1]*6:
 for s in d[h]:
  if s in d[1-h]:...

Também mudei o movimento lamda um pouco. Primeiro diminua e depois integre o código diretamente, pois a chamada de função aparece apenas uma vez.

Eu mantenho para cada estado uma lista de números entre 0 e 11, para representar os movimentos, em vez de uma sequência contendo os movimentos. Os números são convertidos no final.

Também combinei os dois for-loops 'for k in r(3):for j in r(3):em um for y in r(12). Portanto, eu também tenho que fazer os movimentos U4, R4, F4. É claro que esse movimento não aparece na solução mais curta, então " 2'"[x%4]funciona. (Se x % 4 == 3houver uma exceção de índice fora do intervalo)

Também é um pouco mais rápido, pois procuro a entrada no segundo conjunto anterior. Cerca de 0,5 segundo para uma solução de 11 movimentos.

Jakube
fonte
2
Voto positivo por usar bfs bidirecionais - meu algoritmo de pesquisa favorito (ao lado de IDA *). Se o tempo permitir, testarei em algumas horas para otimizar. Além disso, eu não percebi que você realmente não precisa das cores U / R / F para resolver o quebra-cabeça. Bem feito!
Kyle McCormick
Aprovado nos meus 20 casos de teste para correção e otimização.
Kyle McCormick
muito bom .. me ajudou a implementar um mais rápido que 24! bfs direcionais simples em js
RE60K
na verdade '____ll____bbll____dddd' deve ser '____ll____bbll____bbdddd'
RE60K
7

C, 366 - bônus ótimo de 50% = 183

char c[99],t[3][26]={"ZGONFZCPTEZBHUMZ","ZIQPHZRUGAZJWOCZ","ZACB@ZJHFDZKIGEZ"};r=20;f(int m,int n){int e,i,j;for(i=4;i--;){for(j=15;j--;)c[t[n][j+1]]=c[t[n][j]];c[m]="FRU"[n],c[m+1]="4'2 "[i],c[m+2]=0;for(e=0,j=68;j<76;j++) e+= (c[j]!=c[j+8]) + (c[j]!=c[j^1]);i&&e&&e<45-m*2&m<r?f(m+2,(n+1)%3),f(m+2,(n+2)%3):e||(puts(c),r=m);}}main(){scanf("%s",c+64);f(0,2),f(0,1),f(0,0);}

Usando a recursão, o programa pesquisa em uma árvore de até 11 movimentos (o comprimento máximo de uma solução ideal de acordo com http://en.wikipedia.org/wiki/Pocket_Cube e a página mencionada abaixo) e quando encontra uma solução o imprime (até 22 caracteres, rastreado pelo argumento da função m.) A ordem usada é um tipo de ordem de dicionário, em que todas as rotas que começam com U, U2, U 'são pesquisadas antes de qualquer rota que comece com R ou F. Portanto, ele não necessariamente encontra a solução ideal primeiro.

Quando uma solução é impressa, ela ré igual à mqual garante que somente soluções iguais ou menores serão impressas posteriormente. A colocação r=m-2custa 2 caracteres extras, mas garante que apenas uma solução de cada comprimento encontrado (até o ideal) seja impressa. Se você deseja que APENAS mostre a solução ideal, a melhor solução encontrada até agora deve ser armazenada em uma variável e a solução ideal impressa no final do programa (isso custaria cerca de 15 caracteres extras).

a entrada é lida na matriz c[]do índice 64 em diante. Isso é necessário para usar caracteres do alfabeto na tabela de movimentos. Os símbolos @through Wsão usados ​​em vez de A a X de acordo com a pergunta, porque é necessário iniciar um número par para fazer o teste das soluções funcionar. c['Z']também é usado para armazenamento temporário, porque, para executar a rotação de quatro vezes, são necessárias 5 atribuições. Como a primeira parte de c[]não é utilizada, ela está disponível para armazenar a solução (que é finalizada com um byte zero, como todas as seqüências C.)

for(i..)passa por uma sequência de 4 quartos da face especificada por n.

O primeiro for(j..)realiza a troca real de acordo com a tabela t[].

Para testar se o cubo está resolvido, é necessário apenas verificar as quatro faces laterais. As peças URF e DFR podem ser diferenciadas mesmo com os adesivos U e D removidos, porque uma peça lê XRF no sentido horário e a outra XFR. Se as duas peças forem trocadas para que U apareça na face inferior e vice-versa, a cor F aparecerá na face direita e vice-versa.

O segundo for(j..)conta o número de incompatibilidades nas quatro faces laterais. Por exemplo, na face frontal, ele compara G&O, H&P e G&H (duas vezes). Se e== 0, o cubo está resolvido. Se e<9 ou e<13, pode ser possível resolver o cubo no próximo movimento ou 2 movimentos, respectivamente. Caso contrário, definitivamente não é possível resolver o cubo nesse número de movimentos. Para economizar tempo, essa heurística é usada para podar a árvore de pesquisa e evitar o desperdício de tempo em muitos dos ramos de profundidade 10 ou 11 que não terão êxito. Expressa como uma fórmula, isso se torna e<45-m*2.

Código Ungolfed

char c[99],t[3][26]={"ZGONFZCPTEZBHUMZ","ZIQPHZRUGAZJWOCZ","ZACB@ZJHFDZKIGEZ"};
r=20;                                                       //All moves are output as 2 characters. The index of the last move of the longest solution (11 moves) shall be 20.

f(int m,int n){                                             //perform a cycle through four 1/4 turns of the face specified in n. The index of the move reported in the solution is m.
  int e,i,j;                                                //e is for counting mismatches. i loops through the four 1/4 turns. j performs other functions.
  for(i=4;i--;){

    for(j=15;j--;)c[t[n][j+1]]=c[t[n][j]];                  //A 1/4 turn is performed as three 4-sticker rotations of the type z=a;a=b;b=c;c=d;d=z using the data in the movetable t[][]

    c[m]="FRU"[n],c[m+1]="4'2 "[i],c[m+2]=0;                //Write to the output in c[] the face to be turned and the number of 1/4 turns. Terminate with a zero byte to overwrite any longer solution that may have been found before. 

    for(e=0,j=68;j<76;j++)e+=(c[j]!=c[j+8])+(c[j]!=c[j^1]); //Compare each sticker of the top row of the side faces (64+4 through 64+11) with the stickers below and beside it. Count the number of mismatches.

    i && e && e<45-m*2 & m<r?                               //if the number of 1/4turns is not 4 AND the cube is not solved AND the heuristic (as described in the text) is good AND a shorter solution has not already been found,
      f(m+2,(n+1)%3), f(m+2,(n+2)%3):                       //deepen the search to another faceturn of the other two faces. 
      e||(puts(c),r=m);                                     //otherwise, if a solution has been found, print the solution and reduce the value of r to the new max solution length.
  } 
}

main(){
  scanf("%s",c+64);                                         //scan in the current cube state to c[] at index 64.
  f(0,2),f(0,1),f(0,0);                                     //call f() three times to search for solutions beginning with U R and F.
}

atuação

O programa foi testado com os padrões de 1 a 13 em http://www.jaapsch.net/puzzles/cube2.htm

Os resultados a seguir fornecem o tempo na minha máquina para encontrar TODAS as soluções ideais (para os curiosos). Também para as posições mais complexas, o tempo é dado para a modificação de 2 bytes mencionada acima, que encontra apenas uma solução ideal. Para esse tempo, são fornecidos ambos, para encontrar a primeira solução e para o programa terminar. As soluções fornecidas (que geralmente são diferentes das soluções obtidas pela reversão dos geradores na página vinculada) foram verificadas com um simulador de cubo on-line.

U 4 (1 move) horizontal flags (not mirror symmetric)
1 solution 1 sec

U2 (1 move) 4 horizontal flags (mirror symmetric)
1 solution 1 sec

F2 R2 F2 (3 moves) 4 vertical flags  
UUUULRBFRLFBLRBFRLFBDDDD 2 solutions 1 sec

U2 F2 R2 U2 (4 moves) Supertwist; 6 flags
DDUURRBFRRFBLLBFLLFBUUDD 3 solutions 1 sec

U F2 U2 R2 U (5 moves) 4 vertical flags, 2 checkerboards
UDDULBRFRFLBLBRFRFLBUDDU 2 solutions 1 sec

R2 F2 R2 U2 (4 moves) 4 checkerboards
UUUURLFBLRBFLRBFRLFBDDDD 4 solutions 1 sec

R U2 R' F2 R U' R2 U F2 U' (10 moves) Cube in cube
FFFUDDRFRULLLDRRUULBBBDB 18 solutions 26 sec; 1 solution U F2U'R2U R'F2R U2R' 1,13 sec 

R F U' R2 U F' R U F2 R2 (10 moves) Cube in cube 2
DDDUFFLFRBRRLFLLBBRBUUDU 8 solutions 28 sec; 1 solution R F U'R2U F'R U F2R2 12,21 sec 

U R F2 U R F2 R U F' R (10 moves)3-Cycle
UFFULDRFRULBLLFRURBBDBDD 45 solutions 26 sec; 1 solution U R'F U'F'R'F2U R F2 8,14 sec 

U R U' R2 U' R' F' U F2 R F' (11 moves) Column turn
UUUDLLFRFRBBLLFRFRBBDUDD many solutions 29 sec; 1 solution U R U'F U2R F'R'F'U2F' 3,27 sec 

F' U R' F2 U' R F U R2 U R' (11 moves)Corner swap
UUUURLFBLRBFLLFFRRBBDDDD 29 sec 24 solutions; 1 solution R U'F R U'R2U'F'R'U F2 12,28 sec

U F2 U' (3 moves) Zig-zag 
UDUDLLFRFFLBLBRRFRBBUUDD 1 solution 1 sec 

U' F2 U2 R2 U' F2 U2 R2 U' (9 moves) 2 Checkerboards, 4 L
DUUDLLFBRRBFLRFFRLBBUDDU 8 solutions 13 sec; 1 solution U F2U2R2U R2U2F2U' 1,5 sec
Level River St
fonte
Parece bom. Eu adoraria ver uma competição fechada aqui.
Kyle McCormick
@KyleMcCormick Meu programa finalmente terminou e está funcionando bem, mas vejo que você se cansou de esperar e aceitou a outra resposta. É muito melhor do que o meu post de 2 dias atrás, que teve um bug (rostos estão virando na direção errada.) Além disso, a aplicação da heurística em 2 níveis melhorou a velocidade. Ele ainda gera várias soluções, mas a última solução é garantida como ideal (mais sobre possíveis alterações de saída no texto). É muito mais curta que a outra submissão. Se você tiver algum problema no formato de saída, entre em contato.
Level River St
358 bytes através de tacos básicos.
MD XF