import java.util.Random; import java.awt.*; /** The Piece is the basis for all of the pieces and orientations that follow. * There should be no instance variables that are not reentrant, so that * a single instance of each should suffice for the program. Any board-specific * information needed should be passed in via method arguments. *
* The basic Piece knows its color, its score based on the different * orientations, how to move and rotate, and how to draw itself. It needs * extensions, one for each type of piece (Bar, El, Zee, etc.), and each of * those needs extensions for each orientation - 2 for rotationally * symetric pieces, 4 for others. The Pieces need to initialize the * rotation array and set the color for the subclasses. Each of the 2 or * 4 orientation subclasses must give the score if the piece lands in that * orientation, and be able to fill in an array giving Cell locations, given * the root Cell. * * @author Russell Young tetrii@young-0.com * @version 1.0 */ /* Feel free to use, distribute, or change, with the only restriction that you * should not remove my name from the original code, and you should document * your changes so there is no confusion about whose bugs they are. */ public abstract class Piece implements Constants { static final int DISTINCT_PIECES = 7; // Define the colors associated with a piece static final Color TEE_COLOR = Color.blue; static final Color SQUARE_COLOR = Color.red; static final Color BAR_COLOR = Color.yellow; static final Color EL_COLOR = Color.green; static final Color R_EL_COLOR = Color.magenta; static final Color ZEE_COLOR = Color.cyan; static final Color R_ZEE_COLOR = Color.white; static final Color INITIAL = Color.gray; static Piece getPieceForColor(Color c) { if (c == null) return(null); for (int i = 0; i < DISTINCT_PIECES; i++) if (c.equals(pieces[i].color())) return(pieces[i]); return(null); } static Random random = null; protected Piece rotations[]; protected int score; static private Piece[] pieces = null; Piece() {rotations = new Piece[2];} /** This should give the color for each subclass Piece type */ public abstract Color color(); /** One instance of each piece is needed to pass back to the caller. This * must be called before any other Piece calls. */ static void initialize() { random = new Random(); pieces = new Piece[DISTINCT_PIECES]; pieces[0] = Bar.init(); pieces[1] = Tee.init(); pieces[2] = Square.init(); pieces[3] = Zee.init(); pieces[4] = R_Zee.init(); pieces[5] = El.init(); pieces[6] = R_El.init(); } /** Returns a random piece in the starting orientation * @return a Piece chosen at random */ static Piece getNext() { return(pieces[Math.abs(random.nextInt()) % DISTINCT_PIECES]);} // Try to draw no more than necessary. It is OK to trash oldCells because // they will be reset on return from this routine. /** Draws a moving piece in the most efficient way possible, clearing out * old cells and filling in new ones. * @param g the Graphics to draw in * @param cells an array whose first element gives the root location of the * Piece to draw * @param oldCells an array whose first element, if nonnull, gives the root * location of the previously drawn piece * @return boolean false if the piece cannot be drawn without stepping on an * occupied cell */ public boolean draw(Graphics g, Cell[] cells, Cell[] oldCells) { int j, i = 0; boolean ok = true; getCells(cells); if ((oldCells != null) && (oldCells[0] != null)) { getCells(oldCells); for (i = 0; i < CELLS_PER_PIECE; i++) { ok = ok && !cells[i].occupied(); for (j = 0; (j < CELLS_PER_PIECE) && (cells[i] != oldCells[j]); j++); if (j == CELLS_PER_PIECE) cells[i].paint(color(), g); else oldCells[j] = null; } for (i = 0; i < CELLS_PER_PIECE; i++) if (oldCells[i] != null) oldCells[i].paint(null, g); } else for (i = 0; i < CELLS_PER_PIECE; i++) { cells[i].paint(color(), g); ok = ok && !cells[i].occupied(); } return(ok); } /** Draws a moving piece * @param g the Graphics to draw in * @param cells an array whose first element gives the root location of the * Piece to draw * @return boolean false if the piece cannot be drawn without stepping on an * occupied cell */ public boolean draw(Graphics g, Cell[] cells) { return(draw(g, cells, (Cell[])null));} /** Draws a rotating piece, clearing out old cells and filling in new ones. * @param g the Graphics to draw in * @param cells a Cell[] whose first element gives the root location of the * Piece to draw * @param oldP the previous Piece. In a rotation the root position has not * changed, so cells[0] is the root for both this and oldP. * @param c a Cell[] to use for computing the location of the previous piece * (because I am trying to avoid any allocation in this class) * @return boolean false if the piece cannot be drawn without stepping on an * occupied cell */ public boolean draw(Graphics g, Cell[] cells, Piece oldP, Cell[] c) { int i, j; c[0] = cells[0]; oldP.getCells(c); getCells(cells); for (i = 0; i < CELLS_PER_PIECE; i++) { for (j = 0; (j < CELLS_PER_PIECE) && (cells[i] != c[j]); j++); if (j == CELLS_PER_PIECE) cells[i].paint(color(), g); else c[j] = null; } for (i = 0; i < CELLS_PER_PIECE; i++) if (c[i] != null) c[i].paint(null, g); return(true); } /** Clears the next piece area * @param cells a Cell[] giving the cell locations * @param width the width of the area to clear * @param graphics the Graphics object of the area to clear */ void clear(Cell[] cells, int width, Graphics graphics) { graphics.clearRect(0, 0, width*CELL_WIDTH, 2*CELL_HEIGHT); for (int i = 0; i < CELLS_PER_PIECE; i++) cells[i].setColor(null); } ////////////////////////////////////////////////////////////// // // // Following are the methods related to moving the pieces // // // ////////////////////////////////////////////////////////////// /** Rotates a piece * @param direction an int telling Clockwise or Counterclockwise (see * Constants) * @param cells Cell[] where cells[0] gives the root position of the piece * @return the new Piece representing the rotation, or null if it cannot * be done */ Piece rotate(int direction, Cell[] cells) { Piece p = rotations[direction]; return(p.check(cells) ? p : null); } /** Translates a piece * @param direction an int tellingdirection (see Constants) * @param cells Cell[] where cells[0] gives the root position of the piece * @return boolean true if the translation is legal, false otherwise */ boolean translate(int direction, Cell[] cells) { Cell c0 = cells[0]; cells[0] = c0.get(direction); if (!check(cells)) { cells[0] = c0; return(false); } return(true); } /** shorthand for translate(DOWN, cells) */ boolean down(Cell[] cells) {return(translate(DOWN, cells));} /** shorthand for translate(LEFT, cells) */ boolean left(Cell[] cells) {return(translate(LEFT, cells));} /** shorthand for translate(RIGHT, cells) */ boolean right(Cell[] cells) {return(translate(RIGHT, cells));} /** Checks if the current piece can be moved to the position in cells * @param cells the Cell[] where cells[0] is the root position of the Piece * @return boolean true if it is a legal position, false if it overlaps * another piece or runs off the board */ protected boolean check(Cell[] cells) { getCells(cells); for (int i = 0; i < CELLS_PER_PIECE; i++) if (cells[i].occupied()) return(false); return(true); } /** Sets the occupy property for all Cells in the Piece * @param cells a Call[] which has the root location in [0] */ void occupy(Cell[] cells) { getCells(cells); for (int i = 0; i < CELLS_PER_PIECE; i++) cells[i].occupied(true); } /** Method that should be overridden by all subclasses * It should be abstract, but the subclasses cannot define it, and they need * to be instanciated * @param cells a Cell[] to fill in the locations of this Piece's Cells. * cells[0] must contain the root position of the Piece. */ void getCells(Cell[] cells) { System.out.println("Unimplemented getCells method for " + this); } /** Gives the score of this piece. The actual value is filled in by each * oriented subclass. * @return int giving the points for this Piece */ int score() {return(score);} } /* Following are the piece definitions. Each piece needs a class definition for * the general piece, and then 2 or 4 more subclasses defining each rotation */ /** The square is the simplest Piece, since it rotates to itself. Thus it * needs no orientation subclasses. */ class Square extends Piece { Square() {score = 5;} static Piece init() { Square s = new Square(); s.rotations[CLOCKWISE] = s.rotations[COUNTERCLOCKWISE] = s; return(s); } public Color color() {return(SQUARE_COLOR);} void getCells(Cell[] cells) { cells[1] = cells[0].up(); cells[2] = cells[0].right(); cells[3] = cells[1].right(); } public String toString() {return("Square piece");} } /** The Bar class has 2 orientation subclasses. */ class Bar extends Piece { Bar() {} static Piece init() { Bar b0 = new Bar_0(); Bar b90 = new Bar_90(); b0.rotations[CLOCKWISE] = b0.rotations[COUNTERCLOCKWISE] = b90; b90.rotations[CLOCKWISE] = b90.rotations[COUNTERCLOCKWISE] = b0; return(b0); } public Color color() {return(BAR_COLOR);} public String toString() {return("Bar piece");} } class Bar_0 extends Bar { Bar_0() {score = 10;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].right(); cells[3] = cells[2].right(); } } class Bar_90 extends Bar { Bar_90() {score = 1;} void getCells(Cell[] cells) { cells[1] = cells[0].down(); cells[2] = cells[0].up(); cells[3] = cells[2].up(); } } /************************************************ * * * Here are the Zee pieces * * * ************************************************/ class Zee extends Piece { Zee() {} static Piece init() { Zee z0 = new Zee_0(); Zee z90 = new Zee_90(); z0.rotations[CLOCKWISE] = z0.rotations[COUNTERCLOCKWISE] = z90; z90.rotations[CLOCKWISE] = z90.rotations[COUNTERCLOCKWISE] = z0; return(z0); } public Color color() {return(ZEE_COLOR);} public String toString() {return("Zee piece");} } class Zee_0 extends Zee { Zee_0() {score = 6;} void getCells(Cell[] cells) { cells[1] = cells[0].right(); cells[2] = cells[0].up(); cells[3] = cells[2].left(); } } class Zee_90 extends Zee { Zee_90() {score = 4;} void getCells(Cell[] cells) { cells[1] = cells[0].right(); cells[2] = cells[0].down(); cells[3] = cells[1].up(); } } /************************************************ * * * Here are the Reverse Zee pieces * * * ************************************************/ class R_Zee extends Piece { R_Zee() {} static Piece init() { R_Zee rz0 = new R_Zee_0(); R_Zee rz90 = new R_Zee_90(); rz0.rotations[CLOCKWISE] = rz0.rotations[COUNTERCLOCKWISE] = rz90; rz90.rotations[CLOCKWISE] = rz90.rotations[COUNTERCLOCKWISE] = rz0; return(rz0); } public Color color() {return(R_ZEE_COLOR);} public String toString() {return("Reverse Zee piece");} } class R_Zee_0 extends R_Zee { R_Zee_0() {score = 6;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].up(); cells[3] = cells[2].right(); } } class R_Zee_90 extends R_Zee { R_Zee_90() {score = 4;} void getCells(Cell[] cells) { cells[1] = cells[0].down(); cells[2] = cells[0].left(); cells[3] = cells[2].up(); } } /************************************************ * * * Here are the Tee pieces * * * ************************************************/ class Tee extends Piece { Tee() {} static Piece init() { Tee t0 = new Tee_0(); Tee t90 = new Tee_90(); Tee t180 = new Tee_180(); Tee t270 = new Tee_270(); t0.rotations[CLOCKWISE] = t180.rotations[COUNTERCLOCKWISE] = t90; t0.rotations[COUNTERCLOCKWISE] = t180.rotations[CLOCKWISE] = t270; t90.rotations[CLOCKWISE] = t270.rotations[COUNTERCLOCKWISE] = t180; t90.rotations[COUNTERCLOCKWISE] = t270.rotations[CLOCKWISE] = t0; return(t0); } public Color color() {return(TEE_COLOR);} public String toString() {return("Tee piece");} } class Tee_0 extends Tee { Tee_0() {score = 6;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].right(); cells[3] = cells[0].up(); } } class Tee_90 extends Tee { Tee_90() {score = 3;} void getCells(Cell[] cells) { cells[1] = cells[0].right(); cells[2] = cells[0].up(); cells[3] = cells[0].down(); } } class Tee_180 extends Tee { Tee_180() {score = 6;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].right(); cells[3] = cells[0].down(); } } class Tee_270 extends Tee { Tee_270() {score = 3;} void getCells(Cell[] cells) { cells[1] = cells[0].up(); cells[2] = cells[0].left(); cells[3] = cells[0].down(); } } /************************************************ * * * Here are the El pieces * * * ************************************************/ class El extends Piece { El() {} static Piece init() { El e0 = new El_0(); El e90 = new El_90(); El e180 = new El_180(); El e270 = new El_270(); e0.rotations[CLOCKWISE] = e180.rotations[COUNTERCLOCKWISE] = e90; e0.rotations[COUNTERCLOCKWISE] = e180.rotations[CLOCKWISE] = e270; e90.rotations[CLOCKWISE] = e270.rotations[COUNTERCLOCKWISE] = e180; e90.rotations[COUNTERCLOCKWISE] = e270.rotations[CLOCKWISE] = e0; return(e0); } public Color color() {return(EL_COLOR);} public String toString() {return("El piece");} } class El_0 extends El { El_0() {score = 7;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].right(); cells[3] = cells[2].up(); } } class El_90 extends El { El_90() {score = 3;} void getCells(Cell[] cells) { cells[1] = cells[0].down(); cells[2] = cells[0].up(); cells[3] = cells[1].right(); } } class El_180 extends El { El_180() {score = 2;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].right(); cells[3] = cells[1].down(); } } class El_270 extends El { El_270() {score = 4;} void getCells(Cell[] cells) { cells[1] = cells[0].down(); cells[2] = cells[0].up(); cells[3] = cells[2].left(); } } /************************************************ * * * Here are the Reverse El pieces * * * ************************************************/ class R_El extends Piece { R_El() {} static Piece init() { R_El re0 = new R_El_0(); R_El re90 = new R_El_90(); R_El re180 = new R_El_180(); R_El re270 = new R_El_270(); re0.rotations[CLOCKWISE] = re180.rotations[COUNTERCLOCKWISE] = re90; re0.rotations[COUNTERCLOCKWISE] = re180.rotations[CLOCKWISE] = re270; re90.rotations[CLOCKWISE] = re270.rotations[COUNTERCLOCKWISE] = re180; re90.rotations[COUNTERCLOCKWISE] = re270.rotations[CLOCKWISE] = re0; return(re0); } public Color color() {return(R_EL_COLOR);} public String toString() {return("Reverse El piece");} } class R_El_0 extends R_El { R_El_0() {score = 7;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].right(); cells[3] = cells[1].up(); } } class R_El_90 extends R_El { R_El_90() {score = 4;} void getCells(Cell[] cells) { cells[1] = cells[0].down(); cells[2] = cells[0].up(); cells[3] = cells[2].right(); } } class R_El_180 extends R_El { R_El_180() {score = 2;} void getCells(Cell[] cells) { cells[1] = cells[0].left(); cells[2] = cells[0].right(); cells[3] = cells[2].down(); } } class R_El_270 extends R_El { R_El_270() {score = 3;} void getCells(Cell[] cells) { cells[1] = cells[0].down(); cells[2] = cells[0].up(); cells[3] = cells[1].left(); } }