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
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)
3
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
4
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.
5
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.
6
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.
7
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.
8
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).
9
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