The Piece Class
Before we start designing the Piece
class, we need to consider what this class should represent. When you
look at Figure 2, you can see that a Meteor puzzle piece consists of
five connected cells. Each cell is a regular hexagon with six sides:
EAST, SOUTHEAST, SOUTHWEST, WEST, NORTHWEST, and NORTHEAST. When two
cells of a piece are joined at a particular side, we call these cells neighbours. In the end, a Piece
object is nothing more than a set of five connected Cell
objects. Each Cell
object has six sides and six possible neighbouring cells. Implementing the Cell
class is straightforward, as shown in Listing 2. Note that we maintain a processed
flag in a Cell
object. We will use this flag later on to avoid infinite loops.
Figure 2. A puzzle piece and its cells
Listing 2. The Cell class
public class Cell {
public static final int NUMBEROFSIDES = 6;
// The sides of a cell
public static final int EAST = 0;
public static final int SOUTHEAST = 1;
public static final int SOUTHWEST = 2;
public static final int WEST = 3;
public static final int NORTHWEST = 4;
public static final int NORTHEAST = 5;
private Cell[] neighbours = new Cell[NUMBEROFSIDES];
private boolean processed = false;
public Cell getNeighbour(int side) {
return neighbours[side];
}
public void setNeighbour(int side, Cell cell) {
neighbours[side] = cell;
}
public boolean isProcessed() {
return processed;
}
public void setProcessed(boolean b) {
processed = b;
}
}
|
The Piece
class
is more interesting because we need a method to calculate the
permutations of a Piece. We can find all permutations by first rotating
the piece around the six sides of one of its cells, flipping it upside
down, and finally rotating it again around the six sides of one of its
cells. As we mentioned before, a piece consists of five adjacent cells.
Flipping or rotating the piece is simply flipping or rotating all of
its cells. So we need flip()
and rotate()
methods for Cell
objects. Both flipping and rotating are easily accomplished by changing
the neighbouring sides accordingly. These methods are provided in the PieceCell
subclass of the Cell
class, shown in Listing 3. A PieceCell
object is a cell used in a Piece
object.
Listing 3. The PieceCell subclass
public class PieceCell extends Cell {
public void flip() {
Cell buffer = getNeighbour(NORTHEAST);
setNeighbour(NORTHEAST, getNeighbour(NORTHWEST));
setNeighbour(NORTHWEST, buffer);
buffer = getNeighbour(EAST);
setNeighbour(EAST, getNeighbour(WEST));
setNeighbour(WEST, buffer);
buffer = getNeighbour(SOUTHEAST);
setNeighbour(SOUTHEAST, getNeighbour(SOUTHWEST));
setNeighbour(SOUTHWEST, buffer);
}
public void rotate() {
// Clockwise rotation
Cell eastNeighbour = getNeighbour(EAST);
setNeighbour(EAST, getNeighbour(NORTHEAST));
setNeighbour(NORTHEAST, getNeighbour(NORTHWEST));
setNeighbour(NORTHWEST, getNeighbour(WEST));
setNeighbour(WEST, getNeighbour(SOUTHWEST));
setNeighbour(SOUTHWEST, getNeighbour(SOUTHEAST));
setNeighbour(SOUTHEAST, eastNeighbour);
}
}
|
Using the PieceCell
class, we can complete the implementation of the Piece
class. Listing 4 shows you the source code:
Listing 4. The Piece class
public class Piece {
public static final int NUMBEROFCELLS = 5;
public static final int NUMBEROFPERMUTATIONS = 12;
private PieceCell[] pieceCells = new PieceCell[NUMBEROFCELLS];
private int currentPermutation = 0;
private void rotatePiece() {
for (int i = 0; i < NUMBEROFCELLS; i++) {
pieceCells[i].rotate();
}
}
private void flipPiece() {
for (int i = 0; i < NUMBEROFCELLS; i++) {
pieceCells[i].flip();
}
}
public Piece nextPermutation() {
if (currentPermutation == NUMBEROFPERMUTATIONS)
currentPermutation = 0;
switch (currentPermutation%6) {
case 0:
// Flip after every 6 rotations
flipPiece();
break;
default:
rotatePiece();
break;
}
currentPermutation++;
return this;
}
public void resetProcessed() {
for (int i = 0; i < NUMBEROFCELLS; i++) {
pieceCells[i].setProcessed(false);
}
}
//Getters and setters have been omitted
}
|