//C4Board.java /** * * @author Sean Bridges * @version 1.0 */ import java.util.Vector; public final class C4Board implements Board { //------------------------------------------ //class variables public final static int NULL_PLAYER_NUMBER = -1; public final static int FIRST_PLAYER_NUMBER = 0; public final static int SECOND_PLAYER_NUMBER = 1; public static final int NUMBER_OF_ROWS = 6; //the height of the board public static final int NUMBER_OF_COLUMNS = 8; //the width of the board public static final int NUMBER_OF_SLOTS = NUMBER_OF_ROWS * NUMBER_OF_COLUMNS; //------------------------------------------ //instance variables //slots are accessed by row * NUMBER_OF_COLUMNS + column, //where row and columnm start at 0 private C4Slot[] slots; //store the number of chips in each column in an array. //this saves us from having to scan each column when making moves. //the max value for each column is NUMBER_OF_ROWS private int[] numberOfChipsInColumn = new int[NUMBER_OF_COLUMNS]; //the move history, stored as an array of colummns //moveHistoryLength always points to the next free slot int moveHistoryLength = 0; int[] moveHistory = new int[NUMBER_OF_SLOTS]; private C4Stats stats = new C4Stats(); private Vector rows; //to avoid creating new arrays when getting all possible moves //we simply always return all the moves, instead of all the legal //moves private Move[] firstPlayerMoves; private Move[] secondPlayerMoves; //------------------------------------------ //constructors /** Creates new C4Board */ public C4Board(Player firstPlayer, Player secondPlayer) { initSlots(); //create the moves arrays, these are all the possible moves firstPlayerMoves = new Move[NUMBER_OF_COLUMNS]; secondPlayerMoves = new Move[NUMBER_OF_COLUMNS]; for(int i = 0; i < NUMBER_OF_COLUMNS; i++) { firstPlayerMoves[i] = new C4Move(firstPlayer, i); secondPlayerMoves[i] = new C4Move(secondPlayer, i); } }//end constructor //-------------------------------------- //instance methods //-------------------------------------- //initializing /** * Create the slots and organize them into rows */ private void initSlots() { slots = new C4Slot[NUMBER_OF_SLOTS]; //create the slots for(int i = 0; i < NUMBER_OF_SLOTS; i++) { slots[i] = new C4Slot(); } //create the rows rows = new Vector(84); /* * choose all possible groups of 4 slots * a group of four is determined by 2 things, its starting position, and its slope. * the starting position is writen as (row,column), where row exists in the * set {0,1,... numberOfRows - 1} and column lies in the set {0,1, ...number of columns -1} * the slope is written as <delRow, delColumn> where delRow and delColumn exist in the set {-1,0,1} * * * a group of four is valid and not repeated if * 1) its starting and ending positions both lie inside the board. * 2) the slope is one of <1,0>, <1,1>, <0,1>, <-1, 1> * ie it occupies the range 90 to -45 degrees. * * * iterate for all possible groups of four, choose the valid ones * that are not repeated */ int totalNumber = 0; for(int row = 0; row <NUMBER_OF_ROWS; row++) { for(int column = 0; column < NUMBER_OF_COLUMNS; column++) { for(int delRow = -1; delRow <= 1; delRow++) { for(int delColumn = -1; delColumn <= 1; delColumn++) { if( ( ((delRow == 1) && (delColumn == 0)) || ((delRow == 1) && (delColumn == 1)) || ((delRow == 0) && (delColumn == 1)) || ((delRow == -1) && (delColumn == 1)) ) && (inbounds( row + (3*delRow), column + (3*delColumn) )) && (inbounds(row,column)) ) { C4Row newRow = new C4Row( getSlot(row,column), getSlot(row + delRow,column + delColumn), getSlot(row + (2 *delRow), column + (2 * delColumn)), getSlot(row + (3 *delRow), column + (3 * delColumn)), stats ); rows.addElement(newRow); } }//end for delColumn }//end for delRow }//end for column }//end for row } //-------------------------------------- //slot access /** * Returns the number of slots in a given column. * column should be in 0..NUMBER_OF_COLUMNS -1 inclusive. */ public int numerOfChipsInColumn(int column) { return numberOfChipsInColumn[column]; } /** * Return wether or not the given row,column pair exists on the board. * Row and columns are numbered starting at 0. */ private boolean inbounds(int row, int column) { return ((row >= 0) && (column >= 0) && (row < NUMBER_OF_ROWS) && (column < NUMBER_OF_COLUMNS)); } /** * Return the slot at the specified row and column. */ private C4Slot getSlot(int row, int column) { return getSlot((row * NUMBER_OF_COLUMNS) + column); } /** * Access a slot by its row. * the index should be equal to row * NUMBER_OF_COLUMNS + column, * where row and column both start at 0 */ private C4Slot getSlot(int index) { return slots[index]; } //-------------------------------------- //restarting the game private void resetGame() { for(int i = 0; i < NUMBER_OF_SLOTS; i++) { if(slots[i].getContents() != NULL_PLAYER_NUMBER ) { slots[i].setContents(NULL_PLAYER_NUMBER); } } for(int i = 0; i < NUMBER_OF_COLUMNS; i++) { numberOfChipsInColumn[i] = 0; } moveHistoryLength = 0; } //----------------------------------------- //Board methods /** * Return an object which can be interrogated to discover the current * state of the game */ public BoardStats getBoardStats() { return stats; } /** * Try and make a move. * Returns wether or not the move attempt was successful. * * m.toInt() should equal the column the player wants to move in. * starting at 0. */ public boolean move(Move m) { //System.out.println(m); int column = m.toInt(); int columnCount = numberOfChipsInColumn[column]; if(columnCount < NUMBER_OF_ROWS) { //make the move slots[(columnCount * NUMBER_OF_COLUMNS) + column].setContents(m.maker().getNumber()); numberOfChipsInColumn[column] = columnCount + 1; //update the history moveHistory[moveHistoryLength] = column; moveHistoryLength++; return true; } else { return false; } } /** Undo the last move made. */ public void undoLastMove() { //System.out.println("undoing last move"); moveHistoryLength --; int column = moveHistory[moveHistoryLength]; numberOfChipsInColumn[column]--; int row = numberOfChipsInColumn[column]; slots[(row * NUMBER_OF_COLUMNS) + column].setContents(NULL_PLAYER_NUMBER); } /** * Get the list of moves that are possible for the given * player. * For the C4Board this always returns the same array for each player. * Some of the moves may not be legal, but the legal moves is a subset * of the moves returned. */ public Move[] getPossibleMoves(Player aPlayer) { /* * We want to avoid creating new objects when communicating what moves * are allowed. * To solve this we simply always return the same array when asked what * moves are possible for a player. Some of these moves may * cause move() to return false. */ if(aPlayer.getNumber() == FIRST_PLAYER_NUMBER ) { return firstPlayerMoves; } else { return secondPlayerMoves; } } /** Whether or not the game is over. */ public boolean isGameOver() { //game over if someone has won or if all the slots are full. return( (moveHistoryLength == NUMBER_OF_SLOTS ) || stats.hasSomeoneWon()); } /** Called by the game master when the game is started. */ public void gameStarted() { resetGame(); } /** Called by the game master when the game is restarted. */ public void gameRestarted() { resetGame(); } /** Called by the game master when the game is stopped. */ public void gameStoped() { resetGame(); } /** A board must be cloneable since a copy of it is sent * to players when we ask them for their move. */ public Object clone() { C4Board clone = null; try { clone = (C4Board) super.clone(); } catch(CloneNotSupportedException e) { //we should never get here System.out.println(e); } clone.stats = new C4Stats(); clone.numberOfChipsInColumn = (int[]) numberOfChipsInColumn.clone(); clone.moveHistory = (int[]) moveHistory.clone(); clone.initSlots(); for(int i = 0; i < NUMBER_OF_SLOTS; i++) { clone.slots[i].setContents( this.slots[i].getContents() ); } return clone; } //------------------------------------------- //printing public String toString() { StringBuffer buf = new StringBuffer(); buf.append("|0|1|2|3|4|5|6|7|"); for(int row = NUMBER_OF_ROWS - 1; row >= 0; row--) { buf.append("\n|"); for(int column = 0; column < NUMBER_OF_COLUMNS; column++) { if( getSlot(row,column).getContents() == NULL_PLAYER_NUMBER) { buf.append(" "); } else { buf.append(getSlot(row,column).getContents() ); } buf.append("|"); }//end for column }//end for row return buf.toString(); } }//end class C4Board