Virtual Rubik's Cube

In this notebook we show a brief demonstration of how to create and manipulate a virtual 4x4 Rubik's Cube (a Rubik's Revenge puzzle).

We use the rbuikscubennnsolver library from Github.

In [1]:
from rubikscubennnsolver.RubiksCube444 import RubiksCube444, solved_4x4x4
from pprint import pprint
In [2]:
def get_cube():
    """
    Get a 4x4 Rubiks Cube.
    """
    order = 'URFDLB'
    cube = RubiksCube444(solved_4x4x4, order)
    return cube
In [3]:
rr = get_cube()
rr.print_cube()
         U U U U 
         U U U U 
         U U U U 
         U U U U 

L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 

         D D D D 
         D D D D 
         D D D D 
         D D D D 


The cube starts in the solved state, and from there we can apply a sequence of moves (more on the move notation in the next notebook). For example, the notation for turning the upper face clockwise, then left face clockwise, then upper face counter-clockwise, then left face counter-clockwise, would be "U L U' L'". To apply this to the virtual cube, we use the cube's rotate(move) method to apply each move.

We will use this cube and this sequence to demonstrate a curious property that any move sequence will fulfill: if we repeatedly apply a move sequence to a solved cube, the cube will eventually return to a solved state. The number of times we need to apply the sequence depends on the sequence itself (and can be as small as 1 or as high as nearly 100,000).

In between these cycles, the center four squares or the 8 squares comprising the cross will occasionally all go through a solved state (that is, the cube will reach a "lower entropy" state). We can check that with the crosses_solved() and center_solved() methods.

For the sequence we picked, ULU'L, the cross pattern will reappear after the squence is applied 3 times, and the cube will return to the solved state after the sequence is applied 6 times.

In [4]:
# Apply this sequence 3 times to a solved cube, and the cross pattern re-appears on each face.
# Apply this sequence 6 times to a solved cube, and the entire cube returns to solved state.
sequence = ["U","L","U'","L'"]
In [5]:
for move in sequence:
    rr.rotate(move)
for move in sequence:
    rr.rotate(move)
for move in sequence:
    rr.rotate(move)
In [6]:
rr.print_cube()
rr.crosses_solved()
         D U U U 
         U U U U 
         U U U U 
         F U U F 

B L L R  U F F U  L R R R  B B B L 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
B L L L  F F F F  R R R R  B B B L 

         D D D D 
         D D D D 
         D D D D 
         U D D D 


Out[6]:
True
In [7]:
for move in sequence:
    rr.rotate(move)
for move in sequence:
    rr.rotate(move)
for move in sequence:
    rr.rotate(move)
In [8]:
rr.print_cube()
rr.crosses_solved()
rr.solved()
         U U U U 
         U U U U 
         U U U U 
         U U U U 

L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 

         D D D D 
         D D D D 
         D D D D 
         D D D D 


Out[8]:
True

It's important to note that (as far as I can tell) this library will not check if a cube position is valid. For example, on a Rubik's Revenge cube, it is impossible to have a double edge with a left piece oriented correctly and a right piece oriented incorrectly, if all other parts of the cube are solved. The double edges must be oriented correctly or incorrectly, together. The following position is impossible:

In [9]:
impossible_4x4x4="UUUUUUUUUUUUUFUURRRRRRRRRRRRRRRRFUFFFFFFFFFFFFFFDDDDDDDDDDDDDDDDLLLLLLLLLLLLLLLLBBBBBBBBBBBBBBBB"
In [10]:
order = 'URFDLB'
i = RubiksCube444(impossible_4x4x4,order)
In [11]:
i.print_cube()
         U U U U 
         U U U U 
         U U U U 
         U F U U 

L L L L  F U F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 

         D D D D 
         D D D D 
         D D D D 
         D D D D 


This means that if we want, at some point, to generate random Rubik's Cube permutations, we have to be careful to only generate valid cubes (or else implement a method for RubiksCube444 objects to check if a cube configuration is valid!).

Implementing second-layer turns

There are a few moves that the cube will execute, but none implement a second-layer turn.

2U, for example, implements a rotation of the upper two layers, rather than only the second upper layer. But that's also what Uw does, so this is redundant.

U2 implements two rotations of the U layer, so this doesn't do what we want either.

In [12]:
rr = get_cube()
rr.print_cube()

print("\n"*2)

rr.rotate("2U")
rr.print_cube()
         U U U U 
         U U U U 
         U U U U 
         U U U U 

L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 

         D D D D 
         D D D D 
         D D D D 
         D D D D 





         U U U U 
         U U U U 
         U U U U 
         U U U U 

F F F F  R R R R  B B B B  L L L L 
F F F F  R R R R  B B B B  L L L L 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 

         D D D D 
         D D D D 
         D D D D 
         D D D D 


The kludge solution is to implement the clockwise rotation of the second layer by rotating the top two layers clockwise, then the top layer counterclockwise:

Uw U'

Note that this portion of the code (rotate() method in __init__.py of rubiksnnnsolver library) is non-trivial to modify, so instead we just replace any 2U in the sequence with Uw U'.

In [13]:
rr = get_cube()
rr.print_cube()

print("\n"*2)

rr.rotate("Uw")
rr.rotate("U'")
rr.print_cube()
         U U U U 
         U U U U 
         U U U U 
         U U U U 

L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 

         D D D D 
         D D D D 
         D D D D 
         D D D D 





         U U U U 
         U U U U 
         U U U U 
         U U U U 

L L L L  F F F F  R R R R  B B B B 
F F F F  R R R R  B B B B  L L L L 
L L L L  F F F F  R R R R  B B B B 
L L L L  F F F F  R R R R  B B B B 

         D D D D 
         D D D D 
         D D D D 
         D D D D