// Board.java import java.awt.*; import java.util.*; /* Charles Meyer * Tetris II: Lab 10 * April 23, 2008 * Comp Sci 210 */ /* So basically I wrote evey method of substance in this things, everything other * then the names of the global variables, and the getters. Place takes a peice * and a location and puts that piece on the board with the lower left of the * piecce at that spot. ClearRows goes through the board and removes any rows * that are filled. Those, aside from undue and commit, wich I will explain in * a moment, are all the important features. First though, an explanation of * heights and widths. Widths holds the number of peices in each row, while * heights holds the position ot the highest peiece in each coloumn. MaxHeight * simply holds the highest height. Sanity check is an optional tool that (when * DEBUG is set to true) checks heights and widths and maxHeight against the grid. * PrintBoard simply goes through and prints the board as well as the widths and heights. * This leave the undu and commit features. Undu is run to revert the board * back to its last committed state. When the board leaves the committed state, it * copies the grid, widths, and heights, into backup arrays. When undu is pressed, * the backup arrays are passed back to the primarry arrays. When commit is pressed, * the board is committed again. Up to one place and one clearRows can be run * between commits, hence the board has one deep undo capability. */ /** Represents a Tetris board -- essentially a 2-d grid of booleans. Supports tetris pieces and row clearning. Has an "undo" feature that allows clients to add and remove pieces efficiently. Does not do any drawing or have any idea of pixels. Intead, just represents the abtsract 2-d board. See Tetris-Architecture.html for an overview. This is the starter file version -- a few simple things are filled in already @author Nick Parlante @version 1.0, Mar 1, 2001 */ public final class Board { //these are the primarry data structures private int width; //the width of the board private int height; //the height of the board private boolean[][] grid; //the actual boward, a spot is true if it is ocupied private int maxHeight; //the highest height private int widths[]; //the number of blocks in each row private int heights[]; //the height of each coloumn //whether the board is comitted (cannot be undone) private boolean committed; //refers to whether clear row has been run //(since this can be run once while uncomitted) private boolean CRCommitted; // backup data structures private boolean[][] bGrid; private int[] bWidths; private int[] bHeights; private int bMaxHeight; //controls whether sanity check is ran private boolean DEBUG = false; //I believe that this is the lead in space at the top of the board private static final int HEIGHT = 2; //controls whether error messages are printed when undo is called //while comitted and commit is called while already comitted //(the mule does both of the above, but in my opinion a good //program would not) private static final boolean PRINT_MESSAGES = false; /** Creates an empty board of the given width and height measured in blocks. This is not quite done, you still need to initialize your backup data structures (and erase this comment!) */ public Board(int inWidth, int inHeight) { //sets the height and width of the grid based on the input width = inWidth; height = inHeight + HEIGHT; //this sets up the data structures based on the width and height grid = new boolean[width][height]; widths = new int[height]; heights = new int[width]; maxHeight = 0; //the board starts committed committed = true; //this initializes the grid and heights for (int i = 0; i < width; i++) { heights[i] = 0; for (int j = 0; j < height; j++) { grid[i][j] = false; } } //initializes the widths for (int i = 0; i < height; i++) { widths[i] = 0; } sanityCheck(); } /** Returns the width of the board in blocks. */ public int getWidth() { return width; } /** Returns the height of the board in blocks. */ public int getHeight() { return height; } /** Returns the max column height present in the board. For an empty board this is 0. Remember, this should be constant! */ public int getMaxHeight() { return maxHeight; } /** Checks the board for internal consistency -- used for debugging. */ //also fixes the board if it is not consistant based on grid //this is actually sortof useless right now, since the exceptions just crash the program, //but if one changed the exceptions to something less terminal, this might be helpfull public void sanityCheck() { if (DEBUG) { //these will be calculated like Heights and Widths, and //then checked against the originals for consistancy int[] tempHeights = new int[width]; int[] tempWidths = new int[height]; int tempMaxheight = 0; //caluculates tempHeights and tempWidths for (int k = 0; k < grid.length; k++) { for (int i = 0; i < grid[k].length; i++) { if (grid[k][i]) { //increases heights if this piece is higher then //the current value (+1 to acount for 0 index) if (i +1 > tempHeights[k]) { tempHeights[k] = i+1; } //updates temMaxHeight if necessarry if (tempHeights[k] > tempMaxheight) { tempMaxheight = tempHeights[k]; } //increments widths if necessarry tempWidths[i] ++; } } } //this will be set to true if there is inconsistancy boolean throwException = false; //checks tempHeights against Heights for (int k =0; k < heights.length; k++) { //if there is a mismatch if (heights[k] != tempHeights[k]) { throwException = true; } //updates temMaxHeight if necessarry if (tempHeights[k] > tempMaxheight) { tempMaxheight = tempHeights[k]; } } //throws an exception and resets heights, if necessarry if (throwException) { heights = tempHeights; throw new RuntimeException("Sanity check failure on heights"); } //resets throwException throwException = false; //repeats the above process for the widths for (int k = 0; k< widths.length; k++) { if (widths[k] != tempWidths[k]) { throwException = true; } } //throws an exception and resets widths if necessarry if (throwException) { widths = tempWidths; printBoard(); throw new RuntimeException("Sanity check failure on widths"); } //checks to see that the maxHeights match and resets maxHeights if so if (tempMaxheight != maxHeight) { printBoard(); throw new RuntimeException("Sanity check failure on maxHeight"); } } } /** Given a piece and an x, returns the y value where the piece would come to rest if it were dropped straight down at that x.
Implementation: use the skirt and the col heights to compute this fast -- O(skirt length). */ public int dropHeight(Piece piece, int x) { int toReturn = 0; //this goes through the entire skirt and calculates what drop height //is dictated by each piece, and sets toreturn to the highest for (int k = 0; k < piece.getSkirt().length; k++) { //if the piece given doe not fit on the board if (k + x >= width) { printBoard(); System.err.println("ilegal piece given to dropHeight"); } //increases toReturn if it needs to else if (heights[k + x] - piece.getSkirt()[k] > toReturn){ toReturn = heights[k + x] - piece.getSkirt()[k]; } } if (DEBUG) { System.out.println("debug: DropHeight called, returned " + toReturn); } return toReturn; } /** Returns the height of the given column -- i.e. the y value of the highest block + 1. The height is 0 if the column contains no blocks. */ public int getColumnHeight(int x) { return heights[x]; } /** Returns the number of filled blocks in the given row. */ public int getRowWidth(int y) { return widths[y]; } /** Returns true if the given block is filled in the board. Blocks outside of the valid width/height area always return true. */ public final boolean getGrid(int x, int y) { return grid[x][y]; } public static final int PLACE_OK = 0; public static final int PLACE_ROW_FILLED = 1; public static final int PLACE_OUT_BOUNDS = 2; public static final int PLACE_BAD = 3; /** Attempts to add the body of a piece to the board. Copies the piece blocks into the board grid. Returns PLACE_OK for a regular placement, or PLACE_ROW_FILLED for a regular placement that causes at least one row to be filled.
Error cases:
If part of the piece would fall out of bounds, the placement
does not change the board at all, and PLACE_OUT_BOUNDS is returned.
If the placement is "bad" --interfering with existing blocks in the grid --
then the placement is halted partially complete and PLACE_BAD is returned.
An undo() will remove the bad placement.
*/
public int place(Piece piece, int x, int y) {
if (DEBUG) {
System.out.println("debug: place called");
}
if (!committed) {
if (PRINT_MESSAGES) {
System.err.println("Error, place called while uncommitted");
}
return PLACE_BAD;
}
//the board is now not comitted, hence:
committed = false;
//sets up the backup data structures
bGrid = new boolean[grid.length][grid[0].length];
//system.arraycopy does not appear to work for 2-d arrays:
for (int k =0; k Implementation: This is complicated.
Ideally, you want to copy each row down
to its correct location in one pass.
Note that more than one row may be filled.
*/
public boolean clearRows() {
if (DEBUG) {
System.out.println("debug: clearRows called");
}
if (!CRCommitted) {
System.err.println("Error: clear rows called while CRComitted");
return false;
}
CRCommitted = false;
if (!committed) {
committed = false;
//sets up the backup data structures
bGrid = new boolean[grid.length][grid[0].length];
//system.arraycopy does not appear to work for 2-d arrays:
for (int k =0; k