Solve Rubik's cube

38

16

Write the shortest program that solves Rubik's cube (3*3*3) within a reasonable amount of time and moves (say, max. 5 seconds on your machine and less than 1000 moves).

The input is in the format:

UF UR UB UL DF DR DB DL FR FL BR BL UFR URB UBL ULF DRF DFL DLB DBR

(this particular input represents the solved cube).
First 12 2-character strings are the edges in the UF, UR, ... BL positions (U=up, F=front, R=right, B=back, L=left, D=down), then the next 8 3-character strings are the corners in the UFR, URB, ... DBR positions.

The output should give a sequence of moves in this format:

D+ L2 U+ F+ D+ L+ D+ F+ U- F+

Where D1 or D+ represents turning the D (down) face clockwise 90 degrees, L2 is turning the L face 180 degrees, U3 or U- represents turning the U face counterclockwise 90 degrees.
Letters are case-insensitive and spaces are optional.

For example, the output above is correct for the following input:

RU LF UB DR DL BL UL FU BD RF BR FD LDF LBD FUL RFD UFR RDB UBL RBU

For more details, please refer to Tomas Rokicki's cube contest, except scoring will be done directly by file size like a normal code golfing problem. An online tester is included too.

For reference, the shortest solution already written is the last entry in the list of cube contest winners


For those struggling to visualise the layout format:

0-1 2-3 4-5 6-7 8-9 10-11 12-13 14-15 16-17 18-19 20-21 22-23 24-25-26 27-28-29 30-31-32 33-34-35 36-37-38 39-40-41 42-43-44 45-46-47
UF  UR  UB  UL  DF   DR    DB   DL    FR    FL     BR    BL     UFR      URB      UBL      ULF      DRF      DFL      DLB      DBR

Front:

                 +-------+-------+-------+
                /       /       /       /|
               /  30   /   4   /  27   / |
              +-------+-------+-------+  |
             /       /       /       /|28+
            /   6   /       /   2   / | /|
           +-------+-------+-------+  |/ |
          /       /       /       /|3 +  |
         /  33   /   0   /  24   / | /|21+
        +-------+-------+-------+  |/ | /|
        |       |       |       |26+  |/ |
        |  35   |   1   |   25  | /|  +  |
        |       |       |       |/ | /|47+
        +-------+-------+-------+  |/ | /
        |       |       |       |17+  |/
        |  18   |       |  16   | /|11+
        |       |       |       |/ | /
        +-------+-------+-------+  |/
        |       |       |       |37+
        |  40   |   9   |  38   | /
        |       |       |       |/
        +-------+-------+-------+


Hidden faces:

                 +-------+-------+-------+
                /|       |       |       |
               / |  31   |   5   |  29   |
              +  |       |       |       |
             /|32+-------+-------+-------+
            / | /|       |       |       |
           +  |/ |  22   |       |  20   |
          /|7 +  |       |       |       |
         / | /|23+-------+-------+-------+
        +  |/ | /|       |       |       |
        |34+  |/ |  44   |  13   |  46   |
        | /|  +  |       |       |       |
        |/ | /|43+-------+-------+-------+
        +  |/ | /       /       /       /
        |19+  |/  42   /  12   /  45   /
        | /|15+-------+-------+-------+
        |/ | /       /       /       /
        +  |/  14   /       /  10   /
        |41+-------+-------+-------+
        | /       /       /       /
        |/  39   /   8   /   36  /
        +-------+-------+-------+

aditsu quit because SE is EVIL

Posted 2013-02-24T17:44:27.367

Reputation: 22 326

3Are languages other than C/C++/Java/Perl/Python accepted? – Egor Skriptunoff – 2013-03-12T10:00:36.957

@EgorSkriptunoff In here yes, use whatever you like, just no cube-solving libraries. – aditsu quit because SE is EVIL – 2013-03-12T11:24:24.230

And what about scoring? Usual code-golf scoring (bytes in program) or complex scoring like in 2004 contest? – Egor Skriptunoff – 2013-03-12T11:32:10.617

@EgorSkriptunoff I already addressed that: "scoring will be done directly by file size like a normal code golfing problem" – aditsu quit because SE is EVIL – 2013-03-12T11:37:00.493

