of 36 /36
Robotic Rubik's Cube Final Report Brian Winkelmann, Hieu Bui, Jiawei Jiang T.A. Ben Cahill Group # 7 ECE 445 - Senior Design 6th May 2015

Robotic Rubik's Cube

  • Author
    others

  • View
    0

  • Download
    0

Embed Size (px)

Text of Robotic Rubik's Cube

T.A. Ben Cahill
Abstract
Our project is a giant Rubik’s Cube®[1] that is mechanized via robotics . We installed six
stepper motors to turn each side of the cube. These motors are controlled via a Raspberry Pi 2[2]
that is sending signals to separate stepper motor drivers for each motor. The Raspberry Pi
handles the solving and scrambling of the cube. It also provides three simple control buttons
(Scramble, Solve, Stop) for users to interact with the cube. The entire project is all powered by a
standard North American wall outlet.

1. Introduction
Rubik’s Cube® solvers are fun and interesting, but they all currently obstruct the view of
the cube. This makes it hard to grasp what the machine is doing. Our aim is to have a live
tutorial that a user can interact with the cube. We do this by having a large cube mounted on a
pedestal that is visible on all sides. This provides a more user-friendly environment for the
spectators. The cube will have motors embedded inside that can scramble itself, solve itself,
and teach people how to solve it. The cube is able to do this independently of other devices
because of a microcontroller inside. But there will also be a companion computer program that
runs a visual guide to a step-by-step solution of the cube.
Figure 1 - Block Diagram (26 April 2015)

Controller that is the brain behind the cube
1.1.2. Benefits
Show the entire cube while still turning the faces
1.1.3. Functions
1.1.4. Features
Easy controls

2.1.1. Microcontroller
The microcontroller Raspberry Pi 2 and is coded in Python. It takes button inputs to
decide which part of the code to run. It uses this code to determines which motor to be turned
on and in which direction. It sends the generated sequence one turn at a time to the motor
drivers. It will also can connect to a display. If connected, it can show the scramble used and
print what turns it is doing.
2.1.2. Power Supply
The power supply provides power for the microcontroller and the motor driver . It
consists of a transformer, an ON/OFF switch, a 5V voltage regulator and a 12V voltage
regulator. It provides power to the microcontroller via a USB port and to the motor drivers via a
molex connector, with a total power of 11.7 W. We know this because at any time, only one
motor will be activated. Thus the total power requirement of the system is:
V .5A 2V .35A  1.7WP = 5 × 1 + 1 × 0 = 1
Figure 2 - Power Supply Diagram (26 April 2015)
2.1.3. Motor Drivers
The motor driver takes input from the microcontroller and uses that information to turn
the motors. It will be able to turn each motor clockwise and counterclockwise independently.

2.1.4. Stepper Motors
There are six motors to turn each face. Five of these motors are inside the cube, and the
last one is in the base (See the appendix for motor placement). They are directly connected to
the motor drivers, which will power them. They will be able to have full rotation in the
clockwise and counterclockwise direction.
The motor are rated for a 1 kg*cm = 10 N*cm holding torque per phase[6]. The radius of
the cube is the distance from the center to an outside edge or about 127.3 mm
    rτ max =   × F 27.3 mm F 100 N m1 ×   <   *m   .785 NF < 0
The torque to turn a face can vary based on how inaccurate the adjacent face is
positioned. We position the adjacent face to different angle and measure the turning torque.
By using a force gauge attached to the outside edge of the face and pulling perpendicular we
will be able to measure the force and relate that to the torque. We increase the angle until we
measure a force that exceed 0.785N.

2.1.5. Buttons
This is simply a set of three push buttons available to the user. These are accessible in
the front of the pedestal and are the way the user interacts with the cube when a computer is
not connected. “Scramble” will scramble the cube. “Solve” will solve the cube. “Stop” will halt
all motor movement until another button is pressed.
2.1.6. Display
This is an optional component to the project. The cube does not need a display to turn,
but the display is needed to view the program. Most HDMI compatible displays will work
because the raspberry pi will automatically adjust to a display. The display is very useful if the
user wants to follow along with the cube, because the program can print out the scramble
sequence and the solve sequence.

