Resuelve el cubo de bolsillo (Rubik)

16

Tu tarea

.. es hacer lo que Brian Fantana aparentemente no pudo hacer, y resolver el Cubo de Rubik 2x2x2.

cubo de bolsillo - presentador

El diseño

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

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

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

Y se le dará a través de stdin o la línea de comando (su elección, especifique en su respuesta) en el formato:

ABCDEFGHIJKLMNOPQRSTUVWX

Tenga en cuenta que AD conforma la cara U (arriba), EFMN conforma la cara L (izquierda), GHOP conforma la cara F (frente), IJQR conforma la cara R (derecha), KLST conforma la Cara B (parte posterior) y UX conforman la cara D (abajo).

Habrá seis caracteres únicos que representan cada color, pero pueden variar, así que prepárese para cualquier combinación de 6 caracteres ascii que se utilizarán para cada color.

Especificaciones

  • Su código debe generar una solución usando solo las caras Derecha (R), Superior (U) y Frontal (F), y debe usar la notación estándar: R, R ', R2, U, U', U2, F, F ', F2. Puedes encontrar más información aquí . La restricción al subconjunto RUF es estándar para el cubo 2x2 (Sugerencia: trate la esquina inferior-posterior-izquierda como una base fija para trabajar).
  • Su código debe ser capaz de resolver todas las permutaciones posibles del cubo de bolsillo.
  • Cada solución debe tomar menos de 30 segundos en completarse.
  • Cada solución debe tener menos de 30 movimientos.
  • Se otorgará un bono de -20% para las soluciones que siempre brindan respuestas de menos de 20 movimientos (por favor, publíquelo en su respuesta para que pueda verificarlo a fondo)
  • Se otorgará una bonificación de -50% para el código que siempre proporciona una solución óptima. - De nuevo, por favor anuncie en su respuesta
  • Las soluciones no deben contener dos movimientos consecutivos en la misma cara, ya que se pueden combinar fácilmente en un solo movimiento.
  • Las soluciones pueden contener opcionalmente un solo espacio, y solo un solo espacio, entre cada movimiento.
  • La secuencia completa de la solución, si es necesario, puede estar contenida entre paréntesis, comillas, llaves, corchetes o puntos, pero no se permite ningún otro resultado extraño.
  • Proporcione una versión brevemente comentada de su código o una explicación detallada de su código.
  • Sin uso de archivos externos. Esto incluye Internet, tablas de datos y bibliotecas / paquetes creados para este tipo de problema.
  • El código más corto por conteo de bytes gana.
  • Ganador elegido el miércoles (30 de julio de 2014).
Kyle McCormick
fuente
20
Tenemos 2x2, 3x3 y 4x4 , pero todavía estoy esperando el desafío 1x1 para tener mi oportunidad de brillar. Tengo un algoritmo perfecto!
Pomo de la puerta
Aquí hay un solucionador de carácter ~ 500 en K, que genera la solución aún óptima (= más corto): speedsolving.com/forum/...
Jakube
30 segundos deberían ser suficientes para forzarlo utilizando Dijkstra: solo hay 3674160 posiciones.
Peter Taylor
2
1. Supongo que no hay restricciones en el espacio en blanco en la salida 2. Para ser objetivo, debe definir el bono para soluciones de menos de 20 movimientos en lugar de dejarlo como "discrecional".
Level River St
@steveverrill lo arregló. También se agregó la especificación de espacios en blanco. ¡Gracias!
Kyle McCormick

Respuestas:

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)

Stackexchange reemplaza las pestañas con múltiples espacios en blanco. Tan técnica que esta versión tiene 549 bytes. Simplemente reemplace los dos primeros espacios en las líneas 6-10 con un tabulador.

Idea detrás de mi programa: mi primera idea fue una primera búsqueda de aliento. Pero esto tomó demasiado tiempo. Alrededor de 2 minutos para una lucha difícil (11 movimientos óptimos). Entonces decidí abordar el problema desde ambos lados. Yo uso dos juegos. Genero secuencialmente todos los estados con distancia 1,2,3, ... a la codificación y los guardo en el set1, y al mismo tiempo todos los estados con distancia 1,2,3, ... al estado resuelto y los guardo en set2. La primera vez que un estado está en ambos conjuntos, encontramos la solución.

Para esto necesito los colores del cubo resuelto, que no se conocen. Los caracteres 13, 20 y 23 definen el color izquierdo, posterior y descendente. Pero estos 3 colores son suficientes para representar el cubo. Simplemente reemplazo los otros 3 colores con espacios en blanco y puedo representar mi estado resuelto como '____ll____bbll____dddd'.

Ah, y para acortar las permutaciones, utilicé una idea de /codegolf//a/34651/29577

Versión sin golf:

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

Estoy bastante contento con el resultado, porque soy bastante nuevo en Python. Este es uno de mis primeros programas de Python.

editar: medio año después: 427 - 50% = 213.5

Tengo un poco más de experiencia en Python y en golf. Así que revisé mi código original y pude guardar más 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])

Básicamente uso exactamente el mismo enfoque. El mayor cambio es que ya no defino una función. En lugar 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)

puedo hacer

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

También cambié un poco el movimiento lamda. Primero acorte, y luego integre el código directamente, ya que la llamada a la función solo aparece una vez.

Mantengo para cada estado una lista de números entre 0 y 11, para representar los movimientos, en lugar de una cadena que contiene los movimientos. Los números se convierten al final.

