Reputation: 11
I am currently working on an assignment to build a functioning Rubik's cube. The program does not need a GUI. But it must simulate a 3 X 3 cube with rotation behaviors and provide a graphical representation of the cube (I'm going with a flat lettered structure). My code has a class for facets which make a Face (another class) and then there is a cube class which contains the rotation methods.
I am having trouble creating/choosing an algorithm to use that would simulate the cube and all possible rotations accurately. I found a solution on this site referencing a paper proposing 7 different ways to do it (the link in below). But which method is the most intuitive/easy to code? And more importantly, which would best fit the behavior outlined below (in the pseudocode)?
I am having trouble understanding how I could use any method to account for the changes on each face at once especially when taking into account the behavior of the face rotations (as opposed to the rows and columns).
How would you represent a Rubik's Cube in code?
Rotation Pseudocode:
map each cube numbers 1-54, faces 1 – 4 are 1 – 36 while top face is 37 - 45 and bottom is 46 – 54
1 turn: clockwise = +9
1 turn: counterclockwise = -9 loops back to 45
1 turn: up = + 37
1 turn: down = -37 loops back to 46
2 turns: clockwise = +9(2)
2 turns: counterclockwise = -9(2)
2 turns: up = +37(2)
2 turns: down = -37(2)
3 turns: clockwise = +9(3)
3 turns: counterclockwise = -9(3)
3 turns: up = +37(3)
3 turns: down = -37(3)
This pseudocode does not account for the face changes.
Is there a better/easier way to do this, different from the method my pseudocode proposes? How do I account for the face changes?
Example: (front face, 1 turn, clockwise)
123 741
456 852
789 963
Note: I am leaning towards the 54 element vector but am unsure how to manipulate it.
Also, this is my first question so let me know if something is wrong (not enough info, too much, wrong topic, etc.)
Thank you!
Note: This is the code I am working with.
Facet Class:
public class Facets {
public Color color;
public Facets(Color color){
}
public enum Color {
B, G, R, Y, O, P
}
public String getName(){
return this.color.name();
}
}
Face Class:
import java.util.Arrays;
public class Face {
public Facets[] face;
/*public Face(Facets.Color color, Facets.Color[] array){
face = new Facets[9];
for(int i = 0; i < face.length; i++){
face[i] = new Facets(array[i]);
face[i] = new Facets(color);
}
}*/
public Face(Facets.Color color){
face = new Facets[9];
for(int i = 0; i < face.length; i++){
face[i] = new Facets(color);
}
}
public Face(Facets.Color[] array){
face = new Facets[9];
for (int i = 0; i < face.length; i++){
face[i] = new Facets(array[i]);
//face[i] = face[i].toString();
}
}
//Returns a textual representation of Face
public String printFace(){
StringBuilder faceString = new StringBuilder();
for(Facets f: face){
faceString.append(f.getName());
System.out.println(f.toString());
}
return faceString.toString();
}
public static void main(String[] args){
Face face = new Face(Facets.Color.B);
System.out.println(face.toString());
}
}
Cube Class:
public class Cube {
public Cube(Face front, Face right, Face back, Face left, Face top, Face bottom){
}
public Cube createCube(){
}
public Cube rotate(int row, int column, String direction, int turns){
/*Turns must be between 0 - 4
Row must be 1 or 2, column must be 1 or 2, direction must be clockwise, counterclockwise, up or down (0 means no turn, 1 is top row or left column; 2 is bottom row or right column)
*/
}
public int turns(){
}
public Cube row(){
}
public Cube column(){
}
public Cube clockwise(){
}
public Cube counterClockwise(){
}
public Cube up(){
}
public Cube down(){
}
public Cube random(Cube cube){
}
public Cube configuration(Cube cube){
}
}
Rubik's Cube:
public class RubiksCube {
public RubiksCube(Cube cube){
}
public RubiksCube(Face front, Face rightOfFront, Face back, Face leftOfFront, Face top, Face bottom){
}
//calls face and colors and initializes the arrays into a object
//default config: solid color on each side so each array, representing a face, is set to a solid color
public void createNewCube(){
}
public void rotation(Cube cube, int row, int column, String direction, int turns){
}
public Cube configuration(Cube cube){//should return 6 Faces? or cube?
return cube;
}
}
Upvotes: 1
Views: 3839
Reputation: 10003
i just started to write some code to manipulate a cube.
package p;
import java.util.Arrays;
import static p.Facet.*;
enum Facet { // used for face, color, and operations
u(1),d(1),r(2),l(0),f(1),b(3); // maybe FRULBD instead?
Facet(int pad) {
this.pad=pad;
}
// u pad n+1
// l pad 0
// f pad n+1
// r pad 2(n+1)
// b pad 3(n+1) or at the bottom.
// d pad n+1
final int pad; // for formatting
}
class Face {
Face(int n,Facet facet) {
this.n=n;
this.facet=facet;
facets=new Facet[n][n];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
facets[i][j]=facet;
}
Face(Face face) {
this.n=face.n;
this.facet=face.facet;
facets=new Facet[n][n];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
facets[i][j]=face.facets[i][j];
}
@Override public String toString() {
return toString(0);
}
public String toString(int pad) {
StringBuffer stringBuffer=new StringBuffer();
for(int i=0;i<n;i++) {
stringBuffer.append(pad(pad,n));
for(int j=0;j<n;j++)
stringBuffer.append(facets[i][j]);
stringBuffer.append('\n');
}
return stringBuffer.toString();
}
String pad() {
return pad(facet.pad,n);
}
static String pad(int length) {
StringBuffer stringBuffer=new StringBuffer(length);
for(int i=0;i<length;i++)
stringBuffer.append(" ");
return stringBuffer.toString();
}
static String pad(int length,int n) {
return pad(length*(n+1));
}
@Override public int hashCode() {
final int prime=31;
int result=1;
result=prime*result+((facet==null)?0:facet.hashCode());
result=prime*result+Arrays.deepHashCode(facets);
result=prime*result+n;
return result;
}
@Override public boolean equals(Object obj) {
if(this==obj) return true;
if(obj==null) return false;
if(getClass()!=obj.getClass()) return false;
Face other=(Face)obj;
if(facet!=other.facet) return false;
if(!Arrays.deepEquals(facets,other.facets)) return false;
if(n!=other.n) return false;
return true;
}
static void rotateClockwise(Object[][] objects) {
Object temp;
int n=objects.length;
// For each concentric square around the middle of the matrix to rotate...
// This value will be used as (m, n) offset when moving in.
// Integer division by 2 will skip center if odd length.
for(int i=0;i<n/2;i++)
// for the length of this ring
for(int j=0;j<n-2*i-1;j++) {
temp=objects[i][i+j];
objects[i][i+j]=objects[n-i-j-1][i];
objects[n-i-j-1][i]=objects[n-i-1][n-i-j-1];
objects[n-i-1][n-i-j-1]=objects[i+j][n-i-1];
objects[i+j][n-i-1]=temp;
}
}
static void rotateCounterClockwise(Object[][] objects) {
int n=objects.length;
for(int i=0;i<n/2;i++) {
for(int j=i;j<n-i-1;j++) {
Object temp=objects[i][j];
objects[i][j]=objects[j][n-1-i]; // move values from right to top
objects[j][n-1-i]=objects[n-1-i][n-1-j]; // move values from bottom to right
objects[n-1-i][n-1-j]=objects[n-1-j][i]; // move values from left to bottom
objects[n-1-j][i]=temp;
}
}
}
final int n;
final Facet facet;
final Facet[][] facets;
}
class Cube {
Cube(int n) {
u=new Face(n,Facet.u);
d=new Face(n,Facet.d);
r=new Face(n,Facet.r);
l=new Face(n,Facet.l);
f=new Face(n,Facet.f);
b=new Face(n,Facet.b);
}
void rotatex(Face face,Face old) {
for(int i=0;i<face.n;i++)
for(int j=0;i<face.n;j++)
face.facets[i][j]=old.facets[i][j];
}
void rotateConterClockwise(Face f,Face u,Face l,Face d,Face r) {
for(int i=0;i<3;i++)
rotate(f,u,l,d,r);
}
void rotate(Face f,Face u,Face l,Face d,Face r) {
Face.rotateClockwise(f.facets);
Facet[] top=new Facet[u.n];
for(int i=0;i<u.n;i++)
top[i]=u.facets[u.n-1][i];
for(int i=0;i<u.n;i++) // move left to u
u.facets[u.n-1][i]=l.facets[u.n-i-1][u.n-1];
for(int i=0;i<u.n;i++) // bottom to left
l.facets[u.n-i-1][u.n-1]=d.facets[0][i];
for(int i=0;i<u.n;i++) // right to bottom
d.facets[0][i]=r.facets[i][0];
for(int i=0;i<u.n;i++)
r.facets[i][0]=top[i];
}
void op(Facet facet) {
switch(facet) {
case b:
rotate(b,u,r,d,l);
break;
case d:
rotate(d,f,l,b,r);
break;
case f:
rotate(f,u,l,d,r);
break;
case l:
rotate(l,u,b,d,f);
break;
case r:
rotate(r,u,f,d,b);
break;
case u:
rotate(u,d,l,f,r);
break;
}
}
void opInverse(Facet facet) {
switch(facet) {
case b:
rotateConterClockwise(b,u,r,d,l);
break;
case d:
rotateConterClockwise(d,f,l,b,r);
break;
case f:
rotateConterClockwise(f,u,l,d,r);
break;
case l:
rotateConterClockwise(l,u,b,d,f);
break;
case r:
rotateConterClockwise(r,u,f,d,b);
break;
case u:
rotateConterClockwise(u,d,l,f,r);
break;
}
}
@Override public int hashCode() {
final int prime=31;
int result=1;
result=prime*result+((b==null)?0:b.hashCode());
result=prime*result+((d==null)?0:d.hashCode());
result=prime*result+((f==null)?0:f.hashCode());
result=prime*result+((l==null)?0:l.hashCode());
result=prime*result+((r==null)?0:r.hashCode());
result=prime*result+((u==null)?0:u.hashCode());
return result;
}
@Override public boolean equals(Object obj) {
if(this==obj) return true;
if(obj==null) return false;
if(getClass()!=obj.getClass()) return false;
Cube other=(Cube)obj;
if(b==null) {
if(other.b!=null) return false;
} else if(!b.equals(other.b)) return false;
if(d==null) {
if(other.d!=null) return false;
} else if(!d.equals(other.d)) return false;
if(f==null) {
if(other.f!=null) return false;
} else if(!f.equals(other.f)) return false;
if(l==null) {
if(other.l!=null) return false;
} else if(!l.equals(other.l)) return false;
if(r==null) {
if(other.r!=null) return false;
} else if(!r.equals(other.r)) return false;
if(u==null) {
if(other.u!=null) return false;
} else if(!u.equals(other.u)) return false;
return true;
}
@Override public String toString() { //ulfrbd
StringBuffer stringBuffer=new StringBuffer(3*4*(u.n+1));
for(int i=0;i<u.n;i++) {
stringBuffer.append(u.pad());
for(int j=0;j<u.n;j++)
stringBuffer.append(u.facets[i][j]);
stringBuffer.append('\n');
}
for(int i=0;i<u.n;i++) {
stringBuffer.append(l.pad());
for(int j=0;j<u.n;j++)
stringBuffer.append(l.facets[i][j]);
stringBuffer.append(' ');
for(int j=0;j<u.n;j++)
stringBuffer.append(f.facets[i][j]);
stringBuffer.append(' ');
for(int j=0;j<u.n;j++)
stringBuffer.append(r.facets[i][j]);
stringBuffer.append(' ');
for(int j=0;j<u.n;j++)
stringBuffer.append(b.facets[i][j]);
stringBuffer.append('\n');
}
for(int i=0;i<u.n;i++) {
stringBuffer.append(d.pad());
for(int j=0;j<u.n;j++)
stringBuffer.append(d.facets[i][j]);
stringBuffer.append('\n');
}
return stringBuffer.toString();
}
final Face u,d,r,l,f,b;
}
public class Main {
public static void main(String[] args) {
Cube initial=new Cube(3);
Cube cube=new Cube(3);
System.out.println(cube.u.facets[0][0]);
System.out.println(cube.u);
System.out.println(cube);
for(int i=0;i<4;i++) {
cube.op(Facet.f);
System.out.println(cube);
}
// F' U L' U'
cube=new Cube(3);
int i=0;
do {
fpulpup(cube);
System.out.println(i);
System.out.println(cube);
i++;
} while(!cube.equals(initial));
}
private static void fpulpup(Cube cube) {
cube.opInverse(f);
cube.op(u);
cube.opInverse(l);
cube.opInverse(f);
}
}
Upvotes: 0
Reputation: 391
Think about what data structure makes it the easiest for you to conceptualize the cube. The different solutions in the link you provided all have pros and cons. You can have a more terse structure (i.e. the five integer representation) which takes up less memory and optimizes performance. But that representation might be difficult to work with if you are new to the problem. On the other end of the spectrum is an object oriented representation that models the way most people would think about a rubik cube. That might be the best approach for someone new to rubik's cubes.
After you have a data structure that makes sense to you, think about the different operations that can be performed on the cube. You can capture each of these moves in a function that alters the state of the data. If you have a real rubik cube to play with, figure out what all the different turns are and how they change the values of the rubik cube. Then try to model them in their own functions. i.e. a top wise turn would cause five of the six faces to change, right. It would cause four of the faces to receive the top row of their neighbour. And it would cause a shift in the top face as well. The bottom face would be uneffected.
If you are feeling overwhelmed with the complexity try to break your problem down into a smaller one. Maybe you can try to write a representation for a rubik cube that is 2 x 2 rather than 3 x 3. Once you've mastered the smaller problem, return to the larger one.
Upvotes: 3