Löse den (Rubiks) Pocket Cube

16

Deine Aufgabe

.. soll das tun, was Brian Fantana anscheinend nicht konnte, und den 2x2x2 Rubik's Cube lösen.

Taschenwürfel - Ankermann

Das 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   - -   - -

Und wird Ihnen über stdin oder die Kommandozeile (Ihre Wahl - bitte in Ihrer Antwort angeben) in folgendem Format mitgeteilt:

ABCDEFGHIJKLMNOPQRSTUVWX

Beachten Sie, dass AD die U-Seite (up), EFMN die L-Seite (left), GHOP die F-Seite (front), IJQR die R-Seite (right) und KLST die B-Gesicht (Rückseite) und UX bilden das D-Gesicht (nach unten).

Es gibt sechs eindeutige Zeichen für jede Farbe, die jedoch variieren können. Bereiten Sie sich also auf eine beliebige Kombination von 6 ASCII-Zeichen vor, die für jede Farbe verwendet werden sollen.

Spezifikationen

  • Ihr Code sollte eine Lösung nur mit der rechten (R), oberen (U) und vorderen (F) Seite ausgeben und die Standardnotation verwenden: R, R ', R2, U, U', U2, F, F ', F2. Weitere Infos finden Sie hier . Die Beschränkung auf die RUF-Teilmenge ist Standard für den 2x2-Würfel (Hinweis: Behandeln Sie die linke untere Ecke als feste Basis, von der aus Sie arbeiten können).
  • Ihr Code muss in der Lage sein, alle möglichen Permutationen des Taschenwürfels zu lösen.
  • Jede Lösung sollte weniger als 30 Sekunden dauern.
  • Jede Lösung sollte weniger als 30 Züge umfassen.
  • Ein Bonus von -20% wird für Lösungen gewährt, die Antworten mit weniger als 20 Zügen enthalten. (Bitte geben Sie dies in Ihrer Antwort an, damit ich eine gründliche Überprüfung durchführen kann.)
  • Für Code, der immer eine optimale Lösung bietet, wird ein Bonus von -50% gewährt. - Bitte bewerben Sie sich erneut in Ihrer Antwort
  • Die Lösungen dürfen nicht zwei aufeinanderfolgende Züge auf derselben Seite enthalten, da sie leicht zu einem Zug kombiniert werden können.
  • Lösungen können optional ein einzelnes - und nur ein einziges - Leerzeichen zwischen den einzelnen Zügen enthalten.
  • Bei Bedarf kann die gesamte Lösungssequenz in Klammern, Anführungszeichen, geschweiften Klammern, Klammern oder Carets angegeben werden. Es ist jedoch keine andere externe Ausgabe zulässig.
  • Bitte geben Sie entweder eine kurz kommentierte Version Ihres Codes oder eine ausführliche Erklärung Ihres Codes an.
  • Keine Verwendung von externen Dateien. Dies schließt Internet, Datentabellen und Bibliotheken / Pakete ein, die für diese Art von Problem erstellt wurden.
  • Die kürzeste Anzahl von Codes pro Byte gewinnt.
  • Der Gewinner wurde am Mittwoch (30. Juli 2014) ausgewählt.
Kyle McCormick
quelle
20
Wir haben 2x2, 3x3 und 4x4 , aber ich warte immer noch auf die 1x1-Herausforderung, damit ich glänzen kann. Ich habe einen perfekten Algorithmus!
Türklinke
Hier ist ein ~ 500-Zeichen-Löser in K, der sogar die optimale (= kürzeste) Lösung generiert
Jakube
30 Sekunden sollten ausreichen, um es mit Dijkstra brutal zu erzwingen: Es gibt nur 3674160 Positionen.
Peter Taylor
2
1. Ich gehe davon aus, dass die Ausgabe keine Beschränkungen für Leerzeichen enthält. 2. Um objektiv zu sein, sollten Sie den Bonus für Lösungen mit weniger als 20 Zügen definieren, anstatt ihn als "Ermessensspielraum" zu belassen.
Level River St
@steveverrill Es wurde behoben. Außerdem wurde die Whitespace-Spezifikation hinzugefügt. Vielen Dank!
Kyle McCormick

Antworten:

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 ersetzt Tabs durch mehrere Leerzeichen. Also technisch hat diese Version 549 Bytes. Ersetzen Sie einfach die ersten beiden Leerzeichen in den Zeilen 6-10 durch einen Tabulator.

