package bibliothek.chess.model;
import java.util.ArrayList;
import java.util.List;
/**
* A board describes all states in which a game of chess can be.
* @author Benjamin Sigg
*
*/
public class Board {
/**
* Describes states in which the possible moves for a player might
* be restricted.
* @author Benjamin Sigg
*/
public static enum State{
/** The state when the king is in danger */
CHECK,
/** The state when a player has won */
CHECKMATE,
/** The state when a player can't move anymore */
STALLED,
/** The state where nothing special happens */
NOTHING
}
/** The cells which might be covered by one figure */
private Figure[][] figures = new Figure[8][8];
/** Tells for every cell whether it is attacked by the opponent or not */
private boolean[][] attacked = new boolean[8][8];
/** The player which can do the next move */
private Player player = Player.WHITE;
/**
* A list of {@link ChessListener} which are informed whenever the state of
* this board changes.
*/
private List<ChessListener> listeners = new ArrayList<ChessListener>();
/** the white king */
private Figure white;
/** the black king */
private Figure black;
/** whether this board is a copy of the original board or not */
private boolean copy = false;
/**
* Creates a new board, puts all figures at their initial position.
*/
public Board(){
for( int i = 0; i < 8; i++ ){
put( new Figure( this, Player.WHITE, Figure.Type.PAWN, 1, i ));
put( new Figure( this, Player.BLACK, Figure.Type.PAWN, 6, i ));
}
put( new Figure( this, Player.WHITE, Figure.Type.ROCK, 0, 0 ));
put( new Figure( this, Player.WHITE, Figure.Type.KNIGHT, 0, 1 ));
put( new Figure( this, Player.WHITE, Figure.Type.BISHOP, 0, 2 ));
put( new Figure( this, Player.WHITE, Figure.Type.QUEEN, 0, 3 ));
put( white = new Figure( this, Player.WHITE, Figure.Type.KING, 0, 4 ));
put( new Figure( this, Player.WHITE, Figure.Type.BISHOP, 0, 5 ));
put( new Figure( this, Player.WHITE, Figure.Type.KNIGHT, 0, 6 ));
put( new Figure( this, Player.WHITE, Figure.Type.ROCK, 0, 7 ));
put( new Figure( this, Player.BLACK, Figure.Type.ROCK, 7, 0 ));
put( new Figure( this, Player.BLACK, Figure.Type.KNIGHT, 7, 1 ));
put( new Figure( this, Player.BLACK, Figure.Type.BISHOP, 7, 2 ));
put( new Figure( this, Player.BLACK, Figure.Type.QUEEN, 7, 3 ));
put( black = new Figure( this, Player.BLACK, Figure.Type.KING, 7, 4 ));
put( new Figure( this, Player.BLACK, Figure.Type.BISHOP, 7, 5 ));
put( new Figure( this, Player.BLACK, Figure.Type.KNIGHT, 7, 6 ));
put( new Figure( this, Player.BLACK, Figure.Type.ROCK, 7, 7 ));
}
/**
* Creates a new board copying as much as possible from <code>board</code>.
* @param board the original board
*/
private Board( Board board ){
copy = true;
for( int r = 0; r < 8; r++ ){
for( int c = 0; c < 8; c++ ){
if( board.figures[r][c] != null ){
figures[r][c] = board.figures[r][c].copy( this );
if( figures[r][c].getType() == Figure.Type.KING ){
if( figures[r][c].getPlayer() == Player.WHITE )
white = figures[r][c];
else
black = figures[r][c];
}
}
attacked[r][c] = board.attacked[r][c];
player = board.player;
}
}
}
/**
* Gets a copy of this board. The copy is independent of this board.
* @return the copy
*/
public Board copy(){
return new Board( this );
}
/**
* Adds an observer to this board. The observer will be notified whenever
* a state of this board changes.
* @param listener the new observer
*/
public void addListener( ChessListener listener ){
listeners.add( listener );
}
/**
* Removes an observer from this board.
* @param listener the observer to remove
*/
public void removeListener( ChessListener listener ){
listeners.remove( listener );
}
/**
* Gets the player which has the turn.
* @return the current player
*/
public Player getPlayer() {
return player;
}
/**
* Gets a pawn on the first or the last row.
* @return a pawn or <code>null</code> if now pawn is on the first or
* last row.
*/
public Figure pawnReplacement(){
for( int c = 0; c < 8; c++ ){
if( isType( 0, c, Figure.Type.PAWN ))
return getFigure( 0, c );
if( isType( 7, c, Figure.Type.PAWN ))
return getFigure( 7, c );
}
return null;
}
/**
* Switches the players. Also rebuilds the attack-matrix.
*/
public void switchPlayer(){
player = player.opponent();
buildAttackMatrix();
for( ChessListener listener : listeners )
listener.playerSwitched( player );
}
/**
* Builds the attack-matrix. The attack-matrix tells for every cell
* whether it is attacked by a figure of the opponent, or not.
*/
public void buildAttackMatrix(){
for( int r = 0; r < 8; r++ )
for( int c = 0; c < 8; c++ )
attacked[r][c] = false;
for( int r = 0; r < 8; r++ ){
for( int c = 0; c < 8; c++ ){
if( figures[r][c] != null ){
if( figures[r][c].getPlayer() == player.opponent() ){
figures[r][c].attackable( new CellVisitor(){
public boolean visit( int r, int c, Figure figure ) {
attacked[r][c] = true;
return true;
}
});
}
}
}
}
}
/**
* Tells in which state the game is.
* @return the state
*/
public State state(){
// check whether at least one move is possible
boolean moveable = false;
for( int r = 0; !moveable && r < 8; r++ ){
for( int c = 0; !moveable && c < 8; c++ ){
if( figures[r][c] != null ){
if( figures[r][c].getPlayer() == player ){
moveable = figures[r][c].moveable();
}
}
}
}
if( isKingAttacked() ){
if( moveable )
return State.CHECK;
else
return State.CHECKMATE;
}
else{
if( moveable )
return State.NOTHING;
else
return State.STALLED;
}
}
/**
* Puts the <code>figure</code> at the location delivered by the figure
* itself.
* @param figure the figure to add to this board.
*/
public void put( Figure figure ){
figures[figure.getRow()][figure.getColumn()] = figure;
}
/**
* Gets the figure at the designated location.
* @param r the row
* @param c the column
* @return the figure or <code>null</code>
*/
public Figure getFigure( int r, int c ){
return figures[r][c];
}
/**
* Gets the figure which represents the king for a given player.
* @param player the player, black or white
* @return the player's king
*/
public Figure getKing( Player player ){
if( player == Player.WHITE )
return white;
else
return black;
}
/**
* Tells whether the cell r/c is currently under attack by the
* players opponent.
* @param r the row
* @param c the column
* @return <code>true</code> if the cell is under attack
*/
public boolean isAttacked( int r, int c ){
return attacked[r][c];
}
/**
* Tells whether the king of the current player is attacked or not.
* @return <code>true</code> if the king is attacked.
*/
public boolean isKingAttacked(){
Figure king = getKing( player );
return isAttacked( king.getRow(), king.getColumn() );
}
/**
* Removes a figure from this board.
* @param r the row
* @param c the column
*/
public void kill( int r, int c ){
Figure figure = figures[r][c];
figures[r][c] = null;
for( ChessListener listener : listeners )
listener.killed( r, c, figure );
}
/**
* Moves a figure on this board.
* @param sr the current row
* @param sc the current column
* @param dr the row of the destination
* @param dc the column of the destination
*/
public void move( int sr, int sc, int dr, int dc ){
Figure figure = figures[sr][sc];
figure.setLocation( dr, dc );
figures[sr][sc] = null;
if( figures[dr][dc] != null )
kill( dr, dc );
figures[dr][dc] = figure;
if( !copy ){
for( int i = 0; i < 8; i++ ){
for( int j = 0; j < 8; j++ ){
if( i != dr || j != dc ){
Figure check = figures[i][j];
if( check != null )
check.cleanJustMoved();
}
}
}
}
for( ChessListener listener : listeners )
listener.moved( sr, sc, dr, dc, figure );
}
/**
* Tells whether there is a figure of <code>player</code> at the
* designated location.
* @param r the row
* @param c the column
* @param player the player which might have a figure at <code>r/c</code>
* @return <code>true</code> if there is a figure of <code>player</code>
*/
public boolean isPlayer( int r, int c, Player player ){
Figure figure = getFigure( r, c );
if( figure == null )
return false;
return figure.getPlayer() == player;
}
/**
* Tells whether the is a figure of type <code>type</code> at the
* designated location.
* @param r the row
* @param c the column
* @param type the type of the figure
* @return <code>true</code> if a figure was found with the given <code>type</code>
*/
public boolean isType( int r, int c, Figure.Type type ){
Figure figure = getFigure( r, c );
if( figure == null )
return false;
else
return figure.getType() == type;
}
/**
* Tells whether the designated cell is empty or not.
* @param r the row
* @param c the column
* @return <code>true</code> if the cell is empty
*/
public boolean isEmpty( int r, int c ){
return getFigure( r, c ) == null;
}
/**
* Tells whether <code>r/c</code> is a valid location.
* @param r the row
* @param c the column
* @return <code>true</code> if a cell <code>r/c</code> exists.
*/
public boolean isValid( int r, int c ){
return r >= 0 && c >= 0 && r < 8 && c < 8;
}
/**
* Visits field <code>r/c</code>.
* @param r the row
* @param c the column
* @param visitor the visitor
* @return <code>true</code> if other locations should be visited,
* <code>false</code> if the algorithm should stop
*/
public boolean visit( int r, int c, CellVisitor visitor ){
return visitor.visit( r, c, figures[r][c] );
}
/**
* A visitor can be used to visit a set of cells filtered by some
* rule.
* @author Benjamin Sigg
*/
public static interface CellVisitor{
/**
* Called when visiting the cell <code>r/c</code>.
* @param r the row
* @param c the column
* @param figure the figure in this cell
* @return <code>true</code> if other cells should be visited,
* <code>false</code> if the algorithm should stop immediately
*/
public boolean visit( int r, int c, Figure figure );
}
}