//=============================================================================
//
// 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.