@primo thanks for your interest in this :) – aditsu quit because SE is EVIL – 2013-03-12T11:45:03.907

Sorry, I'm a bit inattentive on Tuesdays. :-) Does this code-golf expected to be more interesting than simple "implement Thistlethwaite's algorithm" exercise? – Egor Skriptunoff – 2013-03-12T11:48:12.407

@EgorSkriptunoff the shortest solution I've seen doesn't use Thistlethwaite's – aditsu quit because SE is EVIL – 2013-03-12T12:01:36.537

oooo I wanna try this. – jdstankosky – 2013-03-19T15:54:13.097

I'm having a really difficult time understanding the input formatting. I just can't visualize it at all. :\ I understand that there are only 20 ACTUAL cube pieces, but... maybe I just need a diagram or something. (NOTE: I am severely dyslexic {and I have mild ADD}, so this may be part of my problem.) – jdstankosky – 2013-03-19T18:42:04.787

Could someone assist me by labeling this diagram I made? :P

– jdstankosky – 2013-03-19T18:56:33.310

2@jdstankosky, I've added a diagram. – Peter Taylor – 2013-04-12T13:36:35.957

7Are we allowed to pull off the stickers and move them around? – Iszi – 2013-12-24T18:59:38.417

I actually tried to do that, but I cannot find a way to solve the rubiks cube in less than 100 moves, so how come? Here is the method I learn (I think its the basic one): http://www.rubiksplace.com There are any tips in the algorithms to shorten moves I have to do as a human?

– None – 2013-12-24T11:11:26.043

Every cube can be solved within 20 moves (it has been proven), but it's very hard to achieve that. However, it's easy even for humans to get under 100 moves. Check out Fridrich, Petrus and Thistlethwaite's algorithms for example. – aditsu quit because SE is EVIL – 2013-12-24T14:57:51.190

Besides, this question sets the limit to 1000 moves, not 100. – aditsu quit because SE is EVIL – 2013-12-24T15:01:22.190

Wouldn't it be more interesting to make the code that can solve it in as few moves as possible? – markasoftware – 2014-06-09T00:40:54.360

Answers

25

C++ - 1123

Since nobody posted any answer so far, I decided to simplify and golf my 2004 solution. It's still far behind the shortest one I mentioned in the question.

#include<iostream>
#include<vector>
#define G(i,x,y)for(int i=x;i^y;i++)
#define h(x)s[a[x]/q*q+(a[x]+j)%q-42]
#define B(x)D=x;E=O.substr(j*3,3);G(i,0,3)E+=F[5-F.find(E[2-i])];G(i,0,D.length())D[i]=E[F.find(D[i++])];m.push_back(D);
#define P(a,b)G(i,0,6)G(k,49,52){e[0]=F[i];e[1]=k;m.push_back(e);}G(j,0,24){B(a)B(b)}
#define T C();z=m.size();for(b=c;b;){d=s;G(i,o=w=1,4){w*=z;if(o)G(j,0,w)if(o){s=d;u=j;G(k,0,i){f=m[u%z];G(x,0,f.length()){a=M[F.find(f[x++])];G(i,0,f[x]-48)G(l,0,2){q=3-l;p=4*l;G(j,0,q){t=h(p+3);G(k,-3,0)h(p-k)=h(p-1-k);h(p)=t;}}}u/=z;}C();if(c<b){u=j;G(k,0,i){std::cout<<m[u%z];u/=z;}b=c;o=0;}}}}
std::string s,a,D,E,d,f,e="  ",S="UFURUBULDFDRDBDLFRFLBRBLUFRURBUBLULFDRFDFLDLBDBR",F="ULFBRD",M[]={"KHEB*0.,","KRTI0<8@","KDNS*;2=","IVXG/@7>","BGWP,>4:","QNWT2468"},O=S.substr(24)+"FDRFRUFULFLDRDBRBURUFRFDBDLBLUBURBRDLDFLFULUBLBD";std::vector<std::string>m;int
w,X=8,Y=16,o,c,u,b,z,p,q,t;void C(){c=0;G(i,X,Y)c+=s[i]!=S[i];}main(int
g,char**v){G(i,1,g)s+=v[i];P("U2F1R1L3U2L1R3F1U2","L3R1F3L1R3D2L3R1F3L1R3");T;Y=24;T;X=0;T;m.clear();P("R3D3R1D3R3D2R1L1D1L3D1L1D2L3","R1F3L3F1R3F3L1F1");G(I,5,9){Y=I*6;T}}