2.2.1. Microcontroller
The program is made of two main parts. The first part is called Solve.py and it is
responsible for a whole bunch of things. It takes the cube state, loads and picks a random
scramble, updates cube for each turn, solves the cube, and prints out the cube representation.
The key to doing all of this is the cube object. The cube object store the cube state in to list, one
for the twelve edges and one for the eight corners. The object is also where we modify the two
list when a turn is executed. The cube object also have a a few had functions like printing the
current state, checking if the cube is solved , and resetting to a solved cube.
The remaining code of Solve.py is dedicated to the solving algorithm. The solve is not a
reversing of all the previous moves, but rather a process of algorithms to return the cube to a
solved state. The process we used is very closely related to the solve guide on the Rubik’s
website[1]. It is a five step process. The hard thing about making this into code is recognizing
where the piece that is next is and what orientation it is in. This is accomplished by a large set
of if statements that were hand coded.
The second part of the program is called Stepper.py and it is responsible for the input
and output of the Raspberry Pi. The input it takes are from the three buttons. The output is the
six enables for the six motors, a step signal, and a direction signal. Stepper.py uses the buttons
to call the solve.py function. those functions return a turn sequence. This turn sequence is
transformed by Stepper.py in to motor signals. For example, if the sequence says to turn the
orange face one turn counterclockwise, the output will enable motor 1, set direction to high,
and the pulses step 50 time to do a quarter turn.
To understand button and their callback functions, we looked at the sample code on
Make[8]. When a button is press it calls it specific callback function. The callback event is
triggered on the rising edge of the button. There is also a delay before accepting another rising
edge so that we do not call the callback function twice on accident. In addition, the buttons are
the way to stop the whole program on the Raspberry Pi. If the user presses all three buttons at
the same time it will stop the motors and halt the code.

Figure 4 - Microcontroller Program Flowchart with Solve Subroutine (6 May 2015)
For demonstration purposes MagicCube[10] was used to show a visual representation of
the cube on the display. If more work is done on the project, we hope to link this to the rest of
the code.
2.2.2. Power Supply
The power supply contains three major parts, the transformer, the rectifier and two
linear regulators. Considering that the total power requirement is 11.7W, we chose a
transformer (Stancor P8130) that takes 120V/AC input and provides 12V/AC output with 2A
current limit, so that the power supply can provide at most 24W output to meet the
requirement.
We chose two linear regulator from the TI µA7800 series to stabilize the rectified DC
voltage to meet the voltage requirement of the microcontroller (5V) and motor driver (12V).

The input voltage of the linear regulator may contain AC with frequency at 60Hz. In
order to filter out the oscillation, we use a bypass capacitor with 0.33 µf capacitance.
  0, 05 ΩZ capacitor 1 = 5 5
Likewise, we also need to filter out the oscillations introduced by the linear regulator.
According to the datasheet, we find out that the maximum frequency is 100Hz, so we choose a
capacitor with a slightly smaller capacitance, which is 0.1 µf .
  00, 00 ΩZ capacitor 2 = 1 0
