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).

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.


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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.