Risolvi il cubo tascabile (Rubiks)


16

Il tuo compito

.. è fare ciò che apparentemente Brian Fantana non potrebbe fare, e risolvere il cubo di Rubik 2x2x2.

cubo tascabile - anchorman

Lo schema

- -   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 ti verrà dato tramite stdin o la riga di comando (a tua scelta - specifica nella tua risposta) nel formato:

ABCDEFGHIJKLMNOPQRSTUVWX

Si noti che AD compone la faccia a U (in alto), EFMN compone la faccia a L (a sinistra), GHOP compone la faccia a F (anteriore), IJQR compone la faccia a R (a destra), KLST compone la B-face (retro) e UX compongono la D-face (giù).

Ci saranno sei caratteri unici che rappresentano ciascun colore, ma possono variare, quindi preparati a qualsiasi combinazione di 6 caratteri ASCII da utilizzare per ciascun colore.

specificazioni

  • Il codice dovrebbe generare una soluzione usando solo le facce Destra (R), Superiore (U) e Anteriore (F) e dovrebbe usare la notazione standard: R, R ', R2, U, U', U2, F, F ', F2. Puoi trovare maggiori informazioni qui . La restrizione al sottoinsieme RUF è standard per il cubo 2x2 (Suggerimento: considera l'angolo inferiore posteriore-sinistro come una base fissa su cui lavorare).
  • Il codice deve essere in grado di risolvere tutte le possibili permutazioni del cubo tascabile.
  • Il completamento di ogni soluzione dovrebbe richiedere meno di 30 secondi.
  • Ogni soluzione dovrebbe essere inferiore a 30 mosse.
  • Un bonus del -20% verrà dato per le soluzioni che forniscono sempre risposte inferiori a 20 mosse (pubblicizzalo nella tua risposta in modo che io possa fare un controllo approfondito)
  • Un bonus del -50% verrà dato per il codice che fornisce sempre una soluzione ottimale. - Ancora una volta, per favore pubblicizza nella tua risposta
  • Le soluzioni non devono contenere due mosse consecutive sulla stessa faccia, poiché possono essere facilmente combinate in una mossa.
  • Le soluzioni possono facoltativamente contenere un singolo spazio - e solo un singolo spazio - tra ogni mossa.
  • L'intera sequenza della soluzione, se necessario, può essere contenuta in una coppia di parentesi, virgolette, parentesi graffe, parentesi quadre o punti di inserimento, ma non sono consentite altre uscite estranee.
  • Fornisci una versione brevemente commentata del codice o una spiegazione approfondita del codice.
  • Nessun uso di file esterni. Ciò include Internet, tabelle di dati e librerie / pacchetti creati per questo tipo di problema.
  • Vince il codice più breve per numero di byte.
  • Vincitore scelto mercoledì (30 luglio 2014).

20
Abbiamo 2x2, 3x3 e 4x4 , ma sto ancora aspettando la sfida 1x1 per avere la possibilità di brillare. Ho un algoritmo perfetto!
Maniglia della porta

Ecco un risolutore di ~ 500 caratteri in K, che genera anche la soluzione ottimale (= la più breve): speedolving.com/forum/…
Jakube

30 secondi dovrebbero essere sufficienti per forzare la forza usando Dijkstra: ci sono solo 3674160 posizioni.
Peter Taylor,

2
1. Presumo che non ci siano restrizioni sullo spazio bianco nell'output 2. Per essere oggettivi, è necessario definire il bonus per soluzioni inferiori a 20 mosse invece di lasciarlo come "discrezionale".
Level River St

@steveverrill Risolto il problema. Aggiunte anche le specifiche degli spazi bianchi. Grazie!
Kyle McCormick,

Risposte:


11

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

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 sostituisce le schede con più spazi bianchi. Così tecnica questa versione ha 549 byte. Sostituisci i primi due spazi nelle righe 6-10 con un tabulatore.

Idea alla base del mio programma: la mia prima idea è stata una prima ricerca di respiro. Ma ci è voluto troppo tempo. Circa 2 minuti per uno scramble difficile (11 mosse ottimali). Quindi ho deciso di affrontare il problema da entrambe le parti. Uso due set. Genero in sequenza tutti gli stati con distanza 1,2,3, ... nello scramble e li salvo in set1, e allo stesso tempo tutti gli stati con distanza 1,2,3, ... nello stato risolto e li salvo in set2. La prima volta che uno stato è in entrambi i set, abbiamo trovato la soluzione.

Per questo ho bisogno dei colori del cubo risolto, che non sono noti. I caratteri 13, 20 e 23 definiscono il colore sinistro, posteriore e inferiore. Ma questi 3 colori sono sufficienti per rappresentare il cubo. Sostituisco semplicemente gli altri 3 colori con spazi bianchi e posso rappresentare il mio stato risolto come '____ll____bbll____dddd'.

Oh, e per abbreviare le permutazioni ho usato un'idea da /codegolf//a/34651/29577

Versione non golfata:

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

Sono abbastanza contento del risultato, perché sono abbastanza nuovo in Python. Questo è uno dei miei primi programmi Python.

modifica: sei mesi dopo: 427-50% = 213,5

Ho un po 'più di esperienza in Python e nel golf. Quindi ho rivisto il mio codice originale e ho potuto salvare più di 100 caratteri.

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

Fondamentalmente uso lo stesso identico approccio. Il cambiamento più grande è che non definisco più una funzione. Invece di

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)

posso fare

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

Inoltre ho cambiato leggermente la mossa lamda. Prima accorciare, quindi integrare direttamente il codice, poiché la chiamata di funzione viene visualizzata una sola volta.

Tengo per ogni stato un elenco di numeri tra 0 e 11, per rappresentare le mosse, anziché una stringa contenente le mosse. I numeri vengono convertiti alla fine.

