package suicideChess;
import suicideChess.Square.NotAValidSquare;
/**
* This class contains the board representation.
* The board is represented using bitboards.
*
- a1 is square 0
* - h8 is square 7
* - a2 is square 8
* - ... and so on
*
* Note that the evaluation of the board balance is in this class since I considered that it is
* very closely related to the board position.
*
* @author Jean-Baptiste Hétier
* @version $LastChangedRevision$, $LastChangedDate$
*
*/
public class Board {
/*===========*
* CONSTANTS *
*===========*/
//Some constants to make code more readable
public static final int NB_OF_RANKS = 8;
public static final int NB_OF_FILES = 8;
public static final int NB_OF_SQUARES = NB_OF_RANKS*NB_OF_FILES;
private static final int NB_OF_BITBOARDS = 14;
@SuppressWarnings("serial")
public class NoPieceOnSquare extends Exception {
NoPieceOnSquare(String s) { super(s); };
}
@SuppressWarnings("serial")
public class UnableToParseFENStringException extends Exception {
UnableToParseFENStringException(String s) { super(s); };
}
/**
* Value returned by the evaluation function when White wins
*/
public static final int WHITE_WINS = 99999;
/**
* Value representing the maximum value possible for the evaluation function
*/
public static final int MAX_VALUE = WHITE_WINS+1;
/**
* Value returned by the evaluation function when Black wins
*/
public static final int BLACK_WINS = -99999;
/**
* Value representing the minimum value possible for the evaluation function
*/
public static final int MIN_VALUE = BLACK_WINS-1;
/**
* Value representing the value of a draw position
*/
public static final int DRAW_BOARD = 0;
/**
* Importance of real mobility in position evaluation (ie. how many moves can one make compared to the other)
*/
public static final int REAL_MOBILITY_VALUE = 40; //10;
/**
* Importance of relative mobility (mobility of other pieces that may not be able to play because of a compulsory move)
*/
public static final int RELATIVE_MOBILITY_VALUE = 40; //5;
//with less than that many pawns on one side, the computer will enter endgame mode
public static final int ENDGAME_PAWNS = 3;
//with less than that many pieces on one side, the computer will enter endgame mode
public static final int ENDGAME_PIECES = 8;
public static final int[] SQUARE_WEIGHT = {
/* -20, -10, -10, -10, -10, -10, -10, -20,
-10, 0, 3, 5, 5, 3, 0, -10,
-10, 2, 15, 15, 15, 15, 2, -10,
-10, 7, 15, 25, 25, 15, 7, -10,
-10, 7, 15, 25, 25, 15, 7, -10,
-10, 2, 15, 15, 15, 15, 2, -10,
-10, 0, 3, 5, 5, 3, 0, -10,
-20, -10, -10, -10, -10, -10, -10, -20*/
10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10
};
/*======*
* DATA *
*======*/
//The following table is used to map squares to bits
protected static long mapSquaresToBits[];
//static function used to initialise data
static {
mapSquaresToBits = new long[NB_OF_SQUARES];
for(int i=0; irank 8
* 1->rank 7
* ...
* 7->rank 1
* 8->color on move
* 9->castling (I don't care about this)
* 10->enpassant Square (if any), otherwise '-'
* 11->count of plies since the last pawn advance or capturing move
* 12->fullmove number
*/
bitBoards = new long[NB_OF_BITBOARDS];
if (result.length!=13) {
throw new UnableToParseFENStringException(command);
}
if (result[8].equals("b")) {
currentPlayer=Piece.BLACK;
} else {
currentPlayer=Piece.WHITE;
}
if (!result[10].equals("-")) {
enPassant = true;
enPassantSquare = new Square(result[10]);
} else {
enPassant = false;
enPassantSquare = new Square("a1"); //otherwise it is null
}
try {
fullmoveNumber = Integer.parseInt(result[12]);
halfmoveClock = Integer.parseInt(result[11]);
} catch (NumberFormatException e) {
System.out.println("Error (impossible to parse clock): "+command);
fullmoveNumber = 1;
halfmoveClock = 0;
}
numberOfPieces = new int[Piece.MAX_PIECE_NUMBER+1];
for(int split=0; split < 8; split++) {
int offset=0;
char[] rowToParse = result[split].toCharArray();
for (int character=0; character=Rules.NUMBER_OF_MOVES_BEFORE_DRAW) {
return true;
}
return false;
}
/**
* Used to get the halfmoveClock.
* @return the number of halfmoves since the last capture or pawn movement
*/
public int getHalfmoveClock() {
return halfmoveClock;
}
/**
* This function returns the current player color
* @return Integer
*/
public int getCurrentPlayer() {
return currentPlayer;
}
/**
* This function can be used to display the board
* Black pieces are displayed in uppercase letters.
*/
public void display(){
for (int file = NB_OF_FILES; file >= 1; file--) {
System.out.println(" +---+---+---+---+---+---+---+---+");
String display = file+" |";
for (int rank=1; rank <= NB_OF_RANKS; rank++) {
boolean displayedSomething = false;
long mask = mapSquaresToBits[rank-1+(file-1)*NB_OF_RANKS];
if (displayPiece(Piece.BLACK_PAWN,mask)) {
displayedSomething=true;
display+="'"+Piece.BLACK_PAWN_CHAR+"'|";
}
if (displayPiece(Piece.BLACK_QUEEN,mask)) {
displayedSomething=true;
display+="'"+Piece.BLACK_QUEEN_CHAR+"'|";
}
if (displayPiece(Piece.BLACK_KING,mask)) {
displayedSomething=true;
display+="'"+Piece.BLACK_KING_CHAR+"'|";
}
if (displayPiece(Piece.BLACK_KNIGHT,mask)) {
displayedSomething=true;
display+="'"+Piece.BLACK_KNIGHT_CHAR+"'|";
}
if (displayPiece(Piece.BLACK_ROOK,mask)) {
displayedSomething=true;
display+="'"+Piece.BLACK_ROOK_CHAR+"'|";
}
if (displayPiece(Piece.BLACK_BISHOP,mask)) {
displayedSomething=true;
display+="'"+Piece.BLACK_BISHOP_CHAR+"'|";
}
if (displayPiece(Piece.WHITE_PAWN,mask)) {
displayedSomething=true;
display+=" "+Piece.WHITE_PAWN_CHAR+" |";
}
if (displayPiece(Piece.WHITE_QUEEN,mask)) {
displayedSomething=true;
display+=" "+Piece.WHITE_QUEEN_CHAR+" |";
}
if (displayPiece(Piece.WHITE_KING,mask)) {
displayedSomething=true;
display+=" "+Piece.WHITE_KING_CHAR+" |";
}
if (displayPiece(Piece.WHITE_KNIGHT,mask)) {
displayedSomething=true;
display+=" "+Piece.WHITE_KNIGHT_CHAR+" |";
}
if (displayPiece(Piece.WHITE_ROOK,mask)) {
displayedSomething=true;
display+=" "+Piece.WHITE_ROOK_CHAR+" |";
}
if (displayPiece(Piece.WHITE_BISHOP,mask)) {
displayedSomething=true;
display+=" "+Piece.WHITE_BISHOP_CHAR+" |";
}
if (!displayedSomething)
display+=" |";
}
System.out.println(display);
}
System.out.println(" +---+---+---+---+---+---+---+---+");
System.out.println(" a b c d e f g h");
System.out.println();
}
/*=================*
* PRIVATE METHODS *
*=================*/
/* private long getBitBoard(int bitboard_number) {
return bitBoards[bitboard_number];
}
*/
protected void addPiece(Square square, Piece piece) {
//OR :
// 0 OR a = a
// 1 OR a = 1
//add Piece to corresponding bitboard.
bitBoards[piece.getPieceNumber()] |= mapSquaresToBits[squareToBitBoardSquare(square)];
//update the bitboard of all pieces of that color
bitBoards[piece.getColor()] |= mapSquaresToBits[squareToBitBoardSquare(square)];
numberOfPieces[piece.getPieceNumber()] += 1;
numberOfPieces[piece.getColor()] += 1;
}
protected void removePiece(Square square, Piece piece) throws NoPieceOnSquare {
//XOR :
// 0 XOR a = a
// 1 XOR 0 = 1 !!! Don't remove a piece that don't exist !!!
// 1 XOR 1 = 0
//remove Piece to corresponding bitboard.
if (SuicideChess.BITBOARD_REMOVEPIECE_CHECK_REMOVE) {
if (!isEmpty(square, piece)) {
bitBoards[piece.getPieceNumber()] ^= mapSquaresToBits[squareToBitBoardSquare(square)];
} else {
throw new NoPieceOnSquare("Square: "+square + "; Piece: " + piece.getPieceNumber());
}
} else {
bitBoards[piece.getPieceNumber()] ^= mapSquaresToBits[squareToBitBoardSquare(square)];
}
//update the bitboard of all pieces of that color
bitBoards[piece.getColor()] ^= mapSquaresToBits[squareToBitBoardSquare(square)];;
numberOfPieces[piece.getPieceNumber()] -= 1;
numberOfPieces[piece.getColor()] -= 1;
}
//used by function display()
private boolean displayPiece(int whatToDisplay, long mask) {
if ((bitBoards[whatToDisplay] & mask)==0) {
return false;
} else {
return true;
}
}
//calculates player's mobility
public int mobility(int colour) throws NotAValidSquare {
Board thisCopy = new Board(this);
thisCopy.currentPlayer=colour;
Rules.legalMovesForPlayer(thisCopy);
if (Rules.getLegalMovesCapture().size()!=0) {
return Rules.getLegalMovesCapture().size()*REAL_MOBILITY_VALUE
+Rules.getLegalMovesNonCapture().size()*RELATIVE_MOBILITY_VALUE;
} else {
return Rules.getLegalMovesNonCapture().size()*REAL_MOBILITY_VALUE;
}
}
private void evaluateNewBoardValue (Move move) throws NotAValidSquare {
if (move.isCaptureMove()) {
if (numberOfPieces[Piece.BLACK] == 0) {
boardValue = BLACK_WINS;
} else if (numberOfPieces[Piece.WHITE] == 0){
boardValue = WHITE_WINS;
} else {
//this is a very very basic evaluation function that will be changed.
//boardValue = numberOfBlackPieces - numberOfWhitePieces;
boardValue = 0;
/*if((numberOfPieces[Piece.BLACK_PAWN] <= ENDGAME_PAWNS) || (numberOfPieces[Piece.WHITE_PAWN] <= ENDGAME_PAWNS)
|| (numberOfPieces[Piece.BLACK_PIECES] <= ENDGAME_PIECES) || (numberOfPieces[Piece.WHITE_PIECES] <= ENDGAME_PIECES) ) {
//System.out.println("Playing endgame");
for (int i = Piece.OFFSET; i<=Piece.MAX_PIECE_NUMBER; i++) {
boardValue += numberOfPieces[i]*Piece.PIECE_VALUE_ENDGAME[i];
}
} else {
//System.out.println("Playing midgame");
/*for (int i = Piece.OFFSET; i<=Piece.MAX_PIECE_NUMBER; i++) {
boardValue += numberOfPieces[i]*Piece.PIECE_VALUE_MIDDLEGAME[i];
}*/
for(int squareNb = 0; squareNb