Figure 5 - Power Supply Circuit (26 April 2015)
2.2.3. Motor Drivers
The motor driver circuit contains six drivers to control the six motors we need for the
system. The driver circuit receives signals from the microcontroller to control the motors.
At the beginning of the project, we select DRV8860, a serial input driver, to work with.
However, this type of driver can only drive unipolar stepper motors while the motors that we
have are bipolar. As a result, we need to switch to another type of driver that can work with our
10 
motors. We then select the A4988 driver to work with. The A4988 also accepts a motor supply
voltage ranging from 8 to 35V, which includes the voltage rating of our motor (10V). In addition
to its ability to drive our motor, it is a Step/Direction control driver which is easier to implement
than the serial input driver.
The driver circuit gets the input voltage from the power supply circuit for the motors.
We decide to have a 12V supply instead of 10V (the voltage rating of the NEMA 14) to have
more power for the motor. The supply voltage is within tolerance and does not damage the
motors.
In order to drive a motor, a low signal needs to be sent to the Enable pin of the driver. Each
rising edge to the Step pin will indicate a step for the motor. The number of pulses determine
the number of steps that we want the motor to move. For instance, a signal with 50 pulses will
make the motor complete a 90o turn. Since only one motor needs to be enabled at a given time,
the Step and Direction pins can be connected to the same output from the microcontroller,
while the Enable pin of each driver are wired to separate output pin from the microcontroller.
In our design, we only need the motors to be in full-step mode. Therefore, MS0, MS2 and MS3
pins are wired to GND. Also, we only have high inputs for the the Sleep and Reset pins since we
do not need to use these features in our design.
To prevent the motors from being damaged due to large current, we need to set the
output current limit of the driver.According to the datasheet of the driver carrier:
Current Limit = VREF x 2.5
Since the current rating of the motor is 500mA/phase, we can calculate VREF = 200mV.
We then adjust the potentiometer on the IC to get a value of 200mV for the reference voltage.
11 
12 
2.2.4. Stepper Motors
We need six motors to turn the six faces of the cube. Our design is to have the motor
mounted inside the cube and have the center pieces attached to the motors so that turning the
center piece will make the whole face to turn. Initially, we selected the NEMA 17, a bipolar
stepper motor, to work with. We decided to choose this type of motor since we can use it to
precisely turn the face of the cube to a proper angle by determining the number of steps the
motor needs to move. However, when assembling the cube, since we did not take the corner of
the motors into account, we found out that the motors were unable to fit inside the cubes.
We then selected the NEMA 14. This is the same type of motor as the NEMA 17. It has
less power and torque, but it is smaller and can fit inside the cube as we desire. However, this
creates a problem for us as we progress towards the end of the project. With the driver circuit
implemented, the motor does not have enough power and torque to turn the whole face of the
cube. We do not have a good method and equipments to accurately determine the required
torque to turn a whole face of the cube. We overlook the importance of the torque needed. As
a result, we fail to complete the project as proposed.
13 
3.1. Microcontroller
The microcontroller has plenty of memory space to store the two Python codes and the
1000 scrambles. By opening the scramble file and choosing a random line, we can have a
pseudo random scramble each time. The solving function has 5 steps and after each step we
verified the cube was turning the correct sequence and progressed to the correct state. We
have verified that for each of the 1000 scrambles the cube returns to a solved state after
running the solving function. In fact it does this 1000 scramble test in under 16 seconds. If a
users chooses to they can run the program in interactive mode and run and sequence they
choose.
The output to the stepper motor drivers was verified using an oscilloscope. The
direction and step behaved as expected. This includes 50 steps for a single turn and 100 steps
for a double turn. The motor enables also work well and the code does limit to only having one
motor enabled at any one time.
The inputs are able to detected and run in parallel with the motors turning. The turn
sequence and display cube state can also be shown on the display without interrupting the
motor driver signaling.
3.2. Power Supply
The power supply needs to meet the voltage and power requirements of the
microcontroller and motor driver. We used multimeter to verify that the voltage across the USB
port is 5V ± 0.25V and the voltage across the molex connector is 12V ± 0.5V.
3.3. Motor Driver
The motor driver circuit works well with the microcontroller and the power supply. It
receives the 12V input from the power supply circuit to supply the motors. Also, it receives
signals from the microcontroller to properly drive the motors. At a given time, only one motor
is enabled with the signals from the microcontroller. Once a motor is done with its rotation, it
stops and another motor is enabled. The 6 motors’ rotation accurately corresponds with the
sequence provided by the microcontroller. However, when translating pulses into steps, the
driver sometimes miss a few steps. For example, when the microcontroller outputs 50 pulses,
14 
the driver sometimes only drive the motor to turn 48 or 49 steps. This prevents the center
piece to have a perfect 90o turn. However, this is still within tolerance and does not affect other
faces’ rotation.
3.4. Stepper Motors
As mentioned earlier, we have to switch to a new set of motors since the initial one
does not fit inside the cube. With the new motors, we are able to assemble the cube with the
motors inside and have the faces rotate. In addition, these motors have bidirectional turning.
The center pieces attached to them can turn clockwise and counterclockwise. However, when it
comes to turning a whole face of the cube, the motors fail to do so. More torque and power are
required to turn the face.
3.5. Buttons
Each button sends a desired signal to the microcontroller in order to execute or halt the
program. All the buttons function accurately. The blue “Scramble” button runs the scramble
function to generate scrambling sequence for the driver, the green “Solve” button runs the
solve function to generate solving sequence for the driver, while the red “Stop” button pauses
the motor movement until another “Stop” is pushed again. Pushing all 3 buttons result in the
exit of the program.
3.6. Display
This is the main change to the project from the initial design review. Instead of a
separate computer and display, it is just a display connected to the Raspberry Pi. The display
can show the scramble used and the cube’s state and the end of each step in the solving
process. It is also able to display the step-by-step turns required to solve the cube. A visual
representation of the cube can be displayed, but it is not currently linked to the main program
and therefore is not following along with the motor movements.
15 
Voltage Regulator µA7805, µA7812[4]
2 $1 $2.00
A4988 Motor Driver[5] 6 $4.95 $29.70
Stepper Motor NEMA 14[6] 6 $12.95 $77.70
½” CPVC to PVC Bushing 1 $0.98 $0.98
3Pack #8 x 3” Screw and Bolt 2 $1.18 $2.36
” x 2” Cotter Pins 1 $0.78 $0.78
Super Glue 1 $5.77 $5.77
3D Printed Parts 5 $5 $25.00
Push Buttons 3 $2.95 $8.85
GPIO Breakout for RasPi 2 1 $7.95 $7.95
Micro USB cable 1 $2.95 $2.95
HDMI Cable 1 Borrowed $0
Mouse and Keyboard 1 Borrowed $0
Solder and Wires N/A Class Provided $0
Total $273.63
(2.5 × Hourly Rate × Total Time)
Brian Winkelmann $30.00 225 $16,875
Hieu Bui $30.00 225 $16,875
Jiawei Jiang $30.00 225 $16,875
Total $50,625
5. Conclusion
5.1. Accomplishment
We completed the basic functionality of the power supply, the microcontroller and the
motor driver circuit. The power supply is able to provide power to the microcontroller and the
motor driver. When we press the buttons, the microcontroller is able to respond and run the
corresponding program to scramble, solve or stop to cube and send the correct combination of
output signals to the motor driver. The motor driver is able to respond to the signals from the
microcontroller and drive the motors to turn accordingly.
5.2. Problems and Solutions
The motors do not have enough torque to turn the faces of the cube when it is fully
assembled. We are considering to use customized motors that can be operated under a higher
voltage and still have the same size. Another solution to this problem is to design a gearing
system that can help the motors overcome the friction to turn the faces.
Having exposed wires on the PCB is unsafe. Therefore, better PCB design and soldering
skill are necessary to ensure safety for the user. Also, having loose wires is another problem
which needs to be addressed. It causes unreliable connection between parts and can affect the
functionality of the circuit. A PCB design for the driver circuit is necessary to ensure proper
functionality of the circuit.
5.3. Ethical Considerations
The purpose of this project is to provide an interactive environment for the user to learn
how to solve a Rubik’s cube. Therefore, the product needs to be safe for the user to interact
with it, which correlates to the first item of the IEEE Code of Ethics[7]:
Item 1: to accept responsibility in making decisions consistent with the safety, health,
and welfare of the public, and to disclose promptly factors that might endanger the public or the
environment;
Besides the above item, we also followed other items listed in the IEEE Code of Ethics:
Item 3: to be honest and realistic in stating claims or estimates based on available data;
Item 5: to improve the understanding of technology; its appropriate application, and
potential consequences;
18 
Item 6: to maintain and improve our technical competence and to undertake
technological tasks for others only if qualified by training or experience, or after full disclosure of
pertinent limitations;
Item 7: to seek, accept, and offer honest criticism of technical work, to acknowledge and
correct errors, and to credit properly the contributions of others;
Item 8: to treat fairly all persons and to not engage in acts of discrimination based on
race, religion, gender, disability, age, national origin, sexual orientation, gender identity, or
gender expression;
Item 9: to avoid injuring others, their property, reputation, or employment by false or
malicious action;
Item 10: to assist colleagues and co-workers in their professional development and to
support them in following this code of ethics.
5.4. Safety Concerns
The main safety concern is that our project has moving parts. These moving parts are a
potential pinch hazard. We avoid excessive risk by having all the metal moving parts enclosed in
plastic housing and not openly accessible. There is also the “Stop” button to halt all motor
movement.
Another safety concern is the power supply is a potential shock hazard. We will reduce
this risk by having the power supply board not openly accessible and also by having a ground
plane on our board to safely handle any arcs.
Our last safety concern is the risk of personal injury on sharp corners. We eliminate this
risk by making the corners of the pedestal all smooth. We also use a cube that has larger
rounded edges on each piece. That way there is no risk of someone being poked or hit by a
sharp corner when the cube is moving.
5.5. Future Work
5.5.1. User Interface
So far, the program only has basic functionalities such as scramble and solve. It does not
explain to the user how the program solves the cube as we proposed at the start of the project.
In the future, we will build a user interface to create more interactions between the user and
19 
the program. The user interface will be able to show the steps to solve the cube, and explain
the concept and algorithm behind them.
5.5.2. Color Detection
Currently, our system can not resume from a power outage during execution. We will
use cameras to detect the color and position of each face, and let the program to determine
the current state of the cube and solve from it.
6. References
[1] Rubik’s Cube® used with permission of Rubiks Brand Ltd. https://www.rubiks.com/
[2] Raspberry Pi Foundation http://www.raspberrypi.org/
[3] World Cubing Association (WCA) https://www.worldcubeassociation.org/
[4] LM805 Voltage Regulator https://www.sparkfun.com/datasheets/Components/LM7805.pdf
[7] IEEE Code of Ethics http://www.ieee.org/about/corporate/governance/p7-8.html
[8] Tutorial: Raspberry Pi GPIO Pins and Python
http://makezine.com/projects/tutorial-raspberry-pi-gpio-pins-and-python/
https://jakevdp.github.io/blog/2012/11/26/3d-interactive-rubiks-cube-in-python/
http://www.orientalmotor.com/technology/articles/pdfs/SpeedTorqueCurves.pdf
22 
Pedestal and Gearing
24 
        if self.solved():              return "<Cube_obj: current state is SOLVED>"          else:              return "<Cube_obj: current state is SCRAMBLED>"        def __str__(self):          s = "    {}{}{}\n".format(self.c[1][0],self.e[1][0],self.c[2][0])          s +="    {}Y{}\n".format(self.e[0][0],self.e[2][0])          s +="    {}{}{}\n".format(self.c[0][0],self.e[3][0],self.c[3][0])          s +="{}{}{} ".format(self.c[1][1],self.e[0][1],self.c[0][2])          s +="{}{}{} ".format(self.c[0][1],self.e[3][1],self.c[3][2])          s +="{}{}{} ".format(self.c[3][1],self.e[2][1],self.c[2][2])          s +="{}{}{}\n".format(self.c[2][1],self.e[1][1],self.c[1][2])          s += "{}R{} ".format(self.e[5][0],self.e[4][1])          s += "{}G{} ".format(self.e[4][0],self.e[7][1])          s += "{}O{} ".format(self.e[7][0],self.e[6][1])          s += "{}B{}\n".format(self.e[6][0],self.e[5][1])          s +="{}{}{} ".format(self.c[5][1],self.e[8][1],self.c[4][2])          s +="{}{}{} ".format(self.c[4][1],self.e[11][1],self.c[7][2])          s +="{}{}{} ".format(self.c[7][1],self.e[10][1],self.c[6][2])          s +="{}{}{}\n".format(self.c[6][1],self.e[9][1],self.c[5][2])          s +="    {}{}{}\n".format(self.c[4][0],self.e[11][0],self.c[7][0])          s +="    {}W{}\n".format(self.e[8][0],self.e[10][0])          s +="    {}{}{}".format(self.c[5][0],self.e[9][0],self.c[6][0])          return s        def reset(self):          self.__init__()        def turn(self, side):          face = side[0]          if len(side) < 2:              val = 1          elif side[1] == '2':              val = 2          else:              val = 3          for i in range(val):              if face == 'Y':                  #corner cycle                  temp = list(self.c[0])                  self.c[0] = self.c[3]                  self.c[3] = self.c[2]                  self.c[2] = self.c[1]                  self.c[1] = temp                  #edge cycle                  temp = list(self.e[0])                  self.e[0] = self.e[3]                  self.e[3] = self.e[2]                  self.e[2] = self.e[1]                  self.e[1] = temp              elif face == 'W':                  #corner cycle                  temp = list(self.c[4])                  self.c[4] = self.c[5]                  self.c[5] = self.c[6]                  self.c[6] = self.c[7]                  self.c[7] = temp                  #edge cycle                  temp = list(self.e[8])                  self.e[8] = self.e[9]                  self.e[9] = self.e[10]                  self.e[10] = self.e[11]                  self.e[11] = temp              elif face == 'G':                  #corner cycle 
