// Board.java import java.awt.*; import java.util.*; /** 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. @author Nick Parlante @version 1.0, Mar 1, 2001 Elizabeth French April 30, 2008 */ public final class Board { private int width; //Width of board private int height; //height of board //each cell is a bool. listing if filled or not private boolean[][] grid; //Highest piece on the board - start with the board empty private int maxHeight = 0; //Number of filled spots per row. private int[] widths; //Index of first tile without filled tiles above private int[] heights; //Backup data for grid - only saves from previous committed state private boolean[][] bGrid; private int bMaxHeight = 0; private int[] bWidths; private int[] bHeights; private boolean committed; private boolean DEBUG = true; private static final int HEIGHT = 2; private static final int COPY = 0, SWITCH = 1; /** Creates an empty board (and its backup) of the given width and height measured in blocks. */ public Board(int aWidth, int aHeight) { width = aWidth; height = aHeight + HEIGHT; //copy in the new grid grid = new boolean[width][height]; widths = new int[height]; //# cells filled for each row heights = new int[width]; //index of first free cell per-col //create the backup of the grid bGrid = new boolean[width][height]; bWidths = new int[height]; bHeights = new int[width]; } /** 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. */ public int dropHeight(Piece piece, int x) { int[] skirt = piece.getSkirt(); int blocked = 0; //Highest index the piece can acheive int xDrop; //Highest index the piece can get from x //Calc where skirt would fall for every skirt value; store max for (int i=0; i blocked) blocked = xDrop; } return blocked; } 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. Origin of piece goes to (x,y). 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 halts partially complete and PLACE_BAD is returned */ public int place(Piece piece, int x, int y) { //board must start in committed state then enter uncommitted if (!committed) return PLACE_BAD; committed = false; //Backup the board backupBoard(COPY); //If the piece is out of bounds then return PLACE_OUT_OF_BOUNDS if ( x<0 || x+piece.getWidth() > width || y<0 || y+piece.getHeight()> height) {return PLACE_OUT_BOUNDS;} Point[] body = piece.getBody(); //Check for validity and update all grid information for each block for (int i=0; i maxHeight) maxHeight = heights[xCell]; //update widths widths[yCell]++; //Update grid cell grid[xCell][yCell] = true; } /** Backs up the essential arrays, either by copying or switching the pointers between original and backup depending on the int passed */ public void backupBoard(int arg) { //copies arrays into backups to commit them if (arg == COPY) { System.arraycopy(widths, 0, bWidths, 0, height); //bWidths System.arraycopy(heights, 0, bHeights, 0, width); //bHeights for (int i=0; iImplementation: 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() { //Starts and ends in uncommitted state if (committed) return false; int drop = 0; //# of rows cleared //Drop or clear each row until reach maxHeight for (int i=0; i 0) { for(int j=0; j= 0 && grid[k][heights[k]] != true) { heights[k]--; } //Want index of cell Above the filled cell heights[k]++; if (maxHeight < heights[k]) maxHeight = heights[k]; } } /** If a place() happens, optionally followed by a clearRows(), a subsequent undo() reverts the board to its state before place(). If the conditions for undo() are not met, undo() does nothing. See the overview docs. */ public void undo() { //Board must be uncommitted then enters into committed state if (committed) return; committed = true; //Switch the arrays backupBoard(SWITCH); } /** Puts the board in the committed state. Also updates the heights[], widths[] and maxHeight variables. See the overview docs. */ public void commit() { backupBoard(COPY); committed = true; } /** Returns the width of the board in blocks. */ public int getWidth() { return width; } /** Returns the width 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. */ public int getMaxHeight() { return maxHeight; } /** 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]; } /** used as an aid during debugging. Yay!!!*/ public void printCurrent(String state) { //print out the widths of the rows in one line System.out.print(state+":\n widths: {"); for (int a=0; a