It's not random, but it also doesn't proceed straightforwardly. It solves the edges first, then the corners. At each step, it tries various combinations of up to 4 algorithms and simple face turns (sequentially, not randomly), till it finds an improvement in the number of solved pieces, then repeats until solved. It uses 2 algorithms for edges and 2 for corners, translated to all cube positions.

Example output for RU LF UB DR DL BL UL FU BD RF BR FD LDF LBD FUL RFD UFR RDB UBL RBU:

L2F3B2F3B1U3F1B3R2F3B1U3F1B3D2F2L3D2L1U2B1L1R3U2R1L3B1U2R1U2L1F1B3U2B1F3L1U2L3R1D3L1R3B2L3R1D3L1R3L3R1D3L1R3B2L3R1D3L1R3B3F1D3B1F3R2B3F1D3B1F3U2F3L3R1B3L1R3U2L3R1B3L1R3F1D2F1L1R3D2R1L3F1D2F3L2U1B1F3L2F1B3U1L2R3L1F3R1L3U2R3L1F3R1L3U1F2U1L1R3F2R1L3U1F2U3L3U3L1U3L3U2L1R1U1R3U1R1U2R3F3U3F1U3F3U2F1B1U1B3U1B1U2B3L1B3R3B1L3B3R1B1B3D3B1D3B3D2B1F1D1F3D1F1D2F3R1F3L3F1R3F3L1F1R3B3R1B3R3B2R1L1B1L3B1L1B2L3R1D3L3D1R3D3L1D1B3D3B1D3B3D2B1F1D1F3D1F1D2F3U3R3U1R3U3R2U1D1R1D3R1D1R2D3

(234 moves, 0.3 sec here)

aditsu quit because SE is EVIL

Posted 2013-02-24T17:44:27.367

Reputation: 22 326

2What do you know... another answer was posted within seconds :) – aditsu quit because SE is EVIL – 2013-03-19T10:28:50.013

Although this is longer than the Ruby solution, I feel that it better fits the problem criteria "within a reasonable amount of time and moves." I'd still like to see a solution which averages under ~50 moves, though. – primo – 2013-03-20T06:51:35.483

2@primo Thanks :) My original code averaged over 50 moves, for under 50 I think you either need more (cube) algorithms or a different approach such as Thistlethwaite's method. However, efficiency (in no. of moves) is not very compatible with golfing. Anyway, for alternative solutions check out the winners of Tomas Rokicki's contest. – aditsu quit because SE is EVIL – 2013-03-20T08:09:18.667

23

Python 1166 bytes

A considerable amount of whitespace has been left for the sake of readability. Size is measured after removing this whitespace, and changing various indentation levels to Tab, Tab Space, Tab Tab, etc. I've also avoided any golfing which affected the performance too drastically.

T=[]
S=[0]*20,'QTRXadbhEIFJUVZYeijf',0
I='FBRLUD'

G=[(~i%8,i/8-4)for i in map(ord,'ouf|/[bPcU`Dkqbx-Y:(+=P4cyrh=I;-(:R6')]
R=range

def M(o,s,p):
 z=~p/2%-3;k=1
 for i,j in G[p::6]:i*=k;j*=k;o[i],o[j]=o[j]-z,o[i]+z;s[i],s[j]=s[j],s[i];k=-k

N=lambda p:sum([i<<i for i in R(4)for j in R(i)if p[j]<p[i]])

def H(i,t,s,n=0,d=()):
 if i>4:n=N(s[2-i::2]+s[7+i::2])*84+N(s[i&1::2])*6+divmod(N(s[8:]),24)[i&1]
 elif i>3:
  for j in s:l='UZifVYje'.find(j);t[l]=i;d+=(l-4,)[l<4:];n-=~i<<i;i+=l<4
  n+=N([t[j]^t[d[3]]for j in d])
 elif i>1:
  for j in s:n+=n+[j<'K',j in'QRab'][i&1]
 for j in t[13*i:][:11]:n+=j%(2+i)-n*~i
 return n