Inoltre ho combinato i due for-loop 'for k in r(3):for j in r(3):in uno for y in r(12). Quindi devo anche fare le mosse U4, R4, F4. Naturalmente una tale mossa non appare nella soluzione più breve, quindi " 2'"[x%4]funziona. (Se x % 4 == 3, ci sarebbe un indice fuori intervallo di eccezione)

È anche un po 'più veloce, poiché cerco la voce nel secondo set in precedenza. Circa 0,5 secondi per una soluzione a 11 mosse.


2
Eseguito l'upgrade per l'utilizzo di bfs bidirezionale - il mio algoritmo di ricerca preferito (accanto a IDA *). Se il tempo lo consente, lo testerò tra qualche ora per ottimalità. Inoltre, non mi rendevo conto che non hai davvero bisogno dei colori U / R / F per risolvere il puzzle. Ben fatto!
Kyle McCormick,

Passato per i miei 20 casi di test per correttezza e ottimalità.
Kyle McCormick,

molto bello .. mi ha aiutato a implementare un più veloce di 24! bfs monodirezionale in js
RE60K

in realtà '____ll____bbll____dddd' dovrebbe essere '____ll____bbll____bbdddd'
RE60K

7

C, 366 - bonus ottimale del 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 la ricorsione, il programma cerca attraverso un albero fino a 11 mosse in profondità (la lunghezza massima di un soluton ottimale secondo http://en.wikipedia.org/wiki/Pocket_Cube e la pagina menzionata di seguito) e quando trova una soluzione lo stampa (lungo fino a 22 caratteri, seguito dall'argomento della funzione m). L'ordine utilizzato è un tipo di ordine del dizionario, in cui vengono cercati tutti i percorsi che iniziano con U, U2, U 'prima di cercare qualsiasi percorso che inizia con R o F. Pertanto, non trova necessariamente prima la soluzione ottimale.

Quando una soluzione viene stampata, rviene resa uguale alla mquale garantisce che in seguito vengano stampate solo soluzioni uguali o più brevi. Mettere r=m-2costa 2 caratteri in più ma garantisce che venga stampata una sola soluzione per ogni lunghezza trovata (fino all'ottimale). Se vuoi che mostri SOLO la soluzione ottimale, la soluzione migliore trovata finora deve essere memorizzata in una variabile e la soluzione ottimale stampata alla fine del programma (ciò costerebbe circa 15 caratteri in più).

l'input viene letto nell'array c[]dall'indice 64 in poi. Ciò è necessario per utilizzare i caratteri alfabetici nel mobile. I simboli @through Wvengono utilizzati anziché da A a X per la domanda, poiché è necessario iniziare con un numero pari per far funzionare il test per le soluzioni. c['Z']viene utilizzato anche per l'archiviazione temporanea, perché per eseguire la rotazione quadrupla sono necessarie 5 assegnazioni in totale. Poiché la prima parte di c[]non viene utilizzata, è disponibile per archiviare la soluzione (che termina con un byte zero, come tutte le stringhe C.)

for(i..)passa attraverso una sequenza di 4 quarti di giro del viso specificato da n.

Il primo for(j..)esegue lo scambio effettivo secondo la tabella t[].

Per verificare se il cubo è risolto, è necessario solo controllare le quattro facce laterali. I pezzi URF e DFR possono essere distinti anche con gli adesivi U e D rimossi, poiché un pezzo legge XRF in senso orario e l'altro XFR. Se i due pezzi vengono scambiati in modo che U sia visibile sulla faccia in giù e viceversa, il colore F apparirà sulla faccia a destra e viceversa.

Il secondo for(j..)conta il numero di disallineamenti sulle quattro facce laterali. Ad esempio, per la faccia anteriore confronta G & O, H & P e G & H (due volte). Se e== 0, il cubo viene risolto. Se e<9 o e<13, potrebbe essere possibile risolvere il cubo rispettivamente nella successiva o 2 mosse successive. Altrimenti non è assolutamente possibile risolvere il cubo in questo numero di mosse. Per risparmiare tempo, questa euristica viene utilizzata per potare l'albero di ricerca ed evitare di perdere tempo su molti di quei rami di profondità 10 o 11 che non avranno successo. Espresso come una formula, questo diventa e<45-m*2.

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

Prestazione

Il programma è stato testato con schemi da 1 a 13 a http://www.jaapsch.net/puzzles/cube2.htm

I risultati seguenti forniscono i tempi sulla mia macchina per trovare TUTTE le soluzioni ottimali (per i curiosi). Anche per le posizioni più complesse, i tempi sono indicati per la modifica a 2 byte sopra menzionata che trova solo una soluzione ottimale. Per questo i tempi sono indicati sia per trovare la prima soluzione sia per far terminare il programma. Le soluzioni fornite (che sono generalmente diverse dalle soluzioni ottenute invertendo i generatori nella pagina collegata) sono state verificate con un simulatore di cubi online.

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

Suona bene. Mi piacerebbe vedere una competizione ravvicinata qui.
Kyle McCormick,

@KyleMcCormick Il mio programma è finalmente finito e funziona bene ma vedo che ti sei stancato di aspettare e ho accettato l'altra risposta. È molto meglio del mio post di 2 giorni fa che aveva un bug (le facce giravano nel modo sbagliato). Inoltre, applicare l'euristica a 2 livelli ha migliorato la velocità. Produce ancora diverse soluzioni, ma l'ultima soluzione è garantita (più sulle possibili modifiche dell'output nel testo). È molto più breve dell'altra presentazione. Se hai problemi con il formato di output fammelo sapere.
Level River St

358 byte tramite golf di base.
MD XF,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.