25 
                temp = list(self.c[0])                  self.c[0][0] = self.c[4][2]                  self.c[0][1] = self.c[4][1]                  self.c[0][2] = self.c[4][0]                  self.c[4][0] = self.c[7][1]                  self.c[4][1] = self.c[7][2]                  self.c[4][2] = self.c[7][0]                  self.c[7][0] = self.c[3][1]                  self.c[7][1] = self.c[3][0]                  self.c[7][2] = self.c[3][2]                  self.c[3][0] = temp[2]                  self.c[3][1] = temp[0]                  self.c[3][2] = temp[1]                  #edge cycle 
temp = list(self.e[3])                  self.e[3][0] = self.e[4][1]                  self.e[3][1] = self.e[4][0]                  self.e[4][0] = self.e[11][1]                  self.e[4][1] = self.e[11][0]                  self.e[11] = self.e[7]                  self.e[7] = temp              elif face == 'B':                  #corner cycle                  temp = list(self.c[1])                  self.c[1][0] = self.c[2][2]                  self.c[1][1] = self.c[2][0]                  self.c[1][2] = self.c[2][1]                  self.c[2][0] = self.c[6][2]                  self.c[2][1] = self.c[6][1]                  self.c[2][2] = self.c[6][0]                  self.c[6][0] = self.c[5][1]                  self.c[6][1] = self.c[5][2]                  self.c[6][2] = self.c[5][0]                  self.c[5][0] = temp[1]                  self.c[5][1] = temp[0]                  self.c[5][2] = temp[2]                  #edge cycle                  temp = list(self.e[1])                  self.e[1][0] = self.e[6][1]                  self.e[1][1] = self.e[6][0]                  self.e[6][0] = self.e[9][1]                  self.e[6][1] = self.e[9][0]                  self.e[9] = self.e[5]                  self.e[5] = temp              elif face == 'O':                  #corner cycle                  temp = list(self.c[3])                  self.c[3][0] = self.c[7][2]                  self.c[3][1] = self.c[7][1]                  self.c[3][2] = self.c[7][0]                  self.c[7][0] = self.c[6][1]                  self.c[7][1] = self.c[6][2]                  self.c[7][2] = self.c[6][0]                  self.c[6][0] = self.c[2][1]                  self.c[6][1] = self.c[2][0]                  self.c[6][2] = self.c[2][2]                  self.c[2][0] = temp[2]                  self.c[2][1] = temp[0]                  self.c[2][2] = temp[1]                  #edge cycle                  temp = list(self.e[2])                  self.e[2][0] = self.e[7][1]                  self.e[2][1] = self.e[7][0]                  self.e[7][0] = self.e[10][1]                  self.e[7][1] = self.e[10][0]                  self.e[10] = self.e[6] 
26 
                self.e[6] = temp              elif face == 'R':                  #corner cycle                  temp = list(self.c[0])                  self.c[0][0] = self.c[1][2]                  self.c[0][1] = self.c[1][0]                  self.c[0][2] = self.c[1][1]                  self.c[1][0] = self.c[5][2]                  self.c[1][1] = self.c[5][1]                  self.c[1][2] = self.c[5][0]                  self.c[5][0] = self.c[4][1]                  self.c[5][1] = self.c[4][2]                  self.c[5][2] = self.c[4][0]                  self.c[4][0] = temp[1]                  self.c[4][1] = temp[0]                  self.c[4][2] = temp[2]                  #edge cycle                  temp = list(self.e[0])                  self.e[0][0] = self.e[5][1]                  self.e[0][1] = self.e[5][0]                  self.e[5][0] = self.e[8][1]                  self.e[5][1] = self.e[8][0]                  self.e[8] = self.e[4]                  self.e[4] = temp    def turn(cube, sequence, front = 'G'):      "Takes cube notation and executes the turns"      global seq      inst = reassign(sequence, front).split()      for i in inst:          cube.turn(i)          seq.append(i)          if printstep:               print i              print cube    def next_turn():      "return the next motor move"      global seq      if len(seq) > 0:          return seq.pop(0)      else:          return ' '    def clear_turn(cube):      "Undo moves in seq and clear it"      global seq      while len(seq) > 0:          move = seq.pop() #undo move          if move == "R":              cube.turn("R'")          elif move == "R'":              cube.turn("R")          elif move == "O":              cube.turn("O'")          elif move == "O'":              cube.turn("O")          elif move == "G":              cube.turn("G'")          elif move == "G'":              cube.turn("G")          elif move == "B":              cube.turn("B'")          elif move == "B'":              cube.turn("B")          elif move == "W": 
