//============================================================================= // // Rubik's Cube Java Applet // // Version 1.3 -- May 2 1999 // // Copyright 2000 Michael Schubart (michael@schubart.net) // http://www.schubart.net/ // // The applet plus additional information can be found at // http://www.schubart.net/rc/ // // In order to run the applet you also need these files: // http://www.schubart.net/rc/rc_back.gif // http://www.schubart.net/rc/rc_shapes.gif // http://www.schubart.net/rc/rc_move.au // http://www.schubart.net/rc/rc_bad.au // They have to be in the same directory as the .class files. // //============================================================================= import java.applet.Applet; import java.applet.AudioClip; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.Event; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.image.CropImageFilter; import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.Label; import java.awt.MediaTracker; import java.awt.Panel; import java.awt.TextField; import java.net.MalformedURLException; import java.net.URL; //============================================================================= // The RCCubelet class represents one of the 27 cubelets the cube is made of. //============================================================================= class RCCubelet { // names for the faces public final static int FACE_NONE=-1; // none public final static int FACE_XV=0; // orthogonal to x-axis and visible public final static int FACE_XH=1; // orthogonal to x-axis and hidden public final static int FACE_YV=2; // and so on public final static int FACE_YH=3; public final static int FACE_ZV=4; public final static int FACE_ZH=5; // names for the axes public final static int AXIS_X=0; public final static int AXIS_Y=1; public final static int AXIS_Z=2; // stores the color of each of the 6 faces protected int[] colors=new int[6]; //========================================================================= // Constructor, initializes each of the faces with a different color. //========================================================================= public RCCubelet() { for (int i = 0; i < 6; i++) { colors[i] = i; } } //========================================================================= // Can be used to query the color of one face. // return value: the color //========================================================================= public int getColor(int face) { return colors[face]; } //========================================================================= // Rotate the cubelet. // axis: the axis around which to rotate // direction: the direction in which to rotate //========================================================================= public void rotate(int axis, int direction) { int buffer; switch (axis) { case AXIS_X: if (direction == 1) { buffer = colors[FACE_YV]; colors[FACE_YV] = colors[FACE_ZH]; colors[FACE_ZH] = colors[FACE_YH]; colors[FACE_YH] = colors[FACE_ZV]; colors[FACE_ZV] = buffer; } else if (direction==-1) { buffer = colors[FACE_YV]; colors[FACE_YV] = colors[FACE_ZV]; colors[FACE_ZV] = colors[FACE_YH]; colors[FACE_YH] = colors[FACE_ZH]; colors[FACE_ZH] = buffer; } break; case AXIS_Y: if (direction == 1) { buffer = colors[FACE_XV]; colors[FACE_XV] = colors[FACE_ZV]; colors[FACE_ZV] = colors[FACE_XH]; colors[FACE_XH] = colors[FACE_ZH]; colors[FACE_ZH] = buffer; } else if (direction == -1) { buffer = colors[FACE_XV]; colors[FACE_XV] = colors[FACE_ZH]; colors[FACE_ZH] = colors[FACE_XH]; colors[FACE_XH] = colors[FACE_ZV]; colors[FACE_ZV] = buffer; } break; case AXIS_Z: if (direction == 1) { buffer = colors[FACE_XV]; colors[FACE_XV] = colors[FACE_YH]; colors[FACE_YH] = colors[FACE_XH]; colors[FACE_XH] = colors[FACE_YV]; colors[FACE_YV] = buffer; } else if (direction == -1) { buffer = colors[FACE_XV]; colors[FACE_XV] = colors[FACE_YV]; colors[FACE_YV] = colors[FACE_XH]; colors[FACE_XH] = colors[FACE_YH]; colors[FACE_YH] = buffer; } break; } } } //============================================================================= // The RCCube class represents the whole cube, which is made of 27 cubelets. //============================================================================= class RCCube { // the cubelets protected RCCubelet[][][] cubelet = new RCCubelet[3][3][3]; protected int[][][][] moves = { // AXIS_X {{{0,0,0}, {0,0,2}, {0,2,2}, {0,2,0}, {0,0,1}, {0,1,2}, {0,2,1}, {0,1,0}}, {{1,0,0}, {1,0,2}, {1,2,2}, {1,2,0}, {1,0,1}, {1,1,2}, {1,2,1}, {1,1,0}}, {{2,0,0}, {2,0,2}, {2,2,2}, {2,2,0}, {2,0,1}, {2,1,2}, {2,2,1}, {2,1,0}}}, // AXIS_Y {{{0,0,0}, {2,0,0}, {2,0,2}, {0,0,2}, {1,0,0}, {2,0,1}, {1,0,2}, {0,0,1}}, {{0,1,0}, {2,1,0}, {2,1,2}, {0,1,2}, {1,1,0}, {2,1,1}, {1,1,2}, {0,1,1}}, {{0,2,0}, {2,2,0}, {2,2,2}, {0,2,2}, {1,2,0}, {2,2,1}, {1,2,2}, {0,2,1}}}, // AXIS_Z {{{0,0,0}, {0,2,0}, {2,2,0}, {2,0,0}, {0,1,0}, {1,2,0}, {2,1,0}, {1,0,0}}, {{0,0,1}, {0,2,1}, {2,2,1}, {2,0,1}, {0,1,1}, {1,2,1}, {2,1,1}, {1,0,1}}, {{0,0,2}, {0,2,2}, {2,2,2}, {2,0,2}, {0,1,2}, {1,2,2}, {2,1,2}, {1,0,2}}} }; //========================================================================= // Constructor, creates the cubelets. Since all the cubelets are equal, // the cube is initially in its "solved" state. //========================================================================= public RCCube() { for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y ++) { for (int z = 0; z < 3; z++) { cubelet[x][y][z] = new RCCubelet(); } } } } //========================================================================= // Can be used to query the color of one face of one cubelet. // xPos, yPos, zPos: the coordinates of the cubelet // face: the face of that cubelet // return value: the color //========================================================================= public int getColor(int xPos, int yPos, int zPos, int face) { return cubelet[xPos][yPos][zPos].getColor(face); } //========================================================================= // Rotates one slice of the cube or the whole cube. // axis: the axis around which to rotate // direction: the direction in which to rotate // slice: the depth of the slice to rotate // wholeCube: true -> the whole cube is to be rotated. ignore "slice" //========================================================================= public void rotate(int axis, int direction, int slice, boolean wholeCube) { RCCubelet buffer; // saves one cubelet in circular swaps int[][] m = moves[axis][slice]; // the move RCCubelet[][][] c = cubelet; // shorter name, faster // rotate the whole cube? then rotate each slice, then quit if (wholeCube) { for (int i = 0; i < 3; i++) { rotate(axis, direction, i, false); } return; } if (direction == 1) { // move corner cubelets buffer = cubelet[m[0][0]][m[0][1]][m[0][2]]; c[m[0][0]][m[0][1]][m[0][2]] = c[m[1][0]][m[1][1]][m[1][2]]; c[m[1][0]][m[1][1]][m[1][2]] = c[m[2][0]][m[2][1]][m[2][2]]; c[m[2][0]][m[2][1]][m[2][2]] = c[m[3][0]][m[3][1]][m[3][2]]; c[m[3][0]][m[3][1]][m[3][2]] = buffer; // move edge cubelets buffer = c[m[4][0]][m[4][1]][m[4][2]]; c[m[4][0]][m[4][1]][m[4][2]] = c[m[5][0]][m[5][1]][m[5][2]]; c[m[5][0]][m[5][1]][m[5][2]] = c[m[6][0]][m[6][1]][m[6][2]]; c[m[6][0]][m[6][1]][m[6][2]] = c[m[7][0]][m[7][1]][m[7][2]]; c[m[7][0]][m[7][1]][m[7][2]] = buffer; } else { // move corner cubeletss buffer = c[m[3][0]][m[3][1]][m[3][2]]; c[m[3][0]][m[3][1]][m[3][2]] = c[m[2][0]][m[2][1]][m[2][2]]; c[m[2][0]][m[2][1]][m[2][2]] = c[m[1][0]][m[1][1]][m[1][2]]; c[m[1][0]][m[1][1]][m[1][2]] = c[m[0][0]][m[0][1]][m[0][2]]; c[m[0][0]][m[0][1]][m[0][2]] = buffer; // move edge cubelets buffer = c[m[7][0]][m[7][1]][m[7][2]]; c[m[7][0]][m[7][1]][m[7][2]] = c[m[6][0]][m[6][1]][m[6][2]]; c[m[6][0]][m[6][1]][m[6][2]] = c[m[5][0]][m[5][1]][m[5][2]]; c[m[5][0]][m[5][1]][m[5][2]] = c[m[4][0]][m[4][1]][m[4][2]]; c[m[4][0]][m[4][1]][m[4][2]] = buffer; } // rotate the cubelets that have been moved for (int i = 0; i < 8; i++) { c[m[i][0]][m[i][1]][m[i][2]].rotate(axis, direction); } // P.S.: I know, the comments here are a little sparse. I guess // you'll just have to trust me. :-) } //========================================================================= // Brings the cube to disorder. This is done by performing a large number // of random moves, because not all of the cube's possible permutations // can be solved. // moves: how many moves //========================================================================= public void scramble(int moves) { for (int i = 0; i < moves; i++) { int axis = (int) (Math.random() * 3); int direction = (Math.random() < 0.5) ? 1 : -1; int slice = (int) (Math.random() * 3); rotate(axis, direction, slice, false); } } //========================================================================= // Can be used to query whether the cube is in its "solved" state. This is // done by comparing each of the cubelets' faces that form a face of the // cube with exactly one of them. There must be no differences. // return value: true if solved //========================================================================= public boolean isSolved() { // check faces FACE_XV and FACE_XH for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { if (getColor(2, y, z, RCCubelet.FACE_XV) != getColor(2, 0, 0, RCCubelet.FACE_XV) || getColor(0, y, z, RCCubelet.FACE_XH) != getColor(0, 0, 0, RCCubelet.FACE_XH)) { return false; } } } // check faces FACE_YV and FACE_YH for (int x = 0; x < 3; x++) { for (int z = 0; z < 3; z++) { if (getColor(x, 2, z, RCCubelet.FACE_YV) != getColor(0, 2, 0, RCCubelet.FACE_YV) || getColor(x, 0, z, RCCubelet.FACE_YH) != getColor(0, 0, 0, RCCubelet.FACE_YH)) { return false; } } } // check faces FACE_YV and FACE_YH for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { if (getColor(x, y, 2, RCCubelet.FACE_ZV) != getColor(0, 0, 2, RCCubelet.FACE_ZV) || getColor(x, y, 0, RCCubelet.FACE_ZH) != getColor(0, 0, 0, RCCubelet.FACE_ZH)) { return false; } } } return true; } } //============================================================================= // The RCDisplayEntry describes how to draw one face of one cubelet on the // screen. This class works like a struct in C. //============================================================================= class RCDisplayEntry { // which cubelet is it? public int xSource, ySource, zSource; // and which face on that cubelet? public int faceSource; // where on the screen does it belong? public int xOffset, yOffset; // and how does it look there? public int shape; //========================================================================= // Constructor, copies the given values. //========================================================================= public RCDisplayEntry(int xSource, int ySource, int zSource, int faceSource, int xOffset, int yOffset, int shape) { this.xSource = xSource; this.ySource = ySource; this.zSource = zSource; this.faceSource = faceSource; this.xOffset = xOffset; this.yOffset = yOffset; this.shape=shape; } } //============================================================================= // The RCSurfacePoint is used to describe one point on the surface of the cube, // i.e. one face of one cubelet. Instances can be created form a (x,y) pair of // screen coordinates, the class knows how to transform them. // // The class ignores the "mirrors" of the hidden faces. These are only drawn // and can't be used to manipulate the cube. //============================================================================= class RCSurfacePoint { // on which cubelet lies the point? public int cubeX; public int cubeY; public int cubeZ; // and on which face of that cubelet? public int face; //========================================================================= // Constructor, finds out on which face of the cube the point is. Does // so by trying each face. // xPos, yPos: screen coordinates // xOffset, yOffset: offset, used when the background image does not start // at (0,0) //========================================================================= public RCSurfacePoint(int xPos, int yPos, int xOffset, int yOffset) { transform(xPos, yPos, xOffset, yOffset, RCCubelet.FACE_XV); if (!isOnCube()) { transform(xPos, yPos, xOffset, yOffset, RCCubelet.FACE_YV); } if (!isOnCube()) { transform(xPos, yPos, xOffset, yOffset, RCCubelet.FACE_ZV); } } //========================================================================= // Constructor, we specify on which face of the cube the point is. // xPos, yPos: screen coordinates // xOffset, yOffset: background image offset // face: the point is on this face //========================================================================= public RCSurfacePoint(int xPos, int yPos, int xOffset, int yOffset, int face) { transform(xPos, yPos, xOffset, yOffset, face); } //========================================================================= // Can be used to query whether the object describes an existing surface // point. // return value: yes, it does //========================================================================= public boolean isOnCube() { return (cubeX >= 0 && cubeY >=0 && cubeZ >=0 && cubeX <= 2 && cubeY <=2 && cubeZ <=2); } //========================================================================= // Transforms screen coordinates to a surface point, we specify on which // face of the cube the point is. // xPos, yPos: screen coordinates // xOffset, yOffset: background image offset // face: the point is on this face //========================================================================= protected void transform(int xPos, int yPos, int xOffset, int yOffset, int face) { double x,y; switch (face) { case RCCubelet.FACE_XV: x = xPos - 237 - xOffset; y = yPos - 182 - yOffset; cubeX = 2; cubeY = (int) Math.floor(-x / 64 - y / 32); cubeZ = (int) Math.floor(-x / 28); break; case RCCubelet.FACE_YV: x = xPos - 153 - xOffset; y = yPos - 45 - yOffset; cubeX = (int) Math.floor(x / 56 + y / 28); cubeY = 2; cubeZ = (int) Math.floor(-x / 56 + y / 28); break; case RCCubelet.FACE_ZV: x = xPos - 70 - xOffset; y = yPos - 182 - yOffset; cubeX = (int) Math.floor (x / 28); cubeY = (int) Math.floor (x / 64 - y / 32); cubeZ = 2; break; } this.face=face; } } //============================================================================= // The RCWinFrame class is used for showing a success message. //============================================================================= class RCWinFrame extends Frame { protected Label label = new Label("You win!"); protected Button ok = new Button("OK"); //========================================================================= // Constructor. //========================================================================= public RCWinFrame() { super("Rubik's Cube"); label.setAlignment(Label.CENTER); add("Center", label); add("South", ok); pack(); resize(150, 100); } //========================================================================= // Hide window. //========================================================================= public boolean action(Event event, Object what) { if (event.target == ok) { hide(); return true; } return false; } //========================================================================= // Hide window. //========================================================================= public boolean handleEvent(Event event) { if (event.id == Event.WINDOW_DESTROY) { hide(); } return super.handleEvent(event); } } //============================================================================= // The RubiksCubeApplet class implements a simulation of Rubik's Cube. //============================================================================= public class RubiksCubeApplet extends Applet { // names for the shapes (the different looks cubelet faces can have) public final static int SHAPE_XF = 0; // on FACE_X, fully visible public final static int SHAPE_XP = 1; // on FACE_X, partialy visible public final static int SHAPE_YF = 2; // and so on public final static int SHAPE_YP = 3; public final static int SHAPE_ZF = 4; public final static int SHAPE_ZP = 5; // can be used to translate the graphics within the applet public final static int CUBEXOFFSET = 30; public final static int CUBEYOFFSET = 0; // describes where and how each face gets drawn // 6 cube faces, each with 3*3 cubelet faces -> 52 visible cubelet faces public final static RCDisplayEntry[] displayTable = new RCDisplayEntry[54]; //========================================================================= // This unnamed static function initializes the display table. Don't ask me // to explain the computations. It was a dirty job, but someone had to do // it... //========================================================================= static { int i=0; for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { displayTable[i++] = new RCDisplayEntry (2, y, z, RCCubelet.FACE_XV, 211 - 28 * z, 153 - 32 * y + 14 * z, SHAPE_XF); displayTable[i++] = new RCDisplayEntry (0, y, z, RCCubelet.FACE_XH, 58 - 28 * z, 67 - 32 * y + 14 * z, (y==0 && z==0) ? SHAPE_XP : SHAPE_XF); } } for (int x = 0; x < 3; x++) { for (int z = 0; z < 3; z++) { displayTable[i++] = new RCDisplayEntry (x, 2, z, RCCubelet.FACE_YV, 129 + 28 * x - 28 * z, 46 + 14 * x + 14 * z, SHAPE_YF); displayTable[i++] = new RCDisplayEntry (x, 0, z, RCCubelet.FACE_YH, 129 + 28 * x - 28 * z, 210 + 14 * x + 14 * z, (x==0 && z==0) ? SHAPE_YP : SHAPE_YF); } } for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { displayTable[i++] = new RCDisplayEntry (x, y, 2, RCCubelet.FACE_ZV, 71 + 28 * x, 153 - 32 * y + 14 * x, SHAPE_ZF); displayTable[i++] = new RCDisplayEntry (x ,y, 0, RCCubelet.FACE_ZH, 224 + 28 * x, 67 - 32 * y + 14 * x, (x==0 && y==0) ? SHAPE_ZP : SHAPE_ZF); } } } // the background image protected static Image background; // images of the shapes (6 shapes in 6 colors) protected static Image[][] shape = new Image[6][6]; // audio clips protected static AudioClip moveSound; protected static AudioClip badSound; // the cube itself protected RCCube cube = new RCCube(); // where did the user start dragging with the mouse? protected RCSurfacePoint dragStart; // did they press the Ctrl key then? protected boolean dragCtrlKey; // do we count the number of moves? protected boolean countMoves = false; // the components protected Button scramble = new Button("Scramble"); protected Button giveUp = new Button("Give Up"); protected TextField moves = new TextField(5); protected Checkbox sound = new Checkbox("Sound"); protected Frame success = new RCWinFrame(); public void init() { Panel p = new Panel(); Panel b = new Panel(); Panel o = new Panel(); // create a RCSurfacePoint object - makes sure the class has been // loaded when the user starts playing new RCSurfacePoint(0,0,0,0); // we want something on the bottom setLayout(new BorderLayout()); add("South", p); // on the left side: 2 buttons, 1 checkbox p.setLayout(new BorderLayout()); p.add("West", b); b.add(scramble); b.add(giveUp); b.add(sound); sound.setState(true); // on the right side: label and text field p.add("East", o); o.add(new Label("Moves:")); o.add(moves); // text field is read only moves.setEditable(false); // have the images been loaded by another instance? if (background == null) { MediaTracker tracker = new MediaTracker(this); Image shapes; showStatus("Loading images, please wait..."); background = getImage(getCodeBase(), "rc_back.gif"); tracker.addImage(background, 0); shapes = getImage(getCodeBase(), "rc_shapes.gif"); for (int colorNo = 0; colorNo < 6; colorNo++) { for (int shapeNo = 0; shapeNo < 6; shapeNo++) { // for each shape/color combination: crop a small image // out of the image with all shapes. avoids loading // 54 GIFs through the net. ImageFilter crop = new CropImageFilter(colorNo * 50, shapeNo * 42, 50, 42); shape[colorNo][shapeNo] = createImage (new FilteredImageSource(shapes.getSource(), crop)); tracker.addImage(shape[colorNo][shapeNo], 0); } } // wait for all images try { tracker.waitForAll(); } catch (InterruptedException e) { } } // load sounds moveSound = getAudioClip(getCodeBase(), "rc_move.au"); badSound = getAudioClip(getCodeBase(), "rc_bad.au"); } //========================================================================= // Gets called when the user selects Applet|Info in appletviewer. // return value: author, verison and copyright info //========================================================================= public String getAppletInfo() { return "RubiksCubeApplet\n" + "Version 1.3 -- May 2 1999\n\n" + "Copyright 2000 Michael Schubart\n" + "(michael@schubart.net)\n" + "http://www.schubart.net/"; } //========================================================================= // Gets called when the user selects Applet|Info in appletviewer. // return value: what parameters does the applet understand? //========================================================================= public String[][] getParameterInfo() { String[][] pinfo= { {"", "No parameters.", ""}, }; return pinfo; } //========================================================================= // Gets called when the applet needs to be redrawn. //========================================================================= public void paint(Graphics g) { // workaround for bug in some versions of Java (like 1.0.2 on Win32) g = getGraphics(); // first the background... g.drawImage(background, CUBEXOFFSET, CUBEYOFFSET, this); // ...then the cubelet faces for (int faceNo = 0; faceNo < displayTable.length; faceNo++) { drawFace(g, faceNo); } } //========================================================================= // Draws one face of one cubelet. // faceNo: the number of the display table entry that describes which // cubelet face gets drawn where //========================================================================= public void drawFace(Graphics g, int faceNo) { // get the shape int shapeNo = displayTable[faceNo].shape; // get the color int colorNo = cube.getColor(displayTable[faceNo].xSource, displayTable[faceNo].ySource, displayTable[faceNo].zSource, displayTable[faceNo].faceSource); // then draw it g.drawImage(shape[colorNo][shapeNo], displayTable[faceNo].xOffset + CUBEXOFFSET, displayTable[faceNo].yOffset + CUBEYOFFSET, this); } //========================================================================= // Gets called when the user clicks with the mouse. We start observing // what the mouse does. //========================================================================= public boolean mouseDown(Event event, int x, int y) { // are we for some reason already observing the mouse? if (dragStart != null) { // then ignore the click return true; } // where did the user click? dragStart = new RCSurfacePoint(x, y, CUBEXOFFSET, CUBEYOFFSET); // was the Ctrl key pressed? dragCtrlKey = (event.modifiers & java.awt.Event.CTRL_MASK) != 0; // is it a point on the cube? if (!dragStart.isOnCube()) { // no -> forget that point, don't observe the mouse dragStart = null; if (sound.getState()) { badSound.play(); } } return true; } //========================================================================= // Gets called when the user releases the mouse button. We compute a // vector from the 2 points where the mouse button was clicked and // released. If it's a legal command, we twist the cube accordingly. //========================================================================= public boolean mouseUp(Event event, int x, int y) { // are we observing the mouse? if (dragStart == null) { // no -> ignore the event return true; } // what colors are displayed where? int[] cache = new int[displayTable.length]; RCSurfacePoint dragStop = new RCSurfacePoint (x, y, CUBEXOFFSET, CUBEYOFFSET, dragStart.face); // how are the 2 points relative to each other? int deltaX = mysign(dragStop.cubeX - dragStart.cubeX); int deltaY = mysign(dragStop.cubeY - dragStart.cubeY); int deltaZ = mysign(dragStop.cubeZ - dragStart.cubeZ); // did we twist the cube? boolean rotation = false; // get all the old color values for (int faceNo = 0; faceNo < displayTable.length; faceNo++) { cache[faceNo] = cube.getColor(displayTable[faceNo].xSource, displayTable[faceNo].ySource, displayTable[faceNo].zSource, displayTable[faceNo].faceSource); } // twist the cube according to the 2 points switch (dragStart.face) { case RCCubelet.FACE_XV: if (deltaY == 0 && deltaZ != 0) { cube.rotate(RCCubelet.AXIS_Y, -deltaZ, dragStart.cubeY, dragCtrlKey); rotation = true; } else if (deltaY != 0 && deltaZ == 0) { cube.rotate(RCCubelet.AXIS_Z, deltaY, dragStart.cubeZ, dragCtrlKey); rotation=true; } break; case RCCubelet.FACE_YV: if (deltaX == 0 && deltaZ != 0) { cube.rotate(RCCubelet.AXIS_X, deltaZ, dragStart.cubeX, dragCtrlKey); rotation = true; } else if (deltaX != 0 && deltaZ == 0) { cube.rotate(RCCubelet.AXIS_Z, -deltaX, dragStart.cubeZ, dragCtrlKey); rotation = true; } break; case RCCubelet.FACE_ZV: if (deltaX == 0 && deltaY != 0) { cube.rotate(RCCubelet.AXIS_X, -deltaY, dragStart.cubeX, dragCtrlKey); rotation = true; } else if (deltaX != 0 && deltaY == 0) { cube.rotate(RCCubelet.AXIS_Y, deltaX, dragStart.cubeY, dragCtrlKey); rotation = true; } break; } // stop observing the mouse dragStart = null; // did we twist it? if (rotation) { Graphics g = getGraphics(); // success message stil visible? -> hide if (success.isVisible()) { success.hide(); } // make some noise if (sound.getState() && !dragCtrlKey) { moveSound.play(); } // draw only those faces that have a different color now for (int faceNo = 0;faceNo < displayTable.length; faceNo++) { int cacheColor = cache[faceNo]; int color = cube.getColor(displayTable[faceNo].xSource, displayTable[faceNo].ySource, displayTable[faceNo].zSource, displayTable[faceNo].faceSource); if (color != cacheColor) { drawFace(g,faceNo); } } // do we currently count the moves and has the cube's // cube state really changed? if (countMoves && !dragCtrlKey) { int count = Integer.parseInt(moves.getText()); // increase the number of moves displayed by 1 moves.setText(String.valueOf(count + 1)); // are we there yet? if (cube.isSolved()) { // stop counting countMoves = false; showStatus("You win!"); // show message success.show(); } } } else if (sound.getState()) { badSound.play(); } return true; } //========================================================================= // Convenience function. // return value: 1 if val > 0 // 0 if val = 0 // -1 if val < 0 //========================================================================= protected int mysign(int val) { return (val == 0) ? 0 : ((val > 0) ? 1 : -1); } //========================================================================= // Gets called when the user pressed one of the buttons. //========================================================================= public boolean action(Event event, Object what) { // was it the "Scramble" button? if (event.target == scramble) { // start counting moves countMoves = true; // initialize moves counter moves.setText("0"); // start with a clean cube, forget the old one, let garbage // collection take care of it cube = new RCCube(); // scamble it showStatus("Scramble, scramble..."); cube.scramble(500); // see what you've done! repaint(); return true; } // was it the "Give Up" button? if (event.target == giveUp) { // stop counting moves countMoves = false; // clear counter moves.setText(""); // create a clean cube, dump the old one cube = new RCCube(); // show it repaint(); return true; } return false; } } // That's it! Thank you, good night.