Rozwiąż (Rubiks) Pocket Cube

16

Twoje zadanie

.. to zrobić to, czego Brian Fantana najwyraźniej nie mógł zrobić, i rozwiązać Kostkę Rubika 2x2x2.

kieszonkowy sześcian - prezenter telewizyjny

Układ

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

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

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

I zostanie ci przekazany przez stdin lub wiersz poleceń (twój wybór - proszę podać w odpowiedzi) w formacie:

ABCDEFGHIJKLMNOPQRSTUVWX

Należy pamiętać, że AD tworzą twarz U (w górę), EFMN tworzą twarz L (po lewej), GHOP tworzą twarz F (przednia), IJQR tworzą twarz R (po prawej), KLST tworzą Twarz B (tył) i UX tworzą twarz D (dół).

Będzie sześć unikatowych znaków reprezentujących każdy kolor, ale mogą się one różnić, więc przygotuj się na dowolną kombinację 6 znaków ascii dla każdego koloru.

Dane techniczne

  • Twój kod powinien wypisywać rozwiązanie, używając tylko prawej (R), górnej (U) i przedniej (F) powierzchni, i powinien używać standardowej notacji: R, R ', R2, U, U', U2, F, F „, F2. Więcej informacji znajdziesz tutaj . Ograniczenie do podzbioru RUF jest standardowe dla kostki 2x2 (Wskazówka: traktuj dolny lewy tylny róg jako stałą podstawę do pracy).
  • Twój kod musi być w stanie rozwiązać wszystkie możliwe permutacje kieszeni.
  • Każde rozwiązanie powinno zająć mniej niż 30 sekund.
  • Każde rozwiązanie powinno mieć mniej niż 30 ruchów.
  • Bonus w wysokości -20% zostanie przyznany za rozwiązania zawsze zapewniające odpowiedzi mniejsze niż 20 ruchów (prosimy o zareklamowanie go w swojej odpowiedzi, abym mógł go dokładnie sprawdzić)
  • Bonus w wysokości -50% zostanie przyznany kodowi, który zawsze zapewnia optymalne rozwiązanie. - Ponownie, prosimy o reklamę w swojej odpowiedzi
  • Rozwiązania nie mogą zawierać dwóch kolejnych ruchów na tej samej twarzy, ponieważ można je łatwo połączyć w jeden ruch.
  • Rozwiązania mogą opcjonalnie zawierać pojedynczą spację - i tylko jedną spację - między każdym ruchem.
  • W razie potrzeby cała sekwencja rozwiązania może być zawarta w nawiasach, cudzysłowach, nawiasach klamrowych, nawiasach lub znakach kreskowych, ale żadne inne dane wyjściowe nie są dozwolone.
  • Proszę podać krótko skomentowaną wersję kodu lub dokładne wyjaśnienie kodu.
  • Bez użycia plików zewnętrznych. Obejmuje to Internet, tabele danych oraz biblioteki / pakiety stworzone dla tego rodzaju problemów.
  • Wygrywa najkrótszy kod bajtowy.
  • Zwycięzca wybrany w środę (30 lipca 2014 r.).
Kyle McCormick
źródło
20
Mamy 2x2, 3x3 i 4x4 , ale wciąż czekam na wyzwanie 1x1 na moją szansę zabłysnąć. Mam idealny algorytm!
Klamka
Oto solver ~ 500 znaków w K, który generuje nawet optymalne (= najkrótsze) rozwiązanie: speedsolving.com/forum/…
Jakube
30 sekund powinno wystarczyć do brutalnego użycia siły przy użyciu Dijkstry: jest tylko 3674160 pozycji.
Peter Taylor
2
1. Zakładam, że nie ma żadnych ograniczeń dotyczących białych znaków na wyjściu 2. Aby być obiektywnym, powinieneś zdefiniować premię dla rozwiązań mniejszych niż 20 ruchów zamiast pozostawić ją jako „uznaniową”.
Level River St
@steveverrill Naprawiono. Dodano także specyfikację białych znaków. Dzięki!
Kyle McCormick

Odpowiedzi:

11

Python 2.7: 544 bajtów -50% = 272 bajtów **

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 zastępuje tabulatory wieloma białymi spacjami. Technicznie ta wersja ma 549 bajtów. Wystarczy zamienić pierwsze dwie spacje w wierszach 6-10 na tabulator.

