*schrott*

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;
        }
    }
}
 
Zurück
Oben