Idee hinter meinem Programm: Meine erste Idee war eine erste Suche. Aber das hat zu lange gedauert. Etwa 2 Minuten für ein hartes (11 Züge optimales) Scramble. Deshalb habe ich mich entschlossen, das Problem von beiden Seiten anzusprechen. Ich benutze zwei Sätze. Ich generiere nacheinander alle Zustände mit Abstand 1,2,3, ... zum Scramble und speichere sie in set1, und gleichzeitig alle Zustände mit Abstand 1,2,3, ... zum gelösten Zustand und speichere sie in set2. Wenn sich ein Zustand zum ersten Mal in beiden Mengen befindet, haben wir die Lösung gefunden.

Dafür brauche ich die Farben des gelösten Würfels, die nicht bekannt sind. Die Zeichen 13, 20 und 23 definieren die linke, hintere und untere Farbe. Diese 3 Farben reichen jedoch aus, um den Würfel darzustellen. Ich ersetze einfach die anderen 3 Farben durch Leerzeichen und kann meinen gelösten Zustand als '____ll____bbll____dddd' darstellen.

Oh, und zum Verkürzen der Permutationen habe ich eine Idee von /codegolf//a/34651/29577 verwendet

Ungolfed-Version:

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

Ich bin ziemlich zufrieden mit dem Ergebnis, weil ich Python noch nicht kenne. Dies ist eines meiner ersten Python-Programme.

Bearbeiten: Ein halbes Jahr später: 427 - 50% = 213,5

Habe ein bisschen mehr Erfahrung in Python und im Golfen. Also habe ich meinen ursprünglichen Code überarbeitet und konnte mehr als 100 Zeichen speichern.

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

Ich gehe grundsätzlich genauso vor. Die größte Änderung ist, dass ich keine Funktion mehr definiere. Anstatt von

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)

ich kann

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

Auch ich habe den Umzug lamda ein wenig geändert. Zuerst verkürzen und dann den Code direkt einbinden, da der Funktionsaufruf nur einmal erscheint.

Ich behalte für jeden Zustand eine Liste von Zahlen zwischen 0 und 11, um die Züge darzustellen, anstelle einer Zeichenkette, die die Züge enthält. Die Zahlen werden ganz am Ende umgerechnet.

Außerdem habe ich die beiden for-Schleifen 'for k in r(3):for j in r(3):zu einer kombiniert for y in r(12). Deshalb muss ich auch die Umzüge machen U4, R4, F4. Natürlich erscheint ein solcher Zug nicht in der kürzesten Lösung, " 2'"[x%4]funktioniert also. (Wenn x % 4 == 3es eine Ausnahme außerhalb des Bereichs für den Index geben würde)

Es ist auch ein bisschen schneller, da ich den Eintrag im zweiten Satz früher suche. Etwa 0,5 Sekunden für eine Lösung mit 11 Zügen.

Jakube
quelle
2
Upvoted for using bidirectional bfs - mein bevorzugter Suchalgorithmus (neben IDA *). Wenn es die Zeit erlaubt, werde ich es in ein paar Stunden auf Optimalität testen. Außerdem habe ich nicht bemerkt, dass Sie die U / R / F-Farben nicht wirklich benötigen, um das Rätsel zu lösen. Schön gemacht!
Kyle McCormick
Bestanden für meine 20 Testfälle für Richtigkeit und Optimalität.
Kyle McCormick
sehr nett .. hat mir geholfen eine schneller als 24 umzusetzen! Single Directional Bfs in js
RE60K
Tatsächlich sollte "____ll____bbll____dddd" "____ll____bbll____bbdddd" sein
RE60K
7

C, 366 - 50% optimaler Bonus = 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);}