Pomysł na mój program: Moim pierwszym pomysłem było pierwsze tchnienie. Ale to trwało zbyt długo. Około 2 minut na twardą (optymalną dla 11 ruchów) walkę. Postanowiłem więc podejść do problemu z obu stron. Używam dwóch zestawów. Generuję kolejno wszystkie stany o odległości 1,2,3, ... do szyfrowania i zapisuję je w zestawie 1, a jednocześnie wszystkie stany o odległości 1,2,3, ... do stanu rozwiązanego i zapisuję je w zestawie 2. Gdy stan znajduje się w obu zestawach po raz pierwszy, znaleźliśmy rozwiązanie.

W tym celu potrzebuję kolorów rozwiązanej kostki, które nie są znane. Znaki 13, 20 i 23 określają lewy, tylny i dolny kolor. Ale te 3 kolory są wystarczające do przedstawienia sześcianu. Po prostu zastępuję pozostałe 3 kolory białymi spacjami i mogę przedstawić mój rozwiązany stan jako „____ll____bbll____dddd”.

Aha, i do skrócenia permutacji skorzystałem z pomysłu z /codegolf//a/34651/29577

Wersja bez golfa:

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

Jestem całkiem zadowolony z wyniku, ponieważ jestem całkiem nowy w Pythonie. Jest to jeden z moich pierwszych programów w języku Python.

edycja: pół roku później: 427 - 50% = 213,5

Mam trochę więcej doświadczenia w Pythonie i golfie. Więc poprawiłem swój oryginalny kod i mogłem zapisać ponad 100 znaków.

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

Zasadniczo używam dokładnie tego samego podejścia. Największą zmianą jest to, że nie definiuję już funkcji. Zamiast

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)

mogę zrobić

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

Również nieco zmieniłem ruch Lamdy. Najpierw skróć, a następnie bezpośrednio zintegruj kod, ponieważ wywołanie funkcji pojawia się tylko raz.

Dla każdego stanu przechowuję listę liczb od 0 do 11, aby reprezentować ruchy, zamiast ciągu zawierającego ruchy. Liczby są konwertowane na samym końcu.

Również połączyłem dwie pętle for 'for k in r(3):for j in r(3):w jedną for y in r(12). Dlatego też muszę wykonywać ruchy U4, R4, F4. Oczywiście taki ruch nie pojawia się w najkrótszym rozwiązaniu, więc " 2'"[x%4]działa. (Jeśli x % 4 == 3byłby wyjątek poza zakresem indeksu)

Jest również trochę szybszy, ponieważ szukam wpisu w drugim zestawie wcześniej. Około 0,5 sekundy dla rozwiązania z 11 ruchami.

Jakube
źródło
2
Pozytywnie oceniany za używanie dwukierunkowego bfs - mojego ulubionego algorytmu wyszukiwania (obok IDA *). Jeśli czas pozwoli, przetestuję to za kilka godzin pod kątem optymalności. Nie zdawałem sobie również sprawy, że tak naprawdę nie potrzebujesz kolorów U / R / F, aby rozwiązać zagadkę. Ładnie wykonane!
Kyle McCormick
Zdany dla moich 20 przypadków testowych pod kątem poprawności i optymalności.
Kyle McCormick
bardzo miło .. pomogło mi za wdrożenie szybszego niż 24! jednokierunkowe bfs w js
RE60K,
właściwie „____ll____bbll____dddd” powinno być „____ll____bbll____bbdddd”
RE60K,
7

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