def P(i,m,t,s,l=''):
 for j in~-i,i:
  if T[j][H(j,t,s)]<m:return
 if~m<0:print l;return t,s
 for p in R(6):
  u=t[:];v=s[:]
  for n in 1,2,3:
   M(u,v,p);r=p<n%2*i or P(i,m+1,u,v,l+I[p]+`n`)
   if r>1:return r

s=raw_input().split()
o=[-(p[-1]in'UD')or p[0]in'RL'or p[1]in'UD'for p in s]
s=[chr(64+sum(1<<I.find(a)for a in x))for x in s]

for i in R(7):
 m=0;C={};T+=C,;x=[S]
 for j,k,d in x:
  h=H(i,j,k)
  for p in R(C.get(h,6)):
   C[h]=d;u=j[:];v=list(k)
   for n in i,0,i:M(u,v,p);x+=[(u[:],v[:],d-1)]*(p|1>n)
 if~i&1:
  while[]>d:d=P(i,m,o,s);m-=1
  o,s=d

Sample usage:

$ more in.dat
RU LF UB DR DL BL UL FU BD RF BR FD LDF LBD FUL RFD UFR RDB UBL RBU

$ pypy rubiks.py < in.dat
F3R1U3D3B1
F2R1F2R3F2U1R1L1
R2U3F2U3F2U1R2U3R2U1
F2L2B2R2U2L2D2L2F2

This is an implementation of Thistlethwaite's Algorithm, using an IDA* search to solve for each step. Because all of the heuristic tables need to be calculated on the fly, several compromises have been made, usually breaking a heuristic into two or more fairly equal size parts. This makes the computation of the heuristic tables many hundreds of times faster, while slowing down the search phase, usually only slightly, but it can be significant depending on the initial cube state.

Variable Index

  • T - the main heuristic table.
  • S - a solved cube state. Each individual piece is stored as a bit mask, represented as a character. A solved orientation vector is defined as the zero vector.
  • I - the various twists, in the order that they are eliminated from the search space.
  • G - the groups for twist permutations, stored as pairs to be swapped. Each byte in the compressed string encodes for one pair. Each twist needs six swaps: three for the edge cycle, and three for the corner cycle. The compressed string contains only printable ascii (char 32 to 126).
  • M - a function that performs a move, given by G.
  • N - converts a permutation of four objects to a number, for encoding purposes.
  • H - calculates the heuristic value for the given cube state, used to lookup move depth from T.
  • P - perform a search at a single depth of a single phase of the algorithm.
  • s - the input cube's permutation state.
  • o - the input cube's orientation vector.

Performance

Using Tomas Rokicki's data set, this script averaged 16.02 twists per solve (maximum 35), with an average time of 472ms (i5-3330 CPU @ 3.0 Ghz, PyPy 1.9.0). Minimum solve time was 233ms with a maximum of 2.97s, standard deviation 0.488. Using the scoring guidelines from the contest (white space is not counted, keywords and identifiers count as one byte for a length of 870), the overall score would have been 13,549.

For the last 46 cases (the random states), it averaged 30.83 twists per solve, with an average time of 721ms.


Notes on Thistlethwaite's Algorithm

For the benefit of anyone who might want to attempt an implementation of Thistlethwaite's Algorithm, here is a brief explanation.

The algorithm works on a very simple solution space reduction principle. That is, reduce the cube to a state in which a subset of twists are not necessary to solve it, reduce it to a smaller solution space, and then solve the remainder using only the few twists remaining.

Thistlethwaite originally suggested <L,R,F,B,U,D><L,R,F,B,U2,D2><L,R,F2,B2,U2,D2><L2,R2,F2,B2,U2,D2>. However, given the input format, I think it is easier to first reduce to <L,R,F2,B2,U,D> (no quarter turn F or B), and then <L2,R2,F2,B2,U,D> before finally reaching the half turn state. Instead of explaining exactly why this is, I think it will be evident after defining the criteria for each state.

