// // The Rubix cube program // Copyright c Brian P. Vogl // September 15 - 16, 2000 // import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class rubix extends Applet implements ActionListener { StringBuffer undostack = new StringBuffer(); int autostop = 0, undostop = 0; long waittime = 500; int[][] CD = { //new int[6][9]; Color of each square { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // Green face { 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // Yellow face { 3, 3, 3, 3, 3, 3, 3, 3, 3 }, // Red face { 4, 4, 4, 4, 4, 4, 4, 4, 4 }, // Blue face { 5, 5, 5, 5, 5, 5, 5, 5, 5 }, // White face { 6, 6, 6, 6, 6, 6, 6, 6, 6 } }; // Orange face int[][][] AP = { //new int[12][6][2]; Rotation Rules {{4,6}, {3,6}, {3,0}, {4,2}, {3,2}, {3,8}}, {{4,7}, {3,7}, {3,3}, {4,1}, {3,1}, {3,5}}, {{4,8}, {3,8}, {3,6}, {4,0}, {3,0}, {3,2}}, {{2,6}, {5,0}, {1,0}, {5,2}, {2,0}, {4,0}}, {{2,7}, {5,3}, {1,3}, {5,1}, {2,3}, {4,3}}, {{2,8}, {5,6}, {1,6}, {5,0}, {2,6}, {4,6}}, {{1,6}, {0,6}, {0,8}, {1,2}, {0,2}, {0,0}}, {{1,7}, {0,7}, {0,5}, {1,1}, {0,1}, {0,3}}, {{1,8}, {0,8}, {0,2}, {1,0}, {0,0}, {0,6}}, {{5,6}, {2,8}, {4,8}, {2,2}, {5,8}, {1,8}}, {{5,7}, {2,5}, {4,5}, {2,1}, {5,5}, {1,5}}, {{5,8}, {2,2}, {4,2}, {2,0}, {5,2}, {1,2}} }; // Polygon vertices, (X,Y) polygon number (0-2) and index into CD array. int[][] QP = { //new int[5][54]; {139,473,475, 93,185,427,521, 47,139,231,381,473,475,567, 2, 93,185,276, // X 427,521, 48,139,230,381,473,475,567, 2, 94,184,276,427,474,521, 48,230, 381,428,520,567, 2, 94,184,276,382,474,566, 48,230,428,520, 94,184,474 }, { -8,-10,-10, 8, 8, 6, 6, 24, 24, 24, 22, 20, 20, 22, 40, 40, 40, 40, // Y 36, 36, 56, 56, 56, 52, 50, 50, 52, 70, 72, 72, 70, 66, 80, 66, 86, 86, 82, 96, 96, 82,100,102,102,100,112,112,112,116,116,128,128,132,132,144 }, {0,2,1,0,0,2,1,0,0,0,2,2,1,1,1,0,0,2,2,1,1,0,2,2,2,1,1, // Polygon type 1,1,2,2,2,0,1,1,2,2,0,0,1,1,1,2,2,0,0,0,1,2,0,0,1,2,0 }, {0,5,4,0,0,5,4,0,0,0,5,5,4,4,1,0,0,2,5,4,1,0,2,5,5,4,4, // X index into CD 1,1,2,2,5,3,4,1,2,5,3,3,4,1,1,2,2,3,3,3,1,2,3,3,1,2,3 }, {0,8,6,3,1,7,7,6,4,2,6,5,3,8,8,7,5,6,4,4,7,8,7,3,2,0,5, // Y index into CD 5,6,8,3,1,2,1,4,4,0,5,1,2,2,3,5,0,8,4,0,1,1,7,3,0,2,6 } }; int[][] RP = { //new int[4][42]; Points (X1,Y1)-(X2,Y2) lines { 1, 1,139,277,277,139, 1,139,231, 93,185, 47, 93,231, // X1 47,185,139, 1,139, 1,139,336,474,612,612,474,336,336, 474,566,428,520,382,428,566,382,520,474,336,474,336,474 }, { 39,129,177,129, 39, -9, 39, 87, 23, 71, 7, 55, 7, 55, // Y1 23, 71, 87, 69,117, 99,147,127,175,127, 37,-11, 37,127, 79,143, 95,159,111,159,111,143, 95, 79, 67, 19, 97, 49 }, { 1,139,277,277,139, 1,139,277, 93, 93, 47, 47,231,231, // X2 185,185,139,139,277,139,277,474,612,612,474,336,336,474, 612,428,428,382,382,566,566,520,520,474,474,612,474,612 }, {129,177,129, 39, -9, 39, 87, 39, 71,161, 55,145, 55,145, // Y2 71,161,177,117, 69,147, 99,175,127, 37,-11, 37,127, 79, 127, 95, 5,111, 21,111, 21, 95, 5,-11, 19, 67, 49, 97 } }; int[] PP = { 0,2,8,6,1,5,7,3 }; Color black = new Color(0x00,0x00,0x00); Color green = new Color(0xC6,0xEF,0x8C); Color yellow = new Color(0xFF,0xFF,0x9C); Color red = new Color(0xFF,0x63,0x42); Color blue = new Color(0x63,0xC6,0xDE); Color white = new Color(0xFF,0xFF,0xFF); Color orange = new Color(0xFF,0xB5,0x73); //////Misc buttons Button auto = new Button("Auto"); Button undo = new Button("Undo"); //////////////////ELIMINATED THE BUTTONS WE NOW CLICK SIDES OF THE CUBE /////////////////// //////Up buttons // Button greenup = new Button("Green"); // Button yellowup = new Button("Yellow"); // Button redup = new Button("Red"); // Button blueup = new Button("Blue"); // Button whiteup = new Button("White"); // Button orangeup = new Button("Orange"); //////Down buttons // Button greendn = new Button("Green"); // Button yellowdn = new Button("Yellow"); // Button reddn = new Button("Red"); // Button bluedn = new Button("Blue"); // Button whitedn = new Button("White"); // Button orangedn = new Button("Orange"); // // public class controlAreaUP extends Panel // { public controlAreaUP() // { // greenup.setBackground(green); // yellowup.setBackground(yellow); // redup.setBackground(red); // blueup.setBackground(blue); // whiteup.setBackground(white); // orangeup.setBackground(orange); // greenup.setActionCommand("GU"); // yellowup.setActionCommand("YU"); // redup.setActionCommand("RU"); // blueup.setActionCommand("BU"); // whiteup.setActionCommand("WU"); // orangeup.setActionCommand("OU"); // setLayout(new FlowLayout(FlowLayout.CENTER)); // add(greenup); // add(yellowup); // add(redup); // add(blueup); // add(whiteup); // add(orangeup); // } // } // // public class controlAreaDN extends Panel // { public controlAreaDN() // { // greendn.setBackground(green); // yellowdn.setBackground(yellow); // reddn.setBackground(red); // bluedn.setBackground(blue); // whitedn.setBackground(white); // orangedn.setBackground(orange); // greendn.setActionCommand("GD"); // yellowdn.setActionCommand("YD"); // reddn.setActionCommand("RD"); // bluedn.setActionCommand("BD"); // whitedn.setActionCommand("WD"); // orangedn.setActionCommand("OD"); // setLayout(new FlowLayout(FlowLayout.CENTER)); // add(greendn); // add(yellowdn); // add(reddn); // add(bluedn); // add(whitedn); // add(orangedn); // } // } // // public class controlArea extends Panel // { public controlArea() // { setLayout(new GridLayout(2,1)); // controlAreaUP CUP = new controlAreaUP(); // controlAreaDN CDN = new controlAreaDN(); // add(CUP); // add(CDN); // } // } ///////////////////// OBSOLETE USING SLEEP //////////////////////////////// // public void waitasec() // { long prevsec, nowsec; // Calendar dt; // dt = Calendar.getInstance(); // prevsec = dt.get(dt.SECOND); // do // { dt = Calendar.getInstance(); // nowsec = dt.get(dt.SECOND); // } // while(nowsec == prevsec); // } ///////////////////////////////////////////////////////////////////////////// Label title = new Label("The RUBIX Cube"); public class Line1 extends Panel { Font bigFont =new Font("TimesRoman", Font.BOLD, 22); public Line1() { setLayout(new FlowLayout(FlowLayout.CENTER)); title.setFont(bigFont); add(title); } } public class drawArea extends Panel { public drawArea() { } } public class buttonArea extends Panel { public buttonArea() { auto.setBackground(red); auto.setActionCommand("AUTO"); undo.setBackground(blue); undo.setActionCommand("UNDO"); // controlArea cent = new controlArea(); setLayout(new FlowLayout()); add(auto); // add(cent); add(undo); } } public void init() { addMouseListener(new MPListener()); Line1 NP = new Line1(); drawArea CP = new drawArea(); buttonArea SP = new buttonArea(); auto.addActionListener(this); undo.addActionListener(this); ////////////////////////////////////////////////////////// // greenup.addActionListener(this); // yellowup.addActionListener(this); // redup.addActionListener(this); // blueup.addActionListener(this); // whiteup.addActionListener(this); // orangeup.addActionListener(this); // greendn.addActionListener(this); // yellowdn.addActionListener(this); // reddn.addActionListener(this); // bluedn.addActionListener(this); // whitedn.addActionListener(this); // orangedn.addActionListener(this); //////////////////////////////////////////////////////////// setLayout(new BorderLayout()); add(NP, "North"); add(CP, "Center"); add(SP, "South"); Graphics gr = CP.getGraphics(); CP.setVisible(false); } public void paint(Graphics gr) // Draw the cube { int k, Xo, Yo; int polyX[] = new int[5]; int polyY[] = new int[5]; for(k=0; k < 42; k++) // Draw the cube grid first { gr.setColor(Color.black); gr.drawLine(RP[0][k]+20, (RP[1][k]+35)*2, RP[2][k]+20, (RP[3][k]+35)*2); } for(k=0; k<54; k++) { Xo = QP[0][k]+20; // Determine the offset of the polygons Yo = QP[1][k]+35; polyX[0] = Xo; polyY[0] = 2*Yo; polyX[4] = Xo; polyY[4] = 2*Yo; switch(QP[2][k]) { // Select the polygon to draw case 0: polyX[1] = Xo-45; polyY[1] = 2*(Yo+15); polyX[2] = Xo; polyY[2] = 2*(Yo+30); polyX[3] = Xo+45; polyY[3] = 2*(Yo+15); break; case 1: polyX[1] = Xo; polyY[1] = 2*(Yo+28); polyX[2] = Xo+44; polyY[2] = 2*(Yo+44); polyX[3] = Xo+44; polyY[3] = 2*(Yo+16); break; case 2: polyX[1] = Xo-44; polyY[1] = 2*(Yo+16); polyX[2] = Xo-44; polyY[2] = 2*(Yo+44); polyX[3] = Xo; polyY[3] = 2*(Yo+28); break; } switch(CD[QP[3][k]][QP[4][k]]) { // Select the color to appear on the face of the polygon case 1 : gr.setColor(Color.green); break; case 2 : gr.setColor(Color.yellow); break; case 3 : gr.setColor(Color.red); break; case 4 : gr.setColor(Color.blue); break; case 5 : gr.setColor(Color.white); break; case 6 : gr.setColor(Color.orange); break; } // Finally draw the polygon. gr.fillPolygon(polyX, polyY, polyX.length); } showStatus(Integer.toString(undostack.length()) + " Rotations."); } class MMListener extends MouseMotionAdapter { public void mouseDragged(MouseEvent e) {} public void mouseMoved(MouseEvent e) { int xs=e.getX(); int ys=e.getY(); showStatus("(" + Integer.toString(xs) + ", " + Integer.toString(ys) + ")"); } } class MPListener extends MouseAdapter { public void mousePressed(MouseEvent e) { // Determine which face and which button was pressed over it int xs=e.getX()-20; int ys=e.getY()/2-35; int fc = -1; if(incenter( 6, 5, 7, 4, xs, ys)) fc = 0; // GREEN Face if(incenter( 1, 6,16, 0, xs, ys)) fc = 1; // YELLOW Face if(incenter( 2, 7, 3,16, xs, ys)) fc = 2; // RED Face if(incenter(21,27,22,28, xs, ys)) fc = 3; // BLUE Face if(incenter(28,24,23,37, xs, ys)) fc = 4; // WHITE Face if(incenter(27,25,37,26, xs, ys)) fc = 5; // ORANGE Face int buttstate = e.getModifiers(); if(fc >= 0) { undostop = 0; // stop undoing, were going to add a new one. autostop = 0; // stop autoing, were clicking on faces. if(buttstate == e.BUTTON1_MASK && fc >= 0) { undostack.append((char)(fc + 6)); rotateDn(fc); } if(buttstate == e.BUTTON3_MASK && fc >= 0) { undostack.append((char)(fc + 0)); rotateUp(fc); } check4done(); repaint(); // Finally Redraw the cube. } } public void mouseReleased(MouseEvent e) { } } boolean incenter(int rp1, int rp2, int rp3, int rp4, int xc, int yc) { // Returns true if point is in the center of the box bounded by the 4 RP lines. return( leftofline(rp1, xc, yc) && !leftofline(rp2, xc, yc) && leftofline(rp3, xc, yc) && !leftofline(rp4, xc, yc)); } boolean leftofline(int rp, int xc, int yc) { // Returns true if point is left of or below the RP line if(RP[2][rp] == RP[0][rp]) return ( xc < RP[0][rp] ); double m = ((double)(RP[3][rp] - RP[1][rp]) / (double)(RP[2][rp] - RP[0][rp])); int b = RP[1][rp] - (int)(RP[0][rp] * m); return ( yc < ( m * xc + b )); } public void actionPerformed(ActionEvent Event) { // Determine which button has been pressed String buton = new String(Event.getActionCommand()); int fc = 0; switch(buton.charAt(0)) { case 'U': undoall(); return; case 'A': automode(); return; case 'G': fc = 0; break; case 'Y': fc = 1; break; case 'R': fc = 2; break; case 'B': fc = 3; break; case 'W': fc = 4; break; case 'O': fc = 5; break; } undostop = 0; // stop undoing, were going to add a new one. autostop = 0; // stop autoing, were clicking on buttons. switch(buton.charAt(1)) { case 'U': undostack.append((char)(fc + 0)); rotateUp(fc); break; case 'D': undostack.append((char)(fc + 6)); rotateDn(fc); break; } check4done(); repaint(); // Finally Redraw the cube. } public void automode() // Use a thread to randomly rotate the cube. { if(autostop > 0) autostop = 0; else { undostop = 0; autostop++; autothread at = new autothread(); at.start(); } } public void undoall() // Use a thread to undo all previous rotations { if(undostop > 0) undostop = 0; else { autostop = 0; undostop++; undothread ut = new undothread(); ut.start(); } } class autothread extends Thread { public void run() { int rfc; double rn; Random ran = new Random(); try { sleep(waittime); } catch( InterruptedException e) {} while(autostop > 0) { rn = ran.nextDouble(); if(rn < 1.0) { rfc = (int) (rn * 12); if(rfc < 6) { undostack.append((char)(rfc)); rotateUp(rfc - 0); } else { undostack.append((char)(rfc)); rotateDn(rfc - 6); } check4done(); } repaint(); try { sleep(waittime); } catch( InterruptedException e) {} } } } class undothread extends Thread { public void run() { int t; try { sleep(waittime); } catch( InterruptedException e) {} while((t = undostack.length()) > 0 && undostop > 0) { if(undostack.charAt(t-1) < 6) rotateDn(undostack.charAt(t-1) - 0); else rotateUp(undostack.charAt(t-1) - 6); repaint(); undostack.setLength(t-1); check4done(); try { sleep(waittime); } catch( InterruptedException e) {} } } } public void rotateUp(int fc) // An upper button was desired { int[] TCD = new int[12]; int n, t, p, tmp; for(n=0; n<=1; n++) // Translate the face of the cube { tmp = CD[fc][PP[n*4]]; for(t=0; t<=2; t++) CD[fc][PP[n*4+t]] = CD[fc][PP[n*4+t+1]]; CD[fc][PP[n*4+3]] = tmp; } // Translate the sides of the cube for(p=0; p<12; p++) TCD[p] = CD[AP[p][fc][0]][AP[p][fc][1]]; for(n=0; n<=2; n++) { tmp = TCD[n]; for(t=0; t<=2; t++) TCD[n+t*3] = TCD[n+(t+1)*3]; TCD[n+9] = tmp; } for(p=0; p<12; p++) CD[AP[p][fc][0]][AP[p][fc][1]] = TCD[p]; } public void rotateDn(int fc) // A lower button was desired { int[] TCD = new int[12]; int n, t, p, tmp; for(n=1; n>=0; n--) // Translate the face of the cube { tmp = CD[fc][PP[n*4+3]]; for(t=2; t>=0; t--) CD[fc][PP[n*4+t+1]] = CD[fc][PP[n*4+t]]; CD[fc][PP[n*4]] = tmp; } // Translate the sides of the cube for(p=0; p<12; p++) TCD[p] = CD[AP[p][fc][0]][AP[p][fc][1]]; for(n=2; n>=0; n--) { tmp = TCD[n+9]; for(t=2; t>=0; t--) TCD[n+(t+1)*3] = TCD[n+t*3]; TCD[n] = tmp; } for(p=0; p<12; p++) CD[AP[p][fc][0]][AP[p][fc][1]] = TCD[p]; } public void check4done() { int t,k; for(t=0; t<6; t++) { for(k=0; k<9; k++) { if(CD[t][k] != (t+1)) { return; } } } undostack.setLength(0); } }