current
BSD Fan
muss mal was testen...
Code:
/*-
* Copyright (c) 2003, 2004 Thorsten Greiner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package greiner.amy.chess.engine;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import greiner.amy.bitboard.BitBoard;
import greiner.amy.bitboard.BoardConstants;
import greiner.amy.common.engine.IntVector;
import greiner.amy.common.engine.MoveList;
import org.apache.log4j.Logger;
/**
* An implementation of a chess board using bitboards.
*
* @author <a href = "mailto:thorsten@tgreiner.net">Thorsten Greiner</a>
*/
public class ChessBoard implements ChessConstants, BoardConstants {
/** The Log. */
private static Logger log = Logger.getLogger(ChessBoard.class);
/** Constant for castling right. */
private static final int WHITE_CASTLE_KINGSIDE = 0x01;
/** Constant for castling right. */
private static final int WHITE_CASTLE_QUEENSIDE = 0x02;
/** Constant for castling right. */
private static final int BLACK_CASTLE_KINGSIDE = 0x04;
/** Constant for castling right. */
private static final int BLACK_CASTLE_QUEENSIDE = 0x08;
/** The names of the chess pieces. */
public static final char[] PIECE_NAME = {
' ', 'P', 'N', 'B', 'R', 'Q', 'K' };
/**
* An array of 64 bitboards containing the attacks originating from a
* square.
*/
private long[] atkTo = new long[BitBoard.SIZE];
/**
* An array of 64 bitboards indicating the squares from which the
* respective square is attacked.
*/
private long[] atkFr = new long[BitBoard.SIZE];
/** Bitboards containing the piece masks. */
private long[][] mask = new long[2][KING + 1];
/** A single bitboard for all sliding pieces on the board. */
private long slidingPieces;
/** The board. */
private int[] board = new int[BitBoard.SIZE];
/** Position of the white/black king. */
private int[] kingPos = new int[2];
/** The enPassant square. */
private int enPassant;
/** The castle status. */
private int castle;
/**
* Indicates the side to move, <code>true</code> if white to move,
* <code>false</code> otherwise.
*/
private boolean wtm;
/** The current ply. */
private int ply;
/** History information, required for undo. */
private List history = new ArrayList();
/** The positional hash key. */
private long posHash;
/** The pawn hash key. */
private long pawnHash;
/** The material signature. */
private int[] materialSignature = new int[2];
/** The Evaluator for this board. */
private Evaluator evaluator;
/** The increment needed to advance one rank. */
private static final int RANK_INC = 8;
/**
* Indicates wether a piece is a sliding piece - <code>true</code> for
* Bishop, Rook and Queen.
*/
private static final boolean[] IS_SLIDING = {
false, false, false, true, true, true, false
};
/**
* Table to translate EnPassant squares.
* EPTranslate[e4] = e3 means a pawns double stepped to e4 can be
* captured enpassant on e3
* EPTranslate[e3] = e4 means a enpassant capture on e3 will remove
* pawn on e4
*/
private static final int[] EP_TRANSLATE = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
A4, B4, C4, D4, E4, F4, G4, H4,
A3, B3, C3, D3, E3, F3, G3, H3,
A6, B6, C6, D6, E6, F6, G6, H6,
A5, B5, C5, D5, E5, F5, G5, H5,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
/** Bits to calculate the material signature. */
private static final short[] MATERIAL_SIGNATURE_BITS;
/**
* This inner class keeps status information for doMove/undoMove.
*/
private static final class History {
/** The move. */
private int move;
/** The en passant square. */
private int enPassant;
/** The piece captured. */
private int capturedPiece;
/** The castle status. */
private int castle;
/** The position hash key. */
private long posHash;
/** The pawn hash key. */
private long pawnHash;
/**
* Create a History object.
*/
public History() { }
/**
* Create a History object as a copy of another.
*
* @param that the source.
*/
public History(final History that) {
this.move = that.move;
this.enPassant = that.enPassant;
this.capturedPiece = that.capturedPiece;
this.castle = that.castle;
this.posHash = that.posHash;
this.pawnHash = that.pawnHash;
}
};
static {
MATERIAL_SIGNATURE_BITS = new short[KING + 1];
MATERIAL_SIGNATURE_BITS[PAWN] = 0x01;
MATERIAL_SIGNATURE_BITS[KNIGHT] = 0x02;
MATERIAL_SIGNATURE_BITS[BISHOP] = 0x04;
MATERIAL_SIGNATURE_BITS[ROOK] = 0x08;
MATERIAL_SIGNATURE_BITS[QUEEN] = 0x10;
}
/**
* Set the attacks of a piece of type <code>type</code> on
* <code>square</code> with <code>theWtm</code>.
*
* @param type the type of the piece (<code>Pawn</code> to
* <code>King</code>
* @param theWtm <code>true</code> if white to move
* @param square the target square
*/
private void atkSet(final int type, final boolean theWtm,
final int square) {
byte[] np, nd;
int nsq;
if (type == PAWN) {
if (theWtm) {
nd = Geometry.NEXT_DIR[PAWN][square];
} else {
nd = Geometry.NEXT_DIR[BLACK_PAWN][square];
}
nsq = nd[square];
atkTo[square] |= BitBoard.SET_MASK[nsq];
atkFr[nsq] |= BitBoard.SET_MASK[square];
if ((nsq = nd[nsq]) >= 0) {
atkTo[square] |= BitBoard.SET_MASK[nsq];
atkFr[nsq] |= BitBoard.SET_MASK[square];
}
} else {
np = Geometry.NEXT_POS[type][square];
nd = Geometry.NEXT_DIR[type][square];
nsq = np[square];
while (nsq >= 0) {
atkTo[square] |= BitBoard.SET_MASK[nsq];
atkFr[nsq] |= BitBoard.SET_MASK[square];
if (board[nsq] != 0) {
nsq = nd[nsq];
} else {
nsq = np[nsq];
}
}
}
}
/**
* Clear the attacks of a piece of type <code>type</code> on
* <code>square</code> with <code>theWtm</code>.
*
* @param type the type of the piece (<code>Pawn</code> to
* <code>King</code>
* @param theWtm <code>true</code> if white to move
* @param square the target square
*/
private void atkClr(final int type, final boolean theWtm,
final int square) {
byte[] np, nd;
int nsq;
atkTo[square] = 0;
if (type == PAWN) {
if (theWtm) {
nd = Geometry.NEXT_DIR[PAWN][square];
} else {
nd = Geometry.NEXT_DIR[BLACK_PAWN][square];
}
nsq = nd[square];
atkFr[nsq] &= BitBoard.CLEAR_MASK[square];
if ((nsq = nd[nsq]) >= 0) {
atkFr[nsq] &= BitBoard.CLEAR_MASK[square];
}
} else {
np = Geometry.NEXT_POS[type][square];
nd = Geometry.NEXT_DIR[type][square];
nsq = np[square];
while (nsq >= 0) {
atkFr[nsq] &= BitBoard.CLEAR_MASK[square];
if (board[nsq] != 0) {
nsq = nd[nsq];
} else {
nsq = np[nsq];
}
}
}
}
/**
* Output debugging information.
*/
public void debug() {
log.error("Problem at ply " + ply);
log.error(this);
Iterator e = history.iterator();
while (e.hasNext()) {
History h = (History) e.next();
log.error(Move.toString(h.move));
}
// throw new RuntimeException();
}
/**
* Check the sanity of the internal data structures.
*
* @throws IllegalStateException if the internal state of this ChessBoard
* has been corrupted
*/
void checkSanity() throws IllegalStateException {
for (int i = 0; i < BitBoard.SIZE; i++) {
long tmp = atkTo[i];
while (tmp != 0) {
int idx = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[idx];
if ((atkFr[idx] & BitBoard.SET_MASK[i]) == 0) {
throw new IllegalStateException();
}
}
}
for (int i = 0; i < BitBoard.SIZE; i++) {
long tmp = atkFr[i];
while (tmp != 0) {
int idx = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[idx];
if ((atkTo[idx] & BitBoard.SET_MASK[i]) == 0) {
log.error("Panic 2.");
log.error("atkFr[" + i + "]");
log.error(BitBoard.toString(atkFr[i]));
log.error("---");
log.error("atkTo[" + idx + "]");
log.error(BitBoard.toString(atkTo[idx]));
throw new IllegalStateException();
}
}
}
checkMaterialSignatures();
}
/**
* Check the consistency of the material signatures.
*/
private void checkMaterialSignatures() {
int tw = 0;
int tb = 0;
for (int i = ChessConstants.PAWN; i <= ChessConstants.QUEEN; i++) {
if (getMask(true, i) != 0L) {
tw |= MATERIAL_SIGNATURE_BITS[i];
}
if (getMask(false, i) != 0L) {
tb |= MATERIAL_SIGNATURE_BITS[i];
}
}
if (tw != materialSignature[0]) {
log.error("Material signature mismatch for white, expected "
+ Integer.toHexString(tw)
+ " but it is "
+ Integer.toHexString(materialSignature[0]));
debug();
throw new IllegalStateException();
}
if (tb != materialSignature[1]) {
log.error("Material signature mismatch for black, expected "
+ Integer.toHexString(tb)
+ " but it is "
+ Integer.toHexString(materialSignature[1]));
debug();
throw new IllegalStateException();
}
}
/**
* Recalculate Attacks from "from" to "to" after the piece on "to" has
* been removed.
*
* @param from the from square
* @param to the to square
*/
private void gainAttack(final int from, final int to) {
byte[] nsq = Geometry.NEXT_SQ[from];
int sq = to;
for (;;) {
sq = nsq[sq];
if (sq < 0) {
break;
}
atkTo[from] |= BitBoard.SET_MASK[sq];
atkFr[sq] |= BitBoard.SET_MASK[from];
if (board[sq] != 0) {
break;
}
}
}
/**
* Recalculate Attacks from "from" to "to" after a piece has been put
* onto "to".
*
* @param from the from square
* @param to the to square
*/
private void looseAttack(final int from, final int to) {
byte[] nsq = Geometry.NEXT_SQ[from];
int sq = to;
for (;;) {
sq = nsq[sq];
if (sq < 0) {
break;
}
atkTo[from] &= BitBoard.CLEAR_MASK[sq];
atkFr[sq] &= BitBoard.CLEAR_MASK[from];
if (board[sq] != 0) {
break;
}
}
}
/**
* Recalculate all ray attacks which pass through square "to" after
* the piece on this square has been removed.
*
* @param to the square on which the piece was removed.
*/
private void gainAttacks(final int to) {
long tmp = atkFr[to] & slidingPieces;
while (tmp != 0L) {
int i = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[i];
gainAttack(i, to);
}
}
/**
* Recalculate all ray attacks which pass through square "to" after
* a piece has been put onto this square.
*
* @param to the square on which the piece was placed
*/
private void looseAttacks(final int to) {
long tmp = atkFr[to] & slidingPieces;
while (tmp != 0L) {
int i = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[i];
looseAttack(i, to);
}
}
/**
* Given the Masks and the board[] array, recalculate all necessary
* data.
*/
private void recalcAttacks() {
int i;
long tmp;
for (i = 0; i < BitBoard.SIZE; i++) {
atkTo[i] = 0L;
atkFr[i] = 0L;
}
for (i = 0; i <= KING; i++) {
mask[0][i] = 0L;
mask[1][i] = 0L;
}
slidingPieces = 0;
posHash = 0L;
pawnHash = 0L;
materialSignature[0] = 0;
materialSignature[1] = 0;
for (int sq = 0; sq < BitBoard.SIZE; sq++) {
int pc = board[sq];
if (pc > 0) {
mask[0][0] |= BitBoard.SET_MASK[sq];
mask[0][pc] |= BitBoard.SET_MASK[sq];
materialSignature[0] |= MATERIAL_SIGNATURE_BITS[pc];
if (IS_SLIDING[pc]) {
slidingPieces |= BitBoard.SET_MASK[sq];
}
posHash ^= Hashing.HASH_KEYS[0][pc][sq];
if (pc == PAWN) {
pawnHash ^= Hashing.HASH_KEYS[0][PAWN][sq];
}
if (pc == KING) {
kingPos[0] = sq;
}
} else if (pc < 0) {
mask[1][0] |= BitBoard.SET_MASK[sq];
mask[1][-pc] |= BitBoard.SET_MASK[sq];
materialSignature[1] |= MATERIAL_SIGNATURE_BITS[-pc];
if (IS_SLIDING[-pc]) {
slidingPieces |= BitBoard.SET_MASK[sq];
}
posHash ^= Hashing.HASH_KEYS[1][-pc][sq];
if (-pc == PAWN) {
pawnHash ^= Hashing.HASH_KEYS[1][PAWN][sq];
}
if (-pc == KING) {
kingPos[1] = sq;
}
}
}
tmp = mask[0][0];
while (tmp != 0) {
int sq = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[sq];
atkSet(board[sq], true, sq);
}
tmp = mask[1][0];
while (tmp != 0) {
int sq = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[sq];
atkSet(-board[sq], false, sq);
}
// Clear en passant square if it is set but no pawn attacks it
if (enPassant != 0
&& ((getMask(wtm, PAWN) & atkFr[enPassant]) == 0L)) {
enPassant = 0;
}
if (enPassant != 0) {
posHash ^= Hashing.EN_PASSANT_HASH_KEYS[enPassant];
}
posHash ^= Hashing.CASTLE_HASH_KEYS[castle];
}
/**
* Make a move on the board.
*
* @param move the move to be made
*/
public void doMove(final int move) {
if (history.size() <= ply) {
history.add(new History());
}
History hist = (History) history.get(ply);
// remember status information
hist.enPassant = enPassant;
hist.castle = castle;
hist.move = move;
hist.posHash = posHash;
hist.pawnHash = pawnHash;
int from = Move.getFrom(move);
int to = Move.getTo(move);
int type = getPieceAt(from);
final int side = side(wtm);
//
// Remove the piece from its from square
//
atkClr(type, wtm, from);
board[from] = 0;
mask[side][0] &= BitBoard.CLEAR_MASK[from];
mask[side][type] &= BitBoard.CLEAR_MASK[from];
if (IS_SLIDING[type]) {
slidingPieces &= BitBoard.CLEAR_MASK[from];
}
gainAttacks(from);
posHash ^= Hashing.HASH_KEYS[side][type][from];
if (type == PAWN) {
pawnHash ^= Hashing.HASH_KEYS[side][PAWN][from];
}
if ((move & Move.CAPTURE) != 0) {
//
// Handle captures
//
final int captured = getPieceAt(to);
if (captured == KING) {
log.fatal("Captured a king.");
throw new RuntimeException("Captured a king.");
}
atkClr(captured, !wtm, to);
hist.capturedPiece = captured;
mask[1 ^ side][0] &= BitBoard.CLEAR_MASK[to];
mask[1 ^ side][captured] &= BitBoard.CLEAR_MASK[to];
if (IS_SLIDING[captured]) {
slidingPieces &= BitBoard.CLEAR_MASK[to];
}
// Update castling rights if rook captured
if (wtm) {
if (to == H8) {
castle &= ~BLACK_CASTLE_KINGSIDE;
}
if (to == A8) {
castle &= ~BLACK_CASTLE_QUEENSIDE;
}
} else {
if (to == H1) {
castle &= ~WHITE_CASTLE_KINGSIDE;
}
if (to == A1) {
castle &= ~WHITE_CASTLE_QUEENSIDE;
}
}
posHash ^= Hashing.HASH_KEYS[1 ^ side][captured][to];
if (captured == PAWN) {
pawnHash ^= Hashing.HASH_KEYS[1 ^ side][PAWN][to];
}
if (evaluator != null) {
evaluator.move(from, to, type, wtm);
evaluator.capture(to, captured, !wtm);
}
if (mask[1 ^ side][captured] == 0L) {
materialSignature[1 ^ side] &=
~MATERIAL_SIGNATURE_BITS[captured];
}
} else if ((move & Move.ENPASSANT) != 0) {
//
// Handle en passant captures
//
int epto = EP_TRANSLATE[to];
atkClr(PAWN, !wtm, epto);
hist.capturedPiece = PAWN;
mask[1 ^ side][0] &= BitBoard.CLEAR_MASK[epto];
mask[1 ^ side][PAWN] &= BitBoard.CLEAR_MASK[epto];
board[epto] = 0;
gainAttacks(epto);
looseAttacks(to);
posHash ^= Hashing.HASH_KEYS[1 ^ side][PAWN][epto];
pawnHash ^= Hashing.HASH_KEYS[1 ^ side][PAWN][epto];
if (evaluator != null) {
evaluator.move(from, to, PAWN, wtm);
evaluator.capture(epto, PAWN, !wtm);
}
if (mask[1 ^ side][PAWN] == 0L) {
materialSignature[1 ^ side] &= ~MATERIAL_SIGNATURE_BITS[PAWN];
}
} else if (Move.isCastle(move)) {
//
// Handle castling
//
int rookFrom;
int rookTo;
if (Move.isKingSideCastle(move)) {
rookFrom = from + 3;
rookTo = from + 1;
} else {
rookFrom = from - 4;
rookTo = from - 1;
}
atkClr(ROOK, wtm, rookFrom);
board[rookTo] = board[rookFrom];
board[rookFrom] = 0;
mask[side][0] &= BitBoard.CLEAR_MASK[rookFrom];
mask[side][ROOK] &= BitBoard.CLEAR_MASK[rookFrom];
slidingPieces &= BitBoard.CLEAR_MASK[rookFrom];
mask[side][0] |= BitBoard.SET_MASK[rookTo];
mask[side][ROOK] |= BitBoard.SET_MASK[rookTo];
slidingPieces |= BitBoard.SET_MASK[rookTo];
atkSet(ROOK, wtm, rookTo);
looseAttacks(rookTo);
looseAttacks(to);
// Update castling rights
if (wtm) {
castle &=
~(WHITE_CASTLE_QUEENSIDE | WHITE_CASTLE_KINGSIDE);
} else {
castle &=
~(BLACK_CASTLE_QUEENSIDE | BLACK_CASTLE_KINGSIDE);
}
posHash ^= (Hashing.HASH_KEYS[side][ROOK][rookFrom]
^ Hashing.HASH_KEYS[side][ROOK][rookTo]);
if (evaluator != null) {
evaluator.move(from, to, KING, wtm);
evaluator.move(rookFrom, rookTo, ROOK, wtm);
}
} else {
//
// Handle ordinary move
//
looseAttacks(to);
if (evaluator != null) {
evaluator.move(from, to, type, wtm);
}
}
// Update castling rights if rook or king moved
if (wtm) {
if (from == H1) {
castle &= ~WHITE_CASTLE_KINGSIDE;
}
if (from == A1) {
castle &= ~WHITE_CASTLE_QUEENSIDE;
}
if (from == E1) {
castle &= ~(WHITE_CASTLE_QUEENSIDE | WHITE_CASTLE_KINGSIDE);
}
} else {
if (from == H8) {
castle &= ~BLACK_CASTLE_KINGSIDE;
}
if (from == A8) {
castle &= ~BLACK_CASTLE_QUEENSIDE;
}
if (from == E8) {
castle &= ~(BLACK_CASTLE_QUEENSIDE | BLACK_CASTLE_KINGSIDE);
}
}
//
// Handle promotion
//
if (Move.isPromotion(move)) {
int promotedTo = Move.getPromoPiece(move);
if (evaluator != null) {
evaluator.capture(to, PAWN, wtm);
evaluator.add(to, promotedTo, wtm);
}
materialSignature[side] |= MATERIAL_SIGNATURE_BITS[promotedTo];
if (mask[side][PAWN] == 0L) {
materialSignature[side] &= ~MATERIAL_SIGNATURE_BITS[PAWN];
}
type = promotedTo;
}
board[to] = wtm ? type : -type;
mask[side][0] |= BitBoard.SET_MASK[to];
mask[side][type] |= BitBoard.SET_MASK[to];
if (IS_SLIDING[type]) {
slidingPieces |= BitBoard.SET_MASK[to];
}
if (type == KING) {
kingPos[side] = to;
}
atkSet(type, wtm, to);
posHash ^= Hashing.HASH_KEYS[side][type][to];
if (type == PAWN) {
pawnHash ^= Hashing.HASH_KEYS[side][PAWN][to];
}
//
// If double pawn push, set en passant square
//
if ((move & Move.PAWN_DOUBLE) != 0) {
int etmp = EP_TRANSLATE[to];
if ((mask[1 ^ side][PAWN] & atkFr[etmp]) != 0L) {
enPassant = etmp;
} else {
enPassant = 0;
}
} else {
enPassant = 0;
}
if (castle != hist.castle) {
posHash ^= (Hashing.CASTLE_HASH_KEYS[castle]
^ Hashing.CASTLE_HASH_KEYS[hist.castle]);
}
if (enPassant != hist.enPassant) {
posHash ^= (Hashing.EN_PASSANT_HASH_KEYS[enPassant]
^ Hashing.EN_PASSANT_HASH_KEYS[hist.enPassant]);
}
posHash ^= Hashing.WTM_HASH;
ply++;
wtm = !wtm;
}
/**
* Do a null move.
*/
public void doNull() {
if (history.size() <= ply) {
history.add(new History());
}
History hist = (History) history.get(ply);
// remember status information
hist.enPassant = enPassant;
hist.castle = castle;
hist.move = 0;
hist.posHash = posHash;
hist.pawnHash = pawnHash;
enPassant = 0;
if (enPassant != hist.enPassant) {
posHash ^= (Hashing.EN_PASSANT_HASH_KEYS[enPassant]
^ Hashing.EN_PASSANT_HASH_KEYS[hist.enPassant]);
}
posHash ^= Hashing.WTM_HASH;
ply++;
wtm = !wtm;
}
/**
* Un-Make the last move on the board.
*/
public void undoMove() {
ply--;
wtm = !wtm;
History hist = (History) history.get(ply);
int move = hist.move;
if (move != 0) {
int from = Move.getFrom(move);
int to = Move.getTo(move);
int type = getPieceAt(to);
final int side = side(wtm);
atkClr(type, wtm, to);
mask[side][0] &= BitBoard.CLEAR_MASK[to];
mask[side][type] &= BitBoard.CLEAR_MASK[to];
if (IS_SLIDING[type]) {
slidingPieces &= BitBoard.CLEAR_MASK[to];
}
if ((move & Move.CAPTURE) != 0) {
//
// Handle captures
//
int captured = hist.capturedPiece;
atkSet(captured, !wtm, to);
mask[1 ^ side][0] |= BitBoard.SET_MASK[to];
mask[1 ^ side][captured] |= BitBoard.SET_MASK[to];
if (IS_SLIDING[captured]) {
slidingPieces |= BitBoard.SET_MASK[to];
}
board[to] = wtm ? -captured : captured;
if (evaluator != null) {
evaluator.add(to, captured, !wtm);
evaluator.move(to, from, type, wtm);
}
materialSignature[1 ^ side] |=
MATERIAL_SIGNATURE_BITS[captured];
} else if ((move & Move.ENPASSANT) != 0) {
//
// Handle en passant captures
//
int epto = EP_TRANSLATE[to];
atkSet(PAWN, !wtm, epto);
mask[1 ^ side][0] |= BitBoard.SET_MASK[epto];
mask[1 ^ side][PAWN] |= BitBoard.SET_MASK[epto];
board[epto] = wtm ? -PAWN : PAWN;
looseAttacks(epto);
board[to] = 0;
gainAttacks(to);
if (evaluator != null) {
evaluator.add(epto, PAWN, !wtm);
evaluator.move(to, from, PAWN, wtm);
}
materialSignature[1 ^ side] |=
MATERIAL_SIGNATURE_BITS[PAWN];
} else if (Move.isCastle(move)) {
//
// Handle castling
//
int rookFrom;
int rookTo;
if (Move.isKingSideCastle(move)) {
rookFrom = from + 3;
rookTo = from + 1;
} else {
rookFrom = from - 4;
rookTo = from - 1;
}
atkClr(ROOK, wtm, rookTo);
board[rookFrom] = board[rookTo];
board[rookTo] = 0;
board[to] = 0;
mask[side][0] &= BitBoard.CLEAR_MASK[rookTo];
mask[side][ROOK] &= BitBoard.CLEAR_MASK[rookTo];
slidingPieces &= BitBoard.CLEAR_MASK[rookTo];
mask[side][0] |= BitBoard.SET_MASK[rookFrom];
mask[side][ROOK] |= BitBoard.SET_MASK[rookFrom];
slidingPieces |= BitBoard.SET_MASK[rookFrom];
atkSet(ROOK, wtm, rookFrom);
gainAttacks(rookTo);
gainAttacks(to);
if (evaluator != null) {
evaluator.move(to, from, KING, wtm);
evaluator.move(rookTo, rookFrom, ROOK, wtm);
}
} else {
//
// Handle ordinary move
//
board[to] = 0;
gainAttacks(to);
if (evaluator != null) {
evaluator.move(to, from, type, wtm);
}
}
//
// Handle promotion
//
if (Move.isPromotion(move)) {
if (evaluator != null) {
evaluator.capture(from, type, wtm);
evaluator.add(from, PAWN, wtm);
}
materialSignature[side] |= MATERIAL_SIGNATURE_BITS[PAWN];
if (mask[side][type] == 0L) {
materialSignature[side] &= ~MATERIAL_SIGNATURE_BITS[type];
}
type = PAWN;
}
board[from] = wtm ? type : -type;
mask[side][0] |= BitBoard.SET_MASK[from];
mask[side][type] |= BitBoard.SET_MASK[from];
if (IS_SLIDING[type]) {
slidingPieces |= BitBoard.SET_MASK[from];
}
atkSet(type, wtm, from);
looseAttacks(from);
if (type == KING) {
kingPos[side] = from;
}
}
enPassant = hist.enPassant;
castle = hist.castle;
posHash = hist.posHash;
pawnHash = hist.pawnHash;
}
/**
* Is white to move?
*
* @return <code>true</code> if white is on the move.
*/
public final boolean isWtm() {
return wtm;
}
/**
* Get the piece on <code>square</code>.
*
* @param square the square on the chessboard (0...63)
* @return the type of the piece on <code>square</code> (0,
* <code>Pawn</code> ... <code>King</code>
*/
public final int getPieceAt(final int square) {
return Math.abs(board[square]);
}
/**
* Get the color of the piece on <code>square</code>.
*
* @param square the square on the chessboard (0...63)
* @return <code>true</code> if the piece on <code>square</code>
* is white, <code>false</code> otherwise.
*/
public final boolean isWhiteAt(final int square) {
return board[square] > 0;
}
/**
* Check if the side to move is in check.
*
* @return <code>true</code> if the side to move is in check.
*/
public final boolean isInCheck() {
if (wtm) {
return (atkFr[kingPos[0]] & mask[1][0]) != 0L;
} else {
return (atkFr[kingPos[1]] & mask[0][0]) != 0L;
}
}
/**
* Check if the side <b>not</b> to move is in check. This can
* only happen if the previous move was not legal by the
* rules of chess.
*
* @return <code>true</code> if the side to move is in check.
*/
public final boolean isOppInCheck() {
if (!wtm) {
return (atkFr[kingPos[0]] & mask[1][0]) != 0L;
} else {
return (atkFr[kingPos[1]] & mask[0][0]) != 0L;
}
}
/**
* Check if castling is legal in the current position.
*
* @param move the castling move to check
* @return <code>true</code> if castling is legal.
*/
public final boolean isCastleLegal(final int move) {
int from = Move.getFrom(move);
int oside = wtm ? 1 : 0;
if ((move & Move.CASTLE_KSIDE) != 0) {
if (wtm && (castle & WHITE_CASTLE_KINGSIDE) == 0) {
return false;
}
if (!wtm && (castle & BLACK_CASTLE_KINGSIDE) == 0) {
return false;
}
if (board[from + 1] != 0 || board[from + 2] != 0) {
return false;
}
return ((atkFr[from] | atkFr[from + 1] | atkFr[from + 2])
& mask[oside][0]) == 0;
} else {
if (wtm && (castle & WHITE_CASTLE_QUEENSIDE) == 0) {
return false;
}
if (!wtm && (castle & BLACK_CASTLE_QUEENSIDE) == 0) {
return false;
}
if (board[from - 1] != 0 || board[from - 2] != 0
|| board[from - 3] != 0) {
return false;
}
return ((atkFr[from] | atkFr[from - 1] | atkFr[from - 2])
& mask[oside][0]) == 0;
}
// never reached
}
/**
* Generate the pseudo legal moves in this position.
* A pseudo legal move is a move which may leave the moving
* side's king in check but is legal otherwise.
*
* @param mvs the MoveList to add moves to.
*/
public void generatePseudoLegalMoves(final MoveList mvs) {
long tmp = getMask(!wtm);
while (tmp != 0L) {
int to = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[to];
generateTo(to, mvs);
}
tmp = getMask(wtm);
while (tmp != 0L) {
int to = BitBoard.findFirstOne(tmp);
tmp &= BitBoard.CLEAR_MASK[to];
generateFrom(to, mvs);
}
generateEnPassant(mvs);
}
/**
* Generate all legal moves in this position.
*
* @param mvs the MoveList in which the moves will be inserted
*/
public void generateLegalMoves(final MoveList mvs) {
IntVector tmvs = new IntVector();
generatePseudoLegalMoves(tmvs);
for (int i = 0; i < tmvs.size(); i++) {
int move = tmvs.get(i);
if ((move & Move.CASTLE) != 0 && !isCastleLegal(move)) {
continue;
}
doMove(move);
boolean inCheck = isOppInCheck();
undoMove();
if (!inCheck) {
mvs.add(move);
}
}
}
/**
* Generate all capturing moves on a target square.
*
* @param to the target square.
* @param mvs the list to insert the moves into.
*/
protected void generateTo(final int to, final MoveList mvs) {
long atks = atkFr[to] & getMask(wtm);
while (atks != 0) {
int from = BitBoard.findFirstOne(atks);
atks &= BitBoard.CLEAR_MASK[from];
if (getPieceAt(from) == PAWN && (to <= H1 || to >= A8)) {
// promotion
int move = Move.makeMove(from, to) | Move.CAPTURE;
mvs.add(move | Move.PROMO_QUEEN);
mvs.add(move | Move.PROMO_ROOK);
mvs.add(move | Move.PROMO_BISHOP);
mvs.add(move | Move.PROMO_KNIGHT);
} else {
// normal capture
mvs.add(Move.makeMove(from, to) | Move.CAPTURE);
}
}
}
/**
* Generate all non-capturing moves originating from a square.
*
* @param from the originating square.
* @param mvs the list to insert the moves into.
*/
protected void generateFrom(final int from, final MoveList mvs) {
int pc = getPieceAt(from);
boolean isWhite = isWhiteAt(from);
if (pc == PAWN) {
if (isWhite) {
// white pawns
int next = from + RANK_INC;
if (board[next] == 0) {
if (next >= A8) {
// promotion
int move = Move.makeMove(from, next);
mvs.add(move | Move.PROMO_QUEEN);
mvs.add(move | Move.PROMO_ROOK);
mvs.add(move | Move.PROMO_BISHOP);
mvs.add(move | Move.PROMO_KNIGHT);
} else {
mvs.add(Move.makeMove(from, next));
if (next >= A3 && next <= H3) {
next += RANK_INC;
if (board[next] == 0) {
mvs.add(Move.makeMove(from, next)
| Move.PAWN_DOUBLE);
}
}
}
}
} else {
// black pawns
int next = from - RANK_INC;
if (board[next] == 0) {
if (next <= H1) {
// promotion
int move = Move.makeMove(from, next);
mvs.add(move | Move.PROMO_QUEEN);
mvs.add(move | Move.PROMO_ROOK);
mvs.add(move | Move.PROMO_BISHOP);
mvs.add(move | Move.PROMO_KNIGHT);
} else {
mvs.add(Move.makeMove(from, next));
if (next >= A6 && next <= H6) {
next -= RANK_INC;
if (board[next] == 0) {
mvs.add(Move.makeMove(from, next)
| Move.PAWN_DOUBLE);
}
}
}
}
}
} else if (pc == KING) {
long atks = atkTo[from] & ~(mask[0][0] | mask[1][0]);
long opp = getMask(!isWhite);
while (atks != 0) {
int to = BitBoard.findFirstOne(atks);
atks &= BitBoard.CLEAR_MASK[to];
if ((atkFr[to] & opp) == 0L) {
mvs.add(Move.makeMove(from, to));
}
}
if (isWhite) {
if ((castle & WHITE_CASTLE_KINGSIDE) != 0) {
mvs.add(Move.makeMove(E1, G1) | Move.CASTLE_KSIDE);
}
if ((castle & WHITE_CASTLE_QUEENSIDE) != 0) {
mvs.add(Move.makeMove(E1, C1) | Move.CASTLE_QSIDE);
}
} else {
if ((castle & BLACK_CASTLE_KINGSIDE) != 0) {
mvs.add(Move.makeMove(E8, G8) | Move.CASTLE_KSIDE);
}
if ((castle & BLACK_CASTLE_QUEENSIDE) != 0) {
mvs.add(Move.makeMove(E8, C8) | Move.CASTLE_QSIDE);
}
}
} else {
long atks = atkTo[from] & ~(mask[0][0] | mask[1][0]);
while (atks != 0) {
int to = BitBoard.findFirstOne(atks);
atks &= BitBoard.CLEAR_MASK[to];
mvs.add(Move.makeMove(from, to));
}
}
}
/**
* Generate en passant captures.
*
* @param mvs the list to insert the moves into.
*/
protected void generateEnPassant(final MoveList mvs) {
if (enPassant != 0) {
long atks = atkFr[enPassant] & getMask(wtm, PAWN);
while (atks != 0) {
int from = BitBoard.findFirstOne(atks);
atks &= BitBoard.CLEAR_MASK[from];
mvs.add(Move.makeMove(from, enPassant) | Move.ENPASSANT);
}
}
}
/**
* Check if <code>move</code> is pseudo legal in the current position.
*
* @param move the move
* @return the legality of the move
*/
public boolean isPseudoLegalMove(final int move) {
int from = Move.getFrom(move);
// There must be a right-colored piece on the from-square
if (wtm && board[from] <= 0) {
return false;
}
if (!wtm && board[from] >= 0) {
return false;
}
// if promotion, moving piece must be a pawn
if (Move.isPromotion(move) && getPieceAt(from) != PAWN) {
return false;
}
int to = Move.getTo(move);
if ((move & Move.CAPTURE) != 0) {
// if capture, there must be an enemy piece on the to-square,
// and the to square must be attacked from the from-square.
if (wtm && board[to] >= 0) {
return false;
}
if (!wtm && board[to] <= 0) {
return false;
}
return (atkTo[from] & BitBoard.SET_MASK[to]) != 0L;
} else if ((move & Move.ENPASSANT) != 0) {
// if enpassant capture, the to-square must be the enpassant
// square, and the moving piece must be a pawn
if (enPassant == 0 || to != enPassant) {
return false;
}
if (getPieceAt(from) != PAWN) {
return false;
}
return (atkTo[from] & BitBoard.SET_MASK[to]) != 0L;
} else if ((move & Move.CASTLE) != 0) {
return isCastleLegal(move);
} else {
// to-square must be empty
if (board[to] != 0) {
return false;
}
if (getPieceAt(from) == PAWN) {
int next = wtm ? from + RANK_INC : from - RANK_INC;
if ((move & Move.PAWN_DOUBLE) != 0) {
if (board[next] != 0) {
return false;
}
next = wtm ? next + RANK_INC : next - RANK_INC;
}
if (next != to) {
return false;
}
if (wtm && !Move.isPromotion(move) && to >= A8) {
return false;
}
if (!wtm && !Move.isPromotion(move) && to <= H1) {
return false;
}
} else {
if ((atkTo[from] & BitBoard.SET_MASK[to]) == 0) {
return false;
}
if ((move & Move.PAWN_DOUBLE) != 0) {
return false;
}
}
}
return true;
}
/**
* Create a textual representation of the current position.
*
* @return the current position as an ASCII graphics board.
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(" +---+---+---+---+---+---+---+---+\n");
for (int rk = 7; rk >= 0; rk--) {
buffer.append(' ');
buffer.append((char) ('1' + rk));
buffer.append(' ');
for (int fl = 0; fl < 8; fl++) {
int i = (rk << 3) + fl;
buffer.append('|');
if (enPassant != 0 && i == enPassant) {
buffer.append("<E>");
} else {
if (board[i] < 0) {
buffer.append('*');
} else {
buffer.append(' ');
}
buffer.append(PIECE_NAME[getPieceAt(i)]);
if (board[i] < 0) {
buffer.append('*');
} else {
buffer.append(' ');
}
}
}
buffer.append("|");
if (rk == 4) {
buffer.append(" Hashkey: ");
buffer.append(Long.toString(posHash, 16));
}
if ((rk == 7 && !wtm) || (rk == 0 && wtm)) {
buffer.append(" *");
}
buffer.append("\n +---+---+---+---+---+---+---+---+\n");
}
buffer.append(" a b c d e f g h\n");
return buffer.toString();
}
/**
* Get the position of certain piece types.
*
* @param theWtm the side to move
* @param type the type
* @param squares array to store positions in
* @return the count
*/
public int getPositions(final boolean theWtm, final int type,
final int[] squares) {
long pcs = getMask(theWtm, type);
int cnt = 0;
while (pcs != 0L) {
int sq = BitBoard.findFirstOne(pcs);
pcs &= BitBoard.CLEAR_MASK[sq];
squares[cnt++] = sq;
}
return cnt;
}
/**
* Get the material signature for a side.
* <p>
* The material signature is an integer derived by logical 'OR'ing
* the following constants:
* <ul>
* <li> 1 if at least one pawn present
* <li> 2 if at least one knight present
* <li> 4 if at least one bishop present
* <li> 8 if at least one rook present
* <li> 16 if at least one queen present
* </ul>
*
* @param theWtm the side to move, <code>true</code> for white
* @return the material signature
*/
public int getMaterialSignature(final boolean theWtm) {
return materialSignature[side(theWtm)];
}
/**
* Get the king's position.
*
* @param theWtm the side
* @return the king's position for <code>theWtm</code>
*/
public final int getKingPos(final boolean theWtm) {
return kingPos[side(theWtm)];
}
/**
* Get the mask for a given side.
*
* @param theWtm the side
* @return a bitboard for the given side
*/
public final long getMask(final boolean theWtm) {
return mask[side(theWtm)][0];
}
/**
* Get the mask for a given side/piece type.
*
* @param theWtm the side
* @param type the piece type
* @return a bitboard for the given side/piece type
*/
public final long getMask(final boolean theWtm, final int type) {
return mask[side(theWtm)][type];
}
/**
* Check if white can castle to the king side.
*
* @return <code>true</code> if white can castle king side
*/
public boolean canWhiteCastleKingSide() {
return (castle & WHITE_CASTLE_KINGSIDE) != 0;
}
/**
* Check if white can castle to the queen side.
*
* @return <code>true</code> if white can castle queen side
*/
public boolean canWhiteCastleQueenSide() {
return (castle & WHITE_CASTLE_QUEENSIDE) != 0;
}
/**
* Check if black can castle to the king side.
*
* @return <code>true</code> if black can castle king side
*/
public boolean canBlackCastleKingSide() {
return (castle & BLACK_CASTLE_KINGSIDE) != 0;
}
/**
* Check if black can castle to the queen side.
*
* @return <code>true</code> if black can castle queen side
*/
public boolean canBlackCastleQueenSide() {
return (castle & BLACK_CASTLE_QUEENSIDE) != 0;
}
// ----------------------------------------------------------------
// -- package access only!
// ----------------------------------------------------------------
/**
* Get the atkFr mask for a square.
*
* @param sq the square
* @return the atkFr mask
*/
final long getAtkFr(final int sq) {
return atkFr[sq];
}
/**
* Get the atkTo mask for a square.
*
* @param sq the square
* @return the atkTo mask
*/
final long getAtkTo(final int sq) {
return atkTo[sq];
}
/**
* Get the bitboard of sliding pieces.
*
* @return the bitboard of sliding pieces
*/
final long getSlidingPieces() {
return slidingPieces;
}
/**
* Get the non-pawn bitboard.
*
* @return the bitboard of non-pawn pieces.
*/
final long getMaskNonPawn() {
return mask[0][KNIGHT] | mask[1][KNIGHT]
| mask[0][BISHOP] | mask[1][BISHOP]
| mask[0][ROOK] | mask[1][ROOK]
| mask[0][QUEEN] | mask[1][QUEEN];
}
/**
* Get the enpassant square.
*
* @return the enpassant square
*/
final int getEnPassant() {
return enPassant;
}
/**
* Get the position hash code.
*
* @return the position hash code.
*/
public final long getPosHash() {
return posHash;
}
/**
* Get the pawn hash code.
*
* @return the pawn hash code.
*/
public final long getPawnHash() {
return pawnHash;
}
/**
* Check if this is a draw by repetition.
*
* @return <code>true</code> if the current position occurrs
* for the third time, <code>false</code> otherwise.
*/
public boolean isDrawByRepetition() {
return checkDraw(2);
}
/**
* Check if this position occurred at least once before.
*
* @return <code>true</code> if the current position occured at least one
* time before.
*/
protected boolean isRepeated() {
return checkDraw(1);
}
/**
* Check for a draw by detecting repeated positions.
*
* @param cnt number of occurences of the current position
* @return <code>true</code> if at least <code>cnt</code> occurences
* of the current position where detected
*/
private boolean checkDraw(final int cnt) {
int reps = cnt;
for (int p = ply - 1; p >= 0; p--) {
History h = (History) history.get(p);
if ((h.move & (Move.CAPTURE | Move.CASTLE | Move.PROMOTION)) != 0
|| h.pawnHash != this.pawnHash) {
return false;
}
if (h.posHash == posHash) {
reps--;
if (reps == 0) {
return true;
}
}
}
return false;
}
/**
* Test if the current position is a checkmate.
*
* @return <code>true</code> if the current position is a checkmate,
* <code>false</code> otherwise.
*/
public boolean isCheckMate() {
if (isInCheck()) {
IntVector tmpMoves = new IntVector();
generateLegalMoves(tmpMoves);
return tmpMoves.size() == 0;
} else {
return false;
}
}
/**
* Check if a move is legal in the current position.
*
* @param move the move to check.
* @return <code>true</code> if <code>move</code> is a legal move,
* <code>false</code> otherwise.
*/
public boolean isLegalMove(final int move) {
IntVector tmpMoves = new IntVector();
generateLegalMoves(tmpMoves);
return tmpMoves.contains(move);
}
/**
* Test for a direct check.
*
* @param epm the ever possible moves bitboard
* @param to the to square
* @param oppKing the opponents king position
* @return <code>true</code> if direct check
*/
private boolean testDirectCheck(final long[] epm, final int to,
final int oppKing) {
if ((epm[oppKing] & BitBoard.SET_MASK[to]) != 0L) {
return (Geometry.INTER_PATH[to][oppKing]
& (mask[0][0] | mask[1][0])) == 0L;
}
return false;
}
/**
* Check if a move gives check.
*
* @param move the move
* @return a boolean indicating if the move is a checking move
*/
boolean isCheckingMove(final int move) {
int from = Move.getFrom(move);
int to = Move.getTo(move);
int oppKing = kingPos[wtm ? 1 : 0];
switch (board[from]) {
case PAWN:
if ((Geometry.BLACK_PAWN_EPM[oppKing] & BitBoard.SET_MASK[to])
!= 0L) {
return true;
}
break;
case -PAWN:
if ((Geometry.WHITE_PAWN_EPM[oppKing] & BitBoard.SET_MASK[to])
!= 0L) {
return true;
}
break;
case KNIGHT:
case -KNIGHT:
if ((Geometry.KNIGHT_EPM[oppKing] & BitBoard.SET_MASK[to])
!= 0L) {
return true;
}
break;
case BISHOP:
case -BISHOP:
if (testDirectCheck(Geometry.BISHOP_EPM, to, oppKing)) {
return true;
}
break;
case ROOK:
case -ROOK:
if (testDirectCheck(Geometry.ROOK_EPM, to, oppKing)) {
return true;
}
break;
case QUEEN:
case -QUEEN:
if (testDirectCheck(Geometry.QUEEN_EPM, to, oppKing)) {
return true;
}
break;
}
if ((Geometry.ROOK_EPM[oppKing] & BitBoard.SET_MASK[from]) != 0L) {
long rookOrQueen = Geometry.RAY[oppKing][from]
& (getMask(wtm, ROOK) | getMask(wtm, QUEEN));
while (rookOrQueen != 0L) {
int idx = BitBoard.findFirstOne(rookOrQueen);
long tmp = (mask[0][0] | mask[1][0])
& Geometry.INTER_PATH[idx][oppKing];
if (BitBoard.countBits(tmp) == 1) {
return true;
}
rookOrQueen &= BitBoard.CLEAR_MASK[idx];
}
}
if ((Geometry.BISHOP_EPM[oppKing] & BitBoard.SET_MASK[from]) != 0L) {
long bishopOrQueen = Geometry.RAY[oppKing][from]
& (getMask(wtm, BISHOP) | getMask(wtm, QUEEN));
while (bishopOrQueen != 0L) {
int idx = BitBoard.findFirstOne(bishopOrQueen);
long tmp = (mask[0][0] | mask[1][0])
& Geometry.INTER_PATH[idx][oppKing];
if (BitBoard.countBits(tmp) == 1) {
return true;
}
bishopOrQueen &= BitBoard.CLEAR_MASK[idx];
}
}
return false;
}
/** Material signature mask for major pieces and pawns. */
private static final short MAJOR_PIECES_OR_PAWNS = 0x19;
/**
* Test if there is insufficient material present to checkmate.
*
* @return <code>true</code> if there is insufficient material to checkmate
*/
public boolean isInsufficientMaterial() {
if ((materialSignature[0] & MAJOR_PIECES_OR_PAWNS) != 0
|| (materialSignature[1] & MAJOR_PIECES_OR_PAWNS) != 0) {
return false;
}
long minors = mask[0][BISHOP] | mask[1][BISHOP]
| mask[0][KNIGHT] | mask[1][KNIGHT];
return BitBoard.countBits(minors) < 2;
}
/**
* Check if the current position is a draw according to the fifty
* move rule.
*
* @return <code>true</code> if the current position is drawn according to
* the fify move rule.
*/
public boolean isFiftyMoveRuleDraw() {
int bound = ply - 100;
if (bound < 0) {
return false;
}
for (int p = ply - 1; p >= bound; p--) {
History h = (History) history.get(p);
if ((h.move & (Move.CAPTURE | Move.CASTLE | Move.PROMOTION)) != 0
|| h.pawnHash != this.pawnHash) {
return false;
}
}
return true;
}
/**
* Set the evaluator for this board.
*
* @param theEvaluator the Evaluator.
*/
public void setEvaluator(final Evaluator theEvaluator) {
this.evaluator = theEvaluator;
evaluator.init();
}
/**
* Get the evaluator for this board.
*
* @return the Evaluator.
*/
public Evaluator getEvaluator() {
return evaluator;
}
/**
* Get the last move made.
*
* @return the last move made.
*/
public int getLastMove() {
History hist = (History) history.get(ply - 1);
return hist.move;
}
/**
* Test wether the last move can be undone.
*
* @return a boolean indicating if the last move can be undone.
*/
public boolean canUndo() {
return ply > 0;
}
/**
* Get the piece captured in the last move.
*
* @return the piece captured in the last move
*/
public int getLastCaptured() {
History hist = (History) history.get(ply - 1);
return hist.capturedPiece;
}
/**
* Construct a board with the starting position.
*/
public ChessBoard() {
this(Position.INITIAL_POSITION);
}
/** Parses EPD positions. */
private static final EpdParser EPD_PARSER = new EpdParser();
/**
* Construct a board from an EPD description.
*
* @param epd the position in EPD format.
* @exception IllegalEpdException if the EPD is invalid.
*/
public ChessBoard(final String epd) throws IllegalEpdException {
this(EPD_PARSER.parse(epd));
}
/**
* Construct a board by creating a deep copy of an existing board.
*
* @param theBoard the board to copy
*/
public ChessBoard(final ChessBoard theBoard) {
for (int i = 0; i < BitBoard.SIZE; i++) {
this.atkTo[i] = theBoard.atkTo[i];
this.atkFr[i] = theBoard.atkFr[i];
this.board[i] = theBoard.board[i];
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j <= KING; j++) {
this.mask[i][j] = theBoard.mask[i][j];
}
this.kingPos[i] = theBoard.kingPos[i];
}
this.slidingPieces = theBoard.slidingPieces;
this.enPassant = theBoard.enPassant;
this.castle = theBoard.castle;
this.wtm = theBoard.wtm;
this.ply = theBoard.ply;
this.posHash = theBoard.posHash;
this.pawnHash = theBoard.pawnHash;
this.materialSignature[0] = theBoard.materialSignature[0];
this.materialSignature[1] = theBoard.materialSignature[1];
this.evaluator = theBoard.evaluator;
Iterator iter = theBoard.history.iterator();
while (iter.hasNext()) {
this.history.add(new History((History) iter.next()));
}
}
/**
* Set the current position.
*
* @param pos the position.
*/
public void setPosition(final Position pos) {
System.arraycopy(pos.getBoard(), 0, board, 0, board.length);
wtm = pos.isWtm();
castle = 0;
if (pos.canWhiteCastleKingSide()) {
castle |= WHITE_CASTLE_KINGSIDE;
}
if (pos.canBlackCastleKingSide()) {
castle |= BLACK_CASTLE_KINGSIDE;
}
if (pos.canWhiteCastleQueenSide()) {
castle |= WHITE_CASTLE_QUEENSIDE;
}
if (pos.canBlackCastleQueenSide()) {
castle |= BLACK_CASTLE_QUEENSIDE;
}
enPassant = pos.getEnPassantSquare();
recalcAttacks();
}
/**
* Create a ChessBoard with a given position.
*
* @param pos the position.
*/
public ChessBoard(final Position pos) {
setPosition(pos);
}
/**
* Given a wtm flag, return the appropriate index.
*
* @param theWtm the flag
* @return 0 if <code>theWtm</code> is <code>true</code>, 1 otherwise
*/
private static int side(final boolean theWtm) {
if (theWtm) {
return 0;
} else {
return 1;
}
}
}