27 
28 
                turn(cube, "F U F'",i[0])            for i in ey: #allign              if cube.e[ey[i]] == ['W',f] or cube.e[ey[i]] == [f,'W']:                  diff = (ey[f] ­ ey[i])%4                  if diff == 1:                      turn(cube,"U")                  elif diff == 2:                      turn(cube,"U2")                  elif diff == 3:                      turn(cube,"U'")                  break            if cube.e[ey[f]][0] == 'W': #match              turn(cube, "F2",f)          else:              turn(cube, "F D R' D'",f)      if p: print "DONE"    def white_corners(cube):      if p: print "Solving White Corner"      cy = {'RB':1,'BO':2,'OG':3,'GR':0}      cw = {'RB':5,'BO':6,'OG':7,'GR':4}      for f in ['RB','BO','OG','GR']:          if cube.c[cw[f]] == ['W',f[0],f[1]]: #already right              continue            for i in cw: #move bottom to top              if (cube.c[cw[i]] == ['W',f[0],f[1]] or                  cube.c[cw[i]] == [f[1],'W',f[0]] or                  cube.c[cw[i]] == [f[0],f[1],'W']):                  turn(cube, "F U F'",i[0])            for i in cy: #allign              if (cube.c[cy[i]] == ['W',f[1],f[0]] or                  cube.c[cy[i]] == [f[0],'W',f[1]] or                  cube.c[cy[i]] == [f[1],f[0],'W']):                  diff = (cy[f] ­ cy[i])%4                  if diff == 1:                      turn(cube,"U")                  elif diff == 2:                      turn(cube,"U2")                  elif diff == 3:                      turn(cube,"U'")                  break            while cube.c[cw[f]] != ['W',f[0],f[1]]: #match              turn(cube, "R U R' U'", f[1])      if p: print "DONE"    def middle_layer(cube):      if p: print "Solving Middle Layer"      ey = {'B':1,'O':2,'G':3,'R':0}      es = {'RB':5,'BO':6,'OG':7,'GR':4}      for f in ['RB','BO','OG','GR']:          if cube.e[es[f]] == [f[0],f[1]]: #already right              continue            for i in es: #move side to top              if cube.e[es[i]] == [f[0],f[1]] or cube.e[es[i]] == [f[1],f[0]]:                  turn(cube,"R U' R' U' F' U F",i[1])            for i in ey: #allign and match              if cube.e[ey[i]] == [f[0],f[1]]: #allign to f[1]                  diff = (ey[f[1]] ­ ey[i])%4                  if diff == 1: 