<L,R,F,B,U,D><L,R,F2,B2,U,D>

In order to eliminate F and B quarter turns, only the edges must be oriented correctly. Gilles Roux has a very good explanation on his site of what 'correct' and 'incorrect' orientation is, so I'll leave the explaining to him. But basically, (and this is why this input format is so condusive to F and B elimination), an edge cubie is correctly oriented if it matches the following regex: [^RL][^UD]. A correct orientation is typically signified with a 0 and incorrect with 1. Basically U and D stickers may not appear on the R or L faces, or on the edges of the any U or D edge cubies, or they cannot be moved into place without requiring an F or B quarter twist.

<L,R,F2,B2,U,D><L2,R2,F2,B2,U,D>

Two criteria here. First, all corners must be oriented correctly, and second, each of the for middle layer cubies (FR, FL, BR, BL) must be somewhere in the middle layer. A corner orientation is very simply defined given the input format: the position of the first U or D. For example, URB has orientation 0 (correctly oriented), LDF has orientation 1, and LFU has orientation 2.

<L2,R2,F2,B2,U,D><L2,R2,F2,B2,U2,D2>

The criteria here is as follows: each face may only contain stickers from its face, or from the face directly opposite it. For example, on the U face there may only be U and D stickers, on the R face there may only be R and L stickers, on the F face there may only be F and B stickers, etc. The easiest way to ensure this is to check if each edge piece is in its 'slice', and each corner piece in its 'orbit'. Additionally, one needs to pay attention to edge-corner parity. Although, if you check only for corner parity, edge parity is also guaranteed, and vice versa.

How twists affect orientation

U and D twists affect neither the edge orientation, nor the corner orientation. The pieces may be swapped directly without updating the orientation vector.

R and L twists do not affect the edge orientation, but they do affect the corner orientation. Depending on how you define your cycle, the change in corner orientation will either be +1, +2, +1, +2 or +2, +1, +2, +1, all modulo 3. Note that R2 and L2 twists do not affect the corner orientation, as +1+2 is zero modulo 3, as is +2+1.

F and B affect both the edge orientations and corner orientations. Edge orientations become +1, +1, +1, +1 (mod 2), and corner orientations are the same as for R and L. Note that F2 and B2 affect neither the edge orientations, nor corner orientations.

primo

Posted 2013-02-24T17:44:27.367

Reputation: 30 891

@P0W the input format is a bit confusing, I suspect you may have an error there. Every case I've verified results in a solution. – primo – 2015-01-15T07:32:37.223

Great writeup. Have you heard of Kociemba's algorithm? – miles – 2013-03-27T10:57:14.573

I have. In principle, it's the same algorithm, except instead of four phases, it has only two: <L,R,F,B,U,D> -> <L2,R2,F2,B2,U,D> -> <I>. It requires a maximum of 29 twists to solve a cube (instead of 52 for Thistlethwaite's), but it also requires very large lookup tables, which would be impractical to generate 'on the fly'. – primo – 2013-03-27T11:00:36.120

@primo Would you mind publishing a link to the non-golfed code if you have it? – Bilow – 2018-04-09T12:48:10.170

12

Ruby, 742 characters

