Rubik's Cube Solver by Ben Botto
Rubik's Cube Solver by Ben Botto
Ben Botto
May 9, 2020
While this might be obvious, I do want to get it out of the way early on. It is not
possible on a modern computer to solve a cube with a simple brute force algorithm.
A 3x3 Rubik’s Cube has 43,252,003,274,489,856,000 permutations with a branching
factor of about 13, and that’s far too many states to iterate through. Cooking up a
programmatic solver involves a pinch of efficient data structures, a sprinkle of
group theory, and a splash of clever algorithms.
Cubie: One of the 26 mini cubes that make up the entire Rubik’s Cube.
Center cubie: A cubie with one sticker on it at the center of a face. Note that the
center cubies are static. They cannot be moved relative to each other (the red
center cubie is always opposite the orange), nor can they be moved by twisting
the faces.
Edge cubie: A cubie with two stickers, like the red-yellow cubie.
Corner cubie: A cubie with three stickers, such as the red-blue-yellow cubie.
Twist: A 90- or 180-degree turn of one of the six faces. Given that each face can
be turned 90 degrees clockwise, 90 degrees counterclockwise (“prime”), or 180
degrees, there are 18 possible twists.
Twists generally follow the notation that the first letter of the twisting face is a 90-
degree clockwise turn. So F, B, U, D, L, and R designate 90-degree turns of the front,
back, up, down, left, and right faces, respectively. An apostrophe (’) suffix
designates a 90-degree counterclockwise “prime” twist, so R’ means to turn the right
face 90 degrees counterclockwise. And a “2” suffix indicates a 180-degree twist. L2
means the left face is turned 180-degrees.
A corner, edge, and center cubie on the front face. F would move the red-yellow edge cubie to the front-right
position, F’ to the front-left, and F2 would move it to the front-down position.
2. A single array of 54 chars. This is faster than the first structure, and the row and
stride are calculated manually (trivial). It’s still slow.
3. 6 64-bit integers. This method is essentially a bitboard, for those familiar with
the chess domain, and is significantly faster than methods one and two.
Twisting can be done using bitwise operations, and face comparisons can be
done using masks and 64-bit integer comparison.
4. An array of corner cubies and a separate array of edge cubies. The elements of
each array contain a cubie index (0–11 for edges; 0–7 for corners) and an
orientation (0 or 1 for edges; 0, 1, or 2 for corners).
Bitboard Structure
Expanding on structure three, each face of the cube is made up of 9 stickers, but the
center is stationary so only 8 need to be stored. And there are 6 colors, so a color can
be stored in a single byte. The 8 stickers on a face of a Rubik’s Cube can therefore be
stored in 64 bits.
enum class COLOR : uchar {WHITE, GREEN, RED, BLUE, ORANGE, YELLOW};
WGR
G B
WYO
An advantage of using this structure is that the rolq and rorq bitwise assembly
instructions can be used to move a face. Rolling by 16 bits effects a 90-degree twist,
and rolling by 32 bits gives a 180-degree twist. The adjacent pieces need to be up-
kept manually — e.g. after rotating the top face, the top-most colors of the front, left,
back, and right faces need to be moved, too. Turning faces in this manner is really
fast. For example, rolling
by 16 bits yields
WGW
Y G
OBR
Here’s how that looks with inline assembly in C++, using g++ with AT&T assembly
syntax. (Here is the model on GitHub.)
Index-Orientation Structure
Moving on to structure four from the list above, Korf’s algorithm uses an A* search
with pattern databases as a heuristic. Pattern databases are described in more detail
below, but in a nutshell a pattern database can be thought of as a value function that
accepts the state of a cube and returns a lower-bounds estimate of the number of
moves it takes to get from that state to the solved state. For Korf’s algorithm, “state”
is represented as the permutation and orientation of the cubies. For example, the
indexes and orientations of the 8 corner cubies could make up part of the cube’s
state. That representation given, it makes sense to store an ordered array of corner
cubie indexes i ∈ {0, ..., 7} and orientations o ∈ {0, 1, 2} .
Arbitrarily defining a convention, the corner cubies might be indexed like so:
0 1 2 3 4 5 6 7
ULB URB URF ULF DLF DLB DRB DRF
RBY RGY RGW RBW OBW OBY OGY OGW
The rows represent the index (0, …, 7), the position in the solved state (up-left-back,
…, down-right-front), and the colors (red-blue-yellow, …, orange-green-white). So
the red-blue-yellow (RBY) cubie is indexed as 0 and resides in the up-left-back (ULB)
corner when the cube is in a solved state. The orange-green-yellow (OGY) cubie is
indexed as 6 and sits in the down-right-back (DRB) corner.
0 1 2 3 4 5 6 7 8 9 10 11
UB UR UF UL FR FL BL BR DF DL DB DR
RY RG RW RB WG WB YB YG OW OB OY OG
The red-yellow (RY) cubie is indexed as 0 and sits at the intersection of the up and
back (UB) faces when the cube is in a solved state. The white-green (WG) cubie is
indexed as 4 and occupies the intersection of the front and right (FR) faces.
With this structure the orientation of each cubie needs to be kept as well. An edge
piece can be in one of two orientations, oriented or flipped ( o∈{0,1} ), while a
corner piece can be in three different orientations, oriented, rotated once, or
rotated twice ( o∈{0,1,2} ). More details about the orientation of pieces can be found
in Conrad Rider’s Edge Orientation Detection article.
In this model, rotating a face means updating indexes and orientations. This
representation is difficult to program because it’s hard for a human (for me at least)
to look at a big blob of index and orientation numbers and verify their correctness.
That being said, this model is significantly faster than dynamically calculating
indexes and orientations using one of the other models described above, and so it
works well when using pattern databases.
IDDFS alone would take far too much time to solve most scrambled cubes. There are
18 possible face twists of the cube, a large branching factor. After the first set of
twists, some of the moves can be pruned. For example, turning the same face twice
is redundant: FF is the same as F2; FF’ is the same as no move; FF2 is the same as F’;
and so on. Also, some moves are commutative: FB is the same as BF; U2D is the
same as DU2; etc. But even after pruning, the branching factor is over 13, so
searching for a solution with raw IDDFS would take thousands of years on a modern
computer! Here’s where A* comes in.
A* is a graph-traversal algorithm that’s used to find the optimal path from one node
of a graph to another, and, given that a tree is just a graph, it can be combined with
IDDFS. A* uses a heuristic to guide the search. A heuristic provides an estimated
distance (number of twists) from one node (a scrambled state) to another (the
solved state). For A* to operate correctly — specifically, to guarantee an optimal path
— the heuristic must never overestimate the distance. IDA* works the same way as
IDDFS, but rather than starting the search at depth 0 it queries a heuristic for an
estimated distance to the goal state and starts at that depth. During the search, if the
estimated distance through a node to the solved state exceeds the search depth, then
the node is pruned from the tree. In other words, the heuristic is used to see if
moving from one state of the cube to another brings the cube closer to or farther
away from the solved state. If the twist moves the cube farther away from the solved
state, then that branch of the search can be pruned.
Putting this in lay terms, a permissible heuristic for the Rubik’s Cube is a function
that accepts a scrambled cube and estimates or underestimates the number of
moves to solve the cube. One way to create such a heuristic function is to create a
database of every possible scramble of a subset of the cubies. For example, a
database that takes in the indexes and orientations of the 8 corner cubies and
returns the number of moves to solve just the corners. Given such a database,
provided a scrambled cube will take two moves to solve the corner cubies, it will
take at least two moves to solve the entire cube. It will likely take more than two
moves because of the edge cubies. In the image below, the corners are all solved, so
a corner-only pattern database would return an estimate of 0 moves to the solved
state. That’s obviously an underestimate because the edges are not solved, but it’s a
permissible heuristic because it never overestimates the distance to the solved state.
(This particular scramble is 6 moves away from solved.)
Pattern Databases
Indeed, Richard Korf proposes using a series of pattern databases as a heuristic for
the Rubik’s Cube, and one of the databases stores the number of moves required to
solve the corner pieces of any scramble. There are 8 corner cubies, and each can
occupy any of the 8 positions, so there are 8! possible permutations. Each of the
corner pieces can be oriented in 3 different ways — any of the three stickers can face
up — but the orientations of 7 of the cubies dictate the orientation of the 8th, by the
laws of the cube. (For the math folks out there, this has to do with parity of the cube,
which is even for the entire cube, but even or odd when considering the corners or
edges alone.) Therefore, there are 3⁷ possible ways the corners can be orientated.
Altogether then, there are 8! * 3⁷ possible ways for the corners of the cube to be
scrambled, and these 88,179,840 states can be iterated in a reasonable amount of
time (30 minutes or so). All corner states can be reached in 11 moves or fewer, so
each entry in the corner pattern database can be stored in a nibble (4 bits). On disk,
the corner pattern database occupies about 42MB.
Korf suggests two additional databases: one for 6 of the 12 edges, and another for
the other 6 edges. Given that modern developer boxes have plenty of RAM, I opted
to use two databases with 7 edges each. 7 edges can occupy 12 positions, so there are
12P7 (12! / (12–7)!) permutations. Each corner can be oriented in 2 ways, so there are
2⁷ possible orientations of 7 edges. Again, this is a small enough number of cube
states to iterate over, and all states can be reached in 11 moves or fewer. Storing
each entry in a nibble, each of the 7-edge databases occupies about 244MB (12P7 * 2⁷
/ 2 bytes).
Lastly, I used one additional database that holds the permutations of the 12 edges. It
takes about 228MB (12! / 2 bytes).
Using larger edge databases and the additional edge permutation database results in
a huge speed increase. Larger databases would result in an even bigger performance
increase, but it’s easy to use an enormous amount of memory. Adding just one more
edge piece to the 7-edge database, for example, increases the size of each database
to roughly 2.4GB.
An implementation detail that Korf glazes over in his algorithm is how to create
indexes into these pattern databases. That is, given a scrambled cube, how to create
a perfect hash out of the indexes and orientations of the edges or corners. That topic
is complex enough that I decided to dedicate another article to it, so if you’re
interested in the mathematical details, read my Sequentially Indexing Permutations:
A Linear Algorithm for Computing Lexicographic Rank article.
In a nutshell, the indexing works by first generating the Lehmer code of the cubie
index permutation, then converting the Lehmer code to a base-10 “rank.” (The
Lehmer code is a way of numbering permutations lexicographically). For the 8
corners, the ranks would be.
Permutation Rank
(0 1 2 3 4 5 6 7) 0
(0 1 2 3 4 5 7 6) 1
(0 1 2 3 4 6 5 7) 2
(0 1 2 3 4 6 7 5) 3
...
(4 0 1 2 3 5 6 7) 20160
...
(7 6 5 4 3 2 1 0) 40319
Permutation Rank
(0 0 0 0 0 0 0) 0
(0 0 0 0 0 0 1) 1
(0 0 0 0 0 0 2) 2
(0 0 0 0 0 1 0) 3
...
(1 0 0 0 0 0 0) 729
...
(2 0 0 0 0 0 0) 1458
...
(2 2 2 2 2 2 2) 2186
Lastly, those two ranks are combined together to form an index into the database.
It’s worth pointing out that indexing into the edge databases is more complicated
because it involves generating ranks over partial permutations (i.e. 7 of the 12
edges). Ranking partial permutations is also covered in my other article.
Since the Korf algorithm uses multiple pattern databases, the estimated number of
moves to the solved state, the A* heuristic, is the maximum value returned from all
of the pattern databases. More concretely, given a cube state where the 8 corner
cubies are 4 moves away from solved, 7 edge cubies are 6 moves away from solved,
and the other 7 edge cubies 3 moves away from solved, the lower-bounds estimated
number of moves to the solved state is max(4, 6, 3) = 6 .
IDA*
With the Rubik’s Cube structure and pattern databases in place, the IDA* algorithm
can be coded. I opted to use a non-recursive implementation, which, in my testing,
performed better than the typical recursive version. It also uses an optimization,
similar to best-first search, where leaf nodes are sorted based on their estimated
distance to the solved state. That way cube states that appear to be closest to the
solved state are visited first.
Thistlethwaite’s Algorithm
The optimal solver can take a long time to solve a cube, especially for scrambles that
take 18+ moves to solve. The Thistlethwaite algorithm is markedly faster, solving
cubes effectively instantaneously. It can also be implemented without pattern
databases using a decision tree, which is slower but easier to implement. In version
2.2.0 of my program I used a decision tree to implement Thistlethwaite’s algorithm,
but I’ve since enhanced the program to use pattern databases. A word of warning:
Implementing Thistlethwaite’s algorithm with pattern databases is quite difficult.
Like Korf’s, Thistlethwaite’s algorithm uses IDDFS. Above it was mentioned that the
branching factor of a Rubik’s Cube is a little above 13. There are 18 twists, but some
pairs of moves are redundant or commutative and can be pruned. Thistlethwaite’s
algorithm works by moving the cube from one “group” to another. Each successive
group can be solved with only a subset of the 18 twists, thereby reducing the
branching factor and the number of possible cube states, and making it
computationally easier to solve.
The initial group, group 0, is any scrambled cube. Remember from earlier that each
edge cubie can be in one of two orientations. Well, it turns out that edge pieces
cannot be flipped if quarter turns of the front and back faces are not used. (The
front and back faces are arbitrary — the same holds true for any two opposing faces,
e.g. left and right, front and back, or up and down.) By moving the cube to a state
wherein all 12 edge pieces are correctly oriented, group 1, the cube can be solved
without using quarter turns of the front or back faces. Put another way, an edge
piece is correctly oriented if it can be moved to its solved position without using
quarter turns of the front or back faces. From group 1 the branching factor is
reduced by 4 because the cube can be solved without F, F’, B, or B’ twists.
Next, the cube is moved to a state such that all corners are correctly oriented. That
is, if the cube is oriented such that the red center cubie is pointed up, then all 8
corner cubies have red or orange stickers on the up or down faces. Also, four of the
edges are moved to the correct slice: The front-left, front-right, back-left, and back-
right edges are placed in the E slice (the layer between the front and back faces).
This is group 2. Once group 2 is reached, the cube can be solved without quarter
turns of the left or right faces. This again reduces the branching factor by 4 because
L, L’, R, and R’ are no longer needed.
For group 3, all corners are moved to the correct orbit, meaning that each corner
can be moved to its home position with only 180-degree twists. Also, each edge is
moved to its home slice, M, E, or S, which are the layers between the left and right
faces, the up and down faces, and the front and back faces, respectively. From group
3 the cube can be solved with only 180-degree twists. Once again, the branching
factor is reduced by 4 (U, U’, D, and D’ are no longer needed).
Finally, only the 6 180-degree twists are needed to move from group 3 to the solved
state: U2, F2, L2, R2, B2, and D2. With that, we’ll stick a fork in it an call it done.
Again, this algorithm can be implemented using decision trees. In this case IDDFS is
used to move from one group to the next. Once a new group is reached, a subset of
the 18 twists are excluded from the search. This approach takes about a minute on
average to solve a cube. Alternatively, a series of pattern databases can be used, with
one pattern database per group, where each pattern database provides the exact
number of moves to get from an arbitrary cube state to the next group. That
approach can solve any scramble in mere milliseconds, but the math involved is
complicated.
Final Notes
There is a table on my GitHub that shows how long it takes to solve various
scrambles using my implementation of Korf’s algorithm. My program is
significantly faster than others I found on the internet, specifically because of the
linear ranking algorithm I use. It still takes quite a while to run through a solution,
though.
There’s also a table that shows the size and maximum distances in each group used
in the Thistlethwaite algorithm. Note that my implementation differs slightly from
Thistlethwaite’s in the third phase, and as such it solves cubes in a maximum of 46
twists instead of 52.
But those are topics for another day. I hope you have enjoyed the article. As always,
if you have any questions or comments about the article or code, please drop me a
line below.
Brown, Andrew. Rubik’s Cube Solver. Used for speed comparison. Also used to verify
optimal solutions.
Korf, Richard E. Finding Optimal Solutions to Rubik’s Cube Using Pattern Databases.
This describes Korf’s algorithm for solving the cube.
Korf, Richard E. et. al. Large-Scale Parallel Breadth-First Search. This contains a
terse linear algorithm for generating sequential indexes into a pattern database
using a factorial number system.
MIT. The Mathematics of the Rubik’s Cube. A paper that goes into some of the
mathematics and group theory surrounding the cube.
Rider, Conrad. Edge Orientation Detection. This page presents a simple algorithm
for detecting the orientation of the edge pieces.
Rubiks.com. How to solve the Rubik’s Cube. A simple layer-by-layer approach for
solving a cube as a human.
Taylor, Peter. Indexing Edge Permutations for the Rubik’s Cube. Peter was kind
enough to help me with creating indexes for the 7-edge pattern databases, which is a
variation of the Lehmer code using partial permutations.