También combiné los dos bucles for 'for k in r(3):for j in r(3):en uno for y in r(12). Por lo tanto, también tengo que hacer los movimientos U4, R4, F4. Por supuesto, tal movimiento no aparece en la solución más corta, por lo que " 2'"[x%4]funciona. (Si x % 4 == 3hubiera una excepción de índice fuera de rango)

También es un poco más rápido, ya que busco la entrada en el segundo set anterior. Aproximadamente 0.5 segundos para una solución de 11 movimientos.

Jakube
fuente
2
Votaron por usar bfs bidireccionales: mi algoritmo de búsqueda favorito (junto a IDA *). Si el tiempo lo permite, lo probaré en unas pocas horas para obtener la optimización. Además, no me di cuenta de que realmente no necesitas los colores U / R / F para resolver el rompecabezas. ¡Bien hecho!
Kyle McCormick
Aprobé mis 20 casos de prueba de corrección y optimización.
Kyle McCormick
muy agradable .. me ayudó a implementar un más rápido que 24! bfs direccionales individuales en js
RE60K
en realidad '____ll____bbll____dddd' debería ser '____ll____bbll____bbdddd'
RE60K
7

C, 366 - 50% de bonificación óptima = 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 la recursividad, el programa busca a través de un árbol de hasta 11 movimientos de profundidad (la longitud máxima de una solución óptima de acuerdo con http://en.wikipedia.org/wiki/Pocket_Cube y la página mencionada a continuación) y cuando encuentra una solución lo imprime (hasta 22 caracteres de largo, seguido por el argumento de la función m). El orden utilizado es una especie de orden de diccionario, donde se buscan todas las rutas que comienzan con U, U2, U 'antes de buscar cualquier ruta que comience con R o F. Por lo tanto, no necesariamente encuentra la solución óptima primero.

Cuando se imprime una solución, rse hace igual a lo mque garantiza que solo se imprimirán soluciones iguales o más cortas después. La colocación r=m-2cuesta 2 caracteres adicionales, pero garantiza que solo se imprima una solución de cada longitud encontrada (hasta la óptima). Si desea que SOLO muestre la solución óptima, la mejor solución encontrada hasta ahora debe almacenarse en una variable, y la solución óptima impresa al final del programa (esto costaría unos 15 caracteres adicionales).

la entrada se lee en la matriz c[]desde el índice 64 en adelante. Esto es necesario para usar caracteres alfabéticos en la tabla móvil. Los símbolos a @través Wse utilizan en lugar de A a X según la pregunta, porque es necesario comenzar en un número par para que la prueba de soluciones funcione. c['Z']también se usa para almacenamiento temporal, porque para realizar una rotación de 4 veces se necesitan un total de 5 tareas. Debido a que la primera parte de c[]no se utiliza, está disponible para almacenar la solución (que termina con un byte cero, como todas las cadenas C).

for(i..)pasa por una secuencia de 4 cuartos de vuelta de la cara especificada por n.

El primero for(j..)realiza el intercambio real de acuerdo con la tabla t[].

Para probar si el cubo está resuelto, solo es necesario verificar las cuatro caras laterales. Las piezas URF y DFR se pueden distinguir incluso con las pegatinas U y D quitadas, porque una pieza lee XRF en el sentido de las agujas del reloj y la otra XFR. Si las dos piezas se intercambian de modo que U se muestre en la cara inferior y viceversa, el color F se mostrará en la cara derecha y viceversa.

El segundo for(j..)cuenta el número de desajustes en las cuatro caras laterales. Por ejemplo, para la cara frontal, compara G y O, H y P y G y H (dos veces). Si e== 0, el cubo está resuelto. Si e<9 o e<13, podría ser posible resolver el cubo en el siguiente movimiento o 2 movimientos respectivamente. De lo contrario, definitivamente no es posible resolver el cubo en este número de movimientos. Para ahorrar tiempo, esta heurística se utiliza para podar el árbol de búsqueda y evitar perder tiempo en muchas de las ramas de profundidad 10 u 11 que no tendrán éxito. Expresado como una fórmula, esto se convierte e<45-m*2.

Código sin golf

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

Actuación

El programa se probó con los patrones 1 a 13 en http://www.jaapsch.net/puzzles/cube2.htm

Los resultados de la siguiente manera dan el tiempo en mi máquina para encontrar TODAS las soluciones óptimas (para los curiosos). También para las posiciones más complejas, se da el tiempo para la modificación de 2 bytes mencionada anteriormente que encuentra una sola solución óptima. Para esto, se dan tiempos para encontrar la primera solución y para que el programa finalice. Las soluciones dadas (que generalmente son diferentes a las soluciones obtenidas al invertir los generadores en la página vinculada) se han verificado con un simulador de cubos en línea.

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
fuente
Suena bien. Me encantaría ver una competencia cerrada aquí.
Kyle McCormick
@KyleMcCormick Mi programa finalmente ha terminado y funciona bien, pero veo que te cansaste de esperar y aceptaste la otra respuesta. Es mucho mejor que mi publicación de hace 2 días que tenía un error (las caras giraban en sentido contrario). Además, aplicar la heurística a 2 niveles ha mejorado la velocidad. Todavía genera varias soluciones, pero se garantiza que la última solución será óptima (más sobre posibles cambios de salida en el texto). Es mucho más corta que la otra presentación. Si tiene algún problema con el formato de salida, hágamelo saber.
Level River St
358 bytes a través de campos de golf básicos.
MD XF