Unter Verwendung der Rekursion durchsucht das Programm einen Baum mit einer Tiefe von bis zu 11 Zügen (die maximale Länge einer optimalen Lösung gemäß http://en.wikipedia.org/wiki/Pocket_Cube und der unten genannten Seite) und findet eine Lösung Es wird gedruckt (bis zu 22 Zeichen lang, nach Funktionsargument verfolgt m). Die verwendete Reihenfolge ist eine Art Wörterbuchreihenfolge, bei der alle Routen, die mit U, U2, U 'beginnen, durchsucht werden, bevor Routen durchsucht werden, die mit R oder F beginnen. Es muss also nicht unbedingt zuerst die optimale Lösung gefunden werden.

Wenn eine Lösung gedruckt wird, rwird gleich gemachtm wodurch sichergestellt wird, dass anschließend nur gleiche oder kürzere Lösungen gedruckt werden. Das Einfügen r=m-2kostet 2 zusätzliche Zeichen, stellt jedoch sicher, dass nur eine Lösung jeder gefundenen Länge (bis auf das Optimum) gedruckt wird. Wenn NUR die optimale Lösung angezeigt werden soll, muss die beste bisher gefundene Lösung in einer Variablen gespeichert und die optimale Lösung am Ende des Programms ausgedruckt werden (dies würde ca. 15 zusätzliche Zeichen kosten).

Die Eingabe wird c[]ab Index 64 in das Array eingelesen . Dies ist erforderlich, um Buchstaben in der Tabelle zu verwenden. Die Symbole @bis Wwerden in der Frage anstelle von A bis X verwendet, da es erforderlich ist, mit einer geraden Zahl zu beginnen, damit der Test für Lösungen funktioniert. c['Z']wird auch für die vorübergehende Speicherung verwendet, da für eine 4-fache Drehung insgesamt 5 Zuordnungen erforderlich sind. Weil der erste Teil vonc[] nicht verwendet wird, kann die Lösung gespeichert werden (die wie alle C-Zeichenfolgen mit einem Null-Byte abgeschlossen wird).

for(i..) durchläuft eine Folge von 4 viertel Umdrehungen des durch angegebenen Gesichts n .

Der erste for(j..)führt den eigentlichen Austausch gemäß Tabelle durcht[] .

Um zu testen, ob der Würfel gelöst ist, müssen nur die vier Seitenflächen überprüft werden.Die Teile URF und DFR können auch bei entfernten Aufklebern U und D voneinander unterschieden werden, da bei einem Teil RFA im Uhrzeigersinn und bei dem anderen Teil RFA angezeigt wird. Wenn die beiden Teile so vertauscht sind, dass U auf der Unterseite und umgekehrt angezeigt wird, wird die Farbe F auf der rechten Seite und umgekehrt angezeigt.

Die Sekunde for(j..)zählt die Anzahl der Fehlpaarungen auf den vier Seitenflächen. Zum Beispiel vergleicht es für die Vorderseite G & O, H & P und G & H (zweimal). Wenn e== 0, wird der Würfel gelöst. Wenn e<9 oder e<13, kann der Würfel möglicherweise im nächsten Zug oder in zwei Zügen gelöst werden. Andernfalls ist es definitiv nicht möglich, den Würfel in dieser Anzahl von Zügen zu lösen. Um Zeit zu sparen, wird diese Heuristik verwendet, um den Suchbaum zu beschneiden und Zeitverschwendung bei vielen Zweigen der Tiefe 10 oder 11 zu vermeiden, die nicht erfolgreich sind. Ausgedrückt als Formel wird dies e<45-m*2.

Ungolfed Code

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

Performance

Das Programm wurde mit den Mustern 1 bis 13 unter http://www.jaapsch.net/puzzles/cube2.htm getestet

Die folgenden Ergebnisse geben das Timing auf meiner Maschine an, um ALLE optimalen Lösungen zu finden (für Neugierige). Auch für die komplexeren Positionen wird das Timing für die oben erwähnte 2-Byte-Modifikation angegeben, die nur eine optimale Lösung findet. Hierfür werden sowohl Zeitangaben gemacht, um die erste Lösung zu finden, als auch um das Programm zu beenden. Die angegebenen Lösungen (die sich im Allgemeinen von den Lösungen unterscheiden, die durch Umkehren der Generatoren auf der verknüpften Seite erhalten wurden) wurden mit einem Online-Würfelsimulator überprüft.

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
quelle
Hört sich gut an. Ich würde gerne einen engen Wettbewerb hier sehen.
Kyle McCormick
@KyleMcCormick Mein Programm ist endlich fertig und läuft gut, aber ich sehe, du hast es satt zu warten und hast die andere Antwort angenommen. Es ist viel besser als mein Beitrag vor 2 Tagen, bei dem ein Fehler aufgetreten ist (Gesichter drehen sich in die falsche Richtung). Außerdem hat das Anwenden der Heuristik auf 2 Ebenen die Geschwindigkeit verbessert. Es werden immer noch mehrere Lösungen ausgegeben, aber die letzte Lösung ist garantiert optimal (mehr zu möglichen Änderungen der Ausgabe im Text). Sie ist viel kürzer als die andere Einreichung. Wenn Sie Probleme mit dem Ausgabeformat haben, lassen Sie es mich wissen.
Level River St
358 Bytes über Basic Golf.
MD XF