29 
                    turn(cube,"U")                  elif diff == 2:                      turn(cube,"U2")                  elif diff == 3:                      turn(cube,"U'")                  turn(cube,"U R U' R' U' F' U F",f[1])#right turn                  break              if cube.e[ey[i]] == [f[1],f[0]]: #allign to f[0]                  diff = (ey[f[0]] ­ ey[i])%4                  if diff == 1:                      turn(cube,"U")                  elif diff == 2:                      turn(cube,"U2")                  elif diff == 3:                      turn(cube,"U'")                  turn(cube,"U' L' U L U F U' F'",f[0])#left turn                  break      if p: print "DONE"    def top_face(cube):      if p: print "Solving Top Face"      #yellow cross      ey = {'B':1,'O':2,'G':3,'R':0}      if (cube.e[ey['B']][1] == 'Y' and          cube.e[ey['O']][1] == 'Y' and          cube.e[ey['G']][1] == 'Y' and          cube.e[ey['R']][1] == 'Y'): #dot shape          turn(cube,"F U R U' R' F'")      if (cube.e[ey['B']][0] == 'Y' and          cube.e[ey['O']][1] == 'Y' and          cube.e[ey['G']][0] == 'Y' and          cube.e[ey['R']][1] == 'Y'): #BG line          turn(cube,"F R U R' U' F'", 'R')      elif (cube.e[ey['B']][1] == 'Y' and            cube.e[ey['O']][0] == 'Y' and            cube.e[ey['G']][1] == 'Y' and            cube.e[ey['R']][0] == 'Y'): #RO line          turn(cube,"F R U R' U' F'", 'G')      elif (cube.e[ey['B']][0] == 'Y' and            cube.e[ey['O']][1] == 'Y' and            cube.e[ey['G']][1] == 'Y' and            cube.e[ey['R']][0] == 'Y'): #RB L shape          turn(cube,"F U R U' R' F'", 'G')      elif (cube.e[ey['B']][0] == 'Y' and            cube.e[ey['O']][0] == 'Y' and            cube.e[ey['G']][1] == 'Y' and            cube.e[ey['R']][1] == 'Y'): #BO L shape          turn(cube,"F U R U' R' F'", 'R')      elif (cube.e[ey['B']][1] == 'Y' and            cube.e[ey['O']][0] == 'Y' and            cube.e[ey['G']][0] == 'Y' and            cube.e[ey['R']][1] == 'Y'): #OG L shape          turn(cube,"F U R U' R' F'", 'B')      elif (cube.e[ey['B']][1] == 'Y' and            cube.e[ey['O']][1] == 'Y' and            cube.e[ey['G']][0] == 'Y' and            cube.e[ey['R']][0] == 'Y'): #GR L shape          turn(cube,"F U R U' R' F'", 'O')        #yellow corners      cy = {'RB':1,'BO':2,'OG':3,'GR':0}      up = (int(cube.c[cy['RB']][0] == 'Y') +            int(cube.c[cy['BO']][0] == 'Y') +            int(cube.c[cy['OG']][0] == 'Y') +            int(cube.c[cy['GR']][0] == 'Y'))      while up < 4: 