Korzystając z rekurencji, program przeszukuje drzewo o głębokości do 11 ruchów (maksymalna długość optymalnego rozwiązania według http://en.wikipedia.org/wiki/Pocket_Cube i strony wymienionej poniżej) i kiedy znajduje rozwiązanie wypisuje go (do 22 znaków, śledzone przez argument funkcjim ). Zastosowana kolejność jest rodzajem słownika, w którym wszystkie trasy rozpoczynające się na U, U2, U 'są przeszukiwane przed przeszukaniem jakichkolwiek tras rozpoczynających się na R lub F. Dlatego niekoniecznie musi najpierw znaleźć optymalne rozwiązanie.

Gdy rozwiązanie jest drukowane, rjest ono równe, mco zapewnia, że ​​później zostaną wydrukowane tylko równe lub krótsze rozwiązania. Umieszczenie r=m-2kosztuje dodatkowe 2 znaki, ale zapewnia wydruk tylko jednego rozwiązania dla każdej znalezionej długości (do optymalnej). Jeśli chcesz, aby wyświetlało TYLKO optymalne rozwiązanie, najlepsze rozwiązanie znalezione do tej pory musi być zapisane w zmiennej, a optymalne rozwiązanie wydrukowane na końcu programu (kosztowałoby to dodatkowe 15 znaków).

dane wejściowe są wczytywane do tablicy c[]począwszy od indeksu 64. Jest to konieczne, aby używać w alfabecie znaków alfabetu. Symbole @przelotowe Wsą używane zamiast pytania od A do X, ponieważ konieczne jest rozpoczęcie od liczby parzystej, aby test rozwiązań działał. c['Z']służy również do tymczasowego przechowywania, ponieważ do wykonania 4-krotnego obrotu potrzebnych jest w sumie 5 zadań. Ponieważ pierwsza część c[]jest nieużywana, można przechowywać rozwiązanie (które jest zakończone bajtem zerowym, podobnie jak wszystkie ciągi C.)

for(i..)przechodzi przez sekwencję 4 ćwierć obrotu twarzy określoną przez n.

Pierwszy for(j..)dokonuje faktycznej zamiany zgodnie z tabelą t[].

Aby sprawdzić, czy sześcian został rozwiązany, należy tylko sprawdzić cztery powierzchnie boczne. Elementy URF i DFR można rozróżnić nawet po usunięciu naklejek U i D, ponieważ jeden kawałek odczytuje XRF w kierunku zgodnym z ruchem wskazówek zegara, a drugi XFR. Jeśli dwa elementy zostaną zamienione, tak aby U wyświetlał się na dolnej powierzchni i odwrotnie, kolor F pojawi się na prawej twarzy i odwrotnie.

Drugi for(j..)liczy liczbę niedopasowań na czterech ścianach bocznych. Na przykład dla przedniej powierzchni porównuje G & O, H & P i G & H (dwa razy). Jeśli e== 0, sześcian jest rozwiązany. Jeśli e<9 lub e<13, możliwe jest rozwiązanie sześcianu odpowiednio w następnym ruchu lub 2 ruchach. W przeciwnym razie zdecydowanie nie da się rozwiązać sześcianu w tej liczbie ruchów. Aby zaoszczędzić czas, ta heurystyka służy do przycinania drzewa wyszukiwania i unikania marnowania czasu na wiele gałęzi o głębokości 10 lub 11, które się nie powiodą. Wyrażone jako formuła, staje się e<45-m*2.

Kod niepoznany

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

Występ

Program został przetestowany ze wzorami od 1 do 13 na stronie http://www.jaapsch.net/puzzles/cube2.htm

Poniższe wyniki podają czas na moim komputerze, aby znaleźć WSZYSTKIE optymalne rozwiązania (dla ciekawskich). Również w przypadku bardziej złożonych pozycji czas jest podany dla wspomnianej powyżej 2-bajtowej modyfikacji, która znajduje tylko jedno optymalne rozwiązanie. W tym celu podano zarówno czas znalezienia pierwszego rozwiązania, jak i zakończenia programu. Podane rozwiązania (które zasadniczo różnią się od rozwiązań uzyskanych przez odwrócenie generatorów na połączonej stronie) zostały zweryfikowane za pomocą internetowego symulatora kostki.

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
źródło
Brzmi dobrze. Chciałbym zobaczyć tutaj ścisłą konkurencję.
Kyle McCormick
@KyleMcCormick Mój program w końcu się zakończył i działa dobrze, ale widzę, że masz dość czekania i zaakceptowania innej odpowiedzi. Jest znacznie lepszy niż mój post z 2 dni temu, który miał błąd (twarze obracają się w niewłaściwy sposób.) Ponadto zastosowanie heurystyki na 2 poziomach poprawiło szybkość. Nadal wyświetla kilka rozwiązań, ale ostatnie rozwiązanie jest na pewno optymalne (więcej na temat możliwych zmian wyników w tekście.) Jest znacznie krótsze niż inne przesłanie. Jeśli masz problemy z formatem wyjściowym, daj mi znać.
Level River St
358 bajtów za pośrednictwem podstawowych pól golfowych.
MD XF