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.
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.3102@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.043Every 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