30 
        if up == 0: #state 1              for i in cy:                  if cube.c[cy[i]][2] == 'Y':                      f = i[0]                      break          elif up == 1: #state 2              for i in cy:                  if cube.c[cy[i]][0] == 'Y':                      f = i[0]                      break          elif up == 2: #state 3              for i in cy:                  if cube.c[cy[i]][1] == 'Y':                      f = i[0]                      break          else:              if p: print "~~CUBE ERROR: CORNERS~~"              break          turn(cube,"R U R' U R U2 R'", f)          up = (int(cube.c[cy['RB']][0] == 'Y') +                int(cube.c[cy['BO']][0] == 'Y') +                int(cube.c[cy['OG']][0] == 'Y') +                int(cube.c[cy['GR']][0] == 'Y'))      if p: print "DONE"    def final_layer(cube):      if p: print "Solving Final Layer"      #corners position      cy = {'RB':1,'BO':2,'OG':3,'GR':0}      match = (int(cube.c[cy['RB']] == ['Y','R','B']) +               int(cube.c[cy['BO']] == ['Y','B','O']) +               int(cube.c[cy['OG']] == ['Y','O','G']) +               int(cube.c[cy['GR']] == ['Y','G','R']))      while match < 2:          turn(cube,"U")          match = (int(cube.c[cy['RB']] == ['Y','R','B']) +                  int(cube.c[cy['BO']] == ['Y','B','O']) +                  int(cube.c[cy['OG']] == ['Y','O','G']) +                  int(cube.c[cy['GR']] == ['Y','G','R']))      if match == 4:           pass #matched      elif (cube.c[cy['RB']] == ['Y','R','B'] and             cube.c[cy['BO']] == ['Y','B','O']):          turn(cube,"R' F R' B2 R F' R' B2 R2 U'", 'G')      elif (cube.c[cy['BO']] == ['Y','B','O'] and             cube.c[cy['OG']] == ['Y','O','G']):          turn(cube,"R' F R' B2 R F' R' B2 R2 U'", 'R')      elif (cube.c[cy['OG']] == ['Y','O','G'] and             cube.c[cy['GR']] == ['Y','G','R']):          turn(cube,"R' F R' B2 R F' R' B2 R2 U'", 'B')      elif (cube.c[cy['GR']] == ['Y','G','R'] and             cube.c[cy['RB']] == ['Y','R','B']):          turn(cube,"R' F R' B2 R F' R' B2 R2 U'", 'O')      else: #cross          if cube.c[cy['GR']] == ['Y','G','R']:              turn(cube,"U2")          turn(cube,"R' F R' B2 R F' R' B2 R2 U2", 'G')          turn(cube,"R' F R' B2 R F' R' B2 R2 U'", 'R')        #edge position      ey = {'B':1,'O':2,'G':3,'R':0}      match = (int(cube.e[ey['B']][1] == 'B') +               int(cube.e[ey['O']][1] == 'O') +               int(cube.e[ey['G']][1] == 'G') +               int(cube.e[ey['R']][1] == 'R'))      if match == 0: #no matches 