r=->y{y.split.map{|x|[*x.chars]}}
G=r['UF UR UB UL DF DR DB DL FR FL BR BL UFR URB UBL ULF DRF DFL DLB DBR']
o=r[gets]
x=[];[[%w{U UU UUU L LL LLL}+D=%w{D DD DDD},0],[%w{FDFFF RFDFFFRRR}+D,12],[%w{DDDRRRDRDFDDDFFF DLDDDLLLDDDFFFDF}+D,8],[%w{DFLDLLLDDDFFF RDUUUFDUUULDUUUBDUUU}+D,4],[%w{LDDDRRRDLLLDDDRD RRRDLDDDRDLLLDDD LFFFLLLFLFFFLLLF},16]].map{|q,y|x+=[*y..y+3]
3.times{q.map{|e|q|=[e.tr('LFRB','FRBL')]}}
w=->u{x.count{|t|u[t]!=G[t]}}
s=w[o]
(c=(0..rand(12)).map{q.sample}*''
z=o
c.chars{|m|z="=$'*:036\".?BOHKVGRWZ!$@*-0C69<4(E\\INQTMX!$'B-03<9*?6EHYLQPUZ!9'*-?360<$BSFKN[TWJ$'*!-0369<?BHKNEQTWZ!$'*6-039<?BEHKNTWZQ"[20*('FBLRUD'=~/#{m}/),20].bytes.map{|e|z[e/3-11].rotate e%3}}
t=w[z]
(c.chars{|e|$><<e<<'1 '};o=z;s=t)if s>t
)until s<1}

The above ruby code is not yet golfed fully. There are still possibilities to improve the code further (but its already enough as a starter).

It solves the cube layer by layer but uses no specific algorithm but instead performs random sequences of moves until the cube is solved.

Due to the probabilistic nature it may sometimes take longer than 5 seconds to solve the cube and in rare cases take more than 1000 moves.

Example output (for input 'RU LF UB DR DL BL UL FU BD RF BR FD LDF LBD FUL RFD UFR RDB UBL RBU') is 757 moves:

F1 R1 R1 F1 F1 F1 R1 R1 R1 L1 L1 L1 F1 D1 L1 L1 D1 D1 D1 D1 L1 B1 D1 
B1 B1 B1 L1 L1 L1 F1 D1 F1 F1 F1 L1 D1 L1 L1 L1 B1 D1 B1 B1 B1 R1 D1 
R1 R1 R1 L1 B1 D1 B1 B1 B1 L1 L1 L1 D1 D1 B1 D1 B1 B1 B1 B1 D1 B1 B1 
B1 L1 D1 L1 L1 L1 D1 D1 D1 D1 D1 R1 D1 R1 R1 R1 R1 F1 D1 F1 F1 F1 R1 
R1 R1 R1 D1 R1 R1 R1 F1 L1 D1 L1 L1 L1 F1 F1 F1 D1 D1 D1 D1 L1 D1 L1 
L1 L1 F1 L1 D1 L1 L1 L1 F1 F1 F1 D1 D1 L1 D1 L1 L1 L1 D1 L1 D1 L1 L1 
L1 L1 D1 L1 L1 L1 D1 R1 D1 D1 D1 R1 R1 R1 D1 D1 D1 B1 B1 B1 D1 B1 D1 
L1 D1 D1 D1 L1 L1 L1 D1 D1 D1 F1 F1 F1 D1 F1 D1 D1 D1 B1 B1 B1 D1 B1 
D1 R1 D1 D1 D1 R1 R1 R1 D1 D1 D1 B1 B1 B1 D1 B1 D1 R1 D1 D1 D1 R1 R1 
R1 D1 B1 D1 D1 D1 B1 B1 B1 D1 D1 D1 L1 L1 L1 D1 L1 D1 B1 D1 D1 D1 B1 
B1 B1 D1 D1 D1 L1 L1 L1 D1 L1 D1 D1 F1 D1 D1 D1 F1 F1 F1 D1 D1 D1 R1 
R1 R1 D1 R1 D1 D1 D1 R1 R1 R1 D1 R1 D1 F1 D1 D1 D1 F1 F1 F1 D1 B1 D1 
D1 D1 B1 B1 B1 D1 D1 D1 L1 L1 L1 D1 L1 D1 D1 D1 F1 F1 F1 D1 F1 D1 L1 
D1 D1 D1 L1 L1 L1 D1 D1 D1 D1 D1 D1 F1 F1 F1 D1 F1 D1 L1 D1 D1 D1 L1 
L1 L1 D1 D1 D1 F1 F1 F1 D1 F1 D1 L1 D1 D1 D1 L1 L1 L1 D1 F1 D1 D1 D1 
F1 F1 F1 D1 D1 D1 R1 R1 R1 D1 R1 D1 D1 D1 F1 F1 F1 D1 F1 D1 L1 D1 D1 
D1 L1 L1 L1 D1 D1 D1 F1 F1 F1 D1 F1 D1 L1 D1 D1 D1 L1 L1 L1 D1 D1 D1 
D1 D1 F1 F1 F1 D1 F1 D1 L1 D1 D1 D1 L1 L1 L1 D1 D1 D1 D1 D1 D1 R1 F1 
D1 F1 F1 F1 D1 D1 D1 R1 R1 R1 D1 F1 L1 D1 L1 L1 L1 D1 D1 D1 F1 F1 F1 
D1 B1 R1 D1 R1 R1 R1 D1 D1 D1 B1 B1 B1 D1 B1 R1 D1 R1 R1 R1 D1 D1 D1 
B1 B1 B1 D1 D1 D1 D1 B1 R1 D1 R1 R1 R1 D1 D1 D1 B1 B1 B1 D1 D1 D1 D1 
D1 D1 B1 B1 B1 D1 F1 D1 D1 D1 B1 D1 F1 F1 F1 D1 D1 D1 R1 R1 R1 D1 L1 
D1 D1 D1 R1 D1 L1 L1 L1 D1 D1 D1 B1 D1 D1 D1 F1 F1 F1 D1 B1 B1 B1 D1 
D1 D1 F1 D1 B1 B1 B1 D1 F1 D1 D1 D1 B1 D1 F1 F1 F1 D1 D1 D1 L1 D1 D1 
D1 R1 R1 R1 D1 L1 L1 L1 D1 D1 D1 R1 D1 F1 R1 R1 R1 F1 F1 F1 R1 F1 R1 
R1 R1 F1 F1 F1 R1 R1 R1 R1 D1 L1 D1 D1 D1 R1 D1 L1 L1 L1 D1 D1 D1 B1 
B1 B1 D1 F1 D1 D1 D1 B1 D1 F1 F1 F1 D1 D1 D1 F1 R1 R1 R1 F1 F1 F1 R1 
F1 R1 R1 R1 F1 F1 F1 R1 F1 D1 D1 D1 B1 B1 B1 D1 F1 F1 F1 D1 D1 D1 B1 
D1 F1 R1 R1 R1 F1 F1 F1 R1 F1 R1 R1 R1 F1 F1 F1 R1 F1 F1 F1 D1 B1 D1 
D1 D1 F1 D1 B1 B1 B1 D1 D1 D1 R1 D1 D1 D1 L1 L1 L1 D1 R1 R1 R1 D1 D1 
D1 L1 D1 R1 R1 R1 D1 L1 D1 D1 D1 R1 D1 L1 L1 L1 D1 D1 D1 R1 D1 D1 D1 
L1 L1 L1 D1 R1 R1 R1 D1 D1 D1 L1 D1 F1 F1 F1 D1 B1 D1 D1 D1 F1 D1 B1 
B1 B1 D1 D1 D1 L1 L1 L1 D1 R1 D1 D1 D1 L1 D1 R1 R1 R1 D1 D1 D1 

It is possible to reduce the move-count considerably if same moves are grouped together. Therefore, one can replace the output like

(c.gsub(/(.)\1*/){j=$&.size%4;$><<$1<<j<<' 'if j>0};o=z;s=t)if s>t

Howard

Posted 2013-02-24T17:44:27.367

Reputation: 23 109

Nice, but sometimes it takes more than 20 sec on my computer, in one case it finished in 48.7 sec – aditsu quit because SE is EVIL – 2013-03-19T10:38:06.033

@aditsu Yes. But it also strongly depends on which ruby interpreter you use. On my machine it usually takes less than 5 seconds. – Howard – 2013-03-19T10:40:17.690

I'm currently using ruby 1.9.3_p392, it often takes less than 5 seconds but I can't say "usually"

– aditsu quit because SE is EVIL – 2013-03-19T10:44:00.527

Try this input: FU FR RU BR DB LD LU LB LF RD DF BU FRU BUR FDR DLB DFL LUB FUL DBR – aditsu quit because SE is EVIL – 2013-03-19T10:46:31.353

One request: could you consolidate sequences like U1 U1 U1 into a single U3? – primo – 2013-03-19T14:56:00.187

@primo Of course it is possible - see my edit. Will cost you 27 characters if you introduce it. – Howard – 2013-03-19T16:01:49.320