31 
        turn(cube,"F2 U L R' F2 L' R U F2") #makes a match      if match == 4: #all done          pass      elif match == 2:          if p: print "~~CUBE ERROR: EDGES~~"      else: #1 match          if cube.e[ey['B']][1] == 'B':              f = 'G'              r = 'R'          elif cube.e[ey['O']][1] == 'O':              f = 'R'              r = 'B'          elif cube.e[ey['G']][1] == 'G':              f = 'B'              r = 'O'          elif cube.e[ey['R']][1] == 'R':              f = 'O'              r = 'G'          if cube.e[ey[f]][1] != r:              turn(cube,"F2 U' L R' F2 L' R U' F2", f) #counterclockwise          else:              turn(cube,"F2 U L R' F2 L' R U F2", f) #clockwise        if p: print "DONE"    def solve(cube, printlabel = False, printfig = False):      "Solve the cube based on rubiks.com guide"      global p       p = printlabel      if p: print "Solving Cube"      if printfig: print cube      white_cross(cube)      if printfig: print cube      white_corners(cube)      if printfig: print cube      middle_layer(cube)      if printfig: print cube      top_face(cube)      if printfig: print cube      final_layer(cube)      if printfig: print cube      if p: print "SOLVED"    def testing(cube):      global seq      total = 0      nseq = []      for i in range(1000):          scramble(cube, seed = i)          solve(cube)          total += cube.solved() #increment is correctly solved          nseq.append(len(seq))          seq = []      print "Solved {} cubes".format(total)      print "{} turns on average".format(sum(nseq)/float(len(nseq)))    def print_details(value = True):      global printstep      printstep = value    if __name__ == "__main__": #main function      cube = Cube_obj()      testing(cube)       
32 
33 
34 
35