Esempio n. 1
0
        /// <summary>
        /// Returns the estimated material exchange value of the given move on the
        /// given position as determined by static analysis.
        /// </summary>
        /// <param name="position">The position the move is to be played on.</param>
        /// <param name="move">The move to evaluate.</param>
        /// <returns>The estimated material exchange value of the move.</returns>
        private static Int32 EvaluateStaticExchange(Position position, Int32 move)
        {
            Int32 from    = Move.From(move);
            Int32 to      = Move.To(move);
            Int32 piece   = Move.Piece(move);
            Int32 capture = Move.Capture(move);

            position.Bitboard[piece]  ^= 1UL << from;
            position.OccupiedBitboard ^= 1UL << from;
            position.Square[to]        = piece;

            Int32 value = 0;

            if (Move.IsPromotion(move))
            {
                Int32 promotion = Move.Special(move);
                position.Square[to] = promotion;
                value += PieceValue[promotion] - PieceValue[Piece.Pawn];
            }
            value += PieceValue[capture] - EvaluateStaticExchange(position, 1 - position.SideToMove, to);

            position.Bitboard[piece]  ^= 1UL << from;
            position.OccupiedBitboard ^= 1UL << from;
            position.Square[to]        = capture;

            return(value);
        }
Esempio n. 2
0
        /// <summary>
        /// Returns whether the given move puts the opponent in check.
        /// </summary>
        /// <param name="move">The move to test for check.</param>
        /// <returns>Whether the given move puts the opponent in check.</returns>
        public Boolean CausesCheck(Int32 move)
        {
            UInt64 fromBitboard         = 1UL << Move.From(move);
            UInt64 toBitboard           = 1UL << Move.To(move);
            Int32  piece                = Move.Piece(move);
            Int32  special              = Move.Special(move);
            UInt64 occupiedBitboardCopy = OccupiedBitboard;

            Boolean value = false;

            switch (special & Piece.Mask)
            {
            // Consider normal move.
            case Piece.Empty:
                Bitboard[piece]  ^= fromBitboard | toBitboard;
                OccupiedBitboard ^= fromBitboard;
                OccupiedBitboard |= toBitboard;
                value             = InCheck(1 - SideToMove);
                Bitboard[piece]  ^= fromBitboard | toBitboard;
                OccupiedBitboard  = occupiedBitboardCopy;
                break;

            // Consider castling.
            case Piece.King:
                UInt64 rookToBitboard = 1UL << ((toBitboard < fromBitboard ? 3 : 5) + Rank(Move.To(move)) * 8);
                Bitboard[SideToMove | Piece.Rook] ^= rookToBitboard;
                OccupiedBitboard ^= fromBitboard;
                value             = InCheck(1 - SideToMove);
                Bitboard[SideToMove | Piece.Rook] ^= rookToBitboard;
                OccupiedBitboard = occupiedBitboardCopy;
                break;

            // Consider en passant.
            case Piece.Pawn:
                UInt64 enPassantPawnBitboard = Move.Pawn(EnPassantSquare, 1 - SideToMove);
                Bitboard[piece]  ^= fromBitboard | toBitboard;
                OccupiedBitboard ^= fromBitboard | toBitboard | enPassantPawnBitboard;
                value             = InCheck(1 - SideToMove);
                Bitboard[piece]  ^= fromBitboard | toBitboard;
                OccupiedBitboard  = occupiedBitboardCopy;
                break;

            // Consider pawn promotion.
            default:
                Bitboard[SideToMove | Piece.Pawn] ^= fromBitboard;
                Bitboard[special] ^= toBitboard;
                OccupiedBitboard  ^= fromBitboard;
                OccupiedBitboard  |= toBitboard;
                value              = InCheck(1 - SideToMove);
                Bitboard[SideToMove | Piece.Pawn] ^= fromBitboard;
                Bitboard[special] ^= toBitboard;
                OccupiedBitboard   = occupiedBitboardCopy;
                break;
            }
            return(value);
        }
Esempio n. 3
0
        /// <summary>
        /// Returns a value for the given move that indicates its immediate threat.
        /// Non-capture moves have a default value of zero, while captures have a
        /// value that is the ratio of the captured piece to the moving piece. Pawns
        /// promoting to queen are given an additional increase in value.
        /// </summary>
        /// <param name="move">The move to consider.</param>
        /// <returns>A value for the given move that is useful for move ordering.</returns>
        private Single MoveOrderingValue(Int32 move)
        {
            Single value = PieceValue[Move.Capture(move)] / (Single)PieceValue[Move.Piece(move)];

            if (Move.IsQueenPromotion(move))
            {
                value += QueenPromotionMoveValue;
            }
            return(value);
        }
Esempio n. 4
0
        /// <summary>
        /// Unmakes the given move from the position.
        /// </summary>
        /// <param name="move">The move to unmake.</param>
        public void Unmake(Int32 move)
        {
            Int32 from    = Move.From(move);
            Int32 to      = Move.To(move);
            Int32 piece   = Move.Piece(move);
            Int32 capture = Move.Capture(move);
            Int32 special = Move.Special(move);

            // Rewind core board state.
            SideToMove            = 1 - SideToMove;
            Square[from]          = piece;
            Square[to]            = capture;
            Bitboard[piece]      ^= (1UL << from) | (1UL << to);
            Bitboard[SideToMove] ^= (1UL << from) | (1UL << to);
            OccupiedBitboard     ^= (1UL << from) | (1UL << to);

            // Rewind metainformation.
            ZobristKey = ZobristKeyHistory[HalfMoves - 1];
            EnPassantHistory[HalfMoves] = InvalidSquare;
            EnPassantSquare             = EnPassantHistory[HalfMoves - 1];
            FiftyMovesClock             = FiftyMovesHistory[HalfMoves - 1];
            HalfMoves--;

            // Rewind capture if applicable.
            switch (capture & Piece.Mask)
            {
            case Piece.Empty:
                break;

            case Piece.Rook:
                if ((SideToMove == Colour.White && to == 0) || (SideToMove == Colour.Black && to == 56))
                {
                    CastleQueenside[1 - SideToMove]++;
                }
                else if ((SideToMove == Colour.White && to == 7) || (SideToMove == Colour.Black && to == 63))
                {
                    CastleKingside[1 - SideToMove]++;
                }
                goto default;

            default:
                Bitboard[capture]        ^= 1UL << to;
                Bitboard[1 - SideToMove] ^= 1UL << to;
                OccupiedBitboard         |= 1UL << to;
                Material[1 - SideToMove] += Engine.PieceValue[capture];
                break;
            }

            switch (special & Piece.Mask)
            {
            // Rewind regular move.
            case Piece.Empty:
                switch (piece & Piece.Mask)
                {
                // For rook move, restore castling on one side if applicable.
                case Piece.Rook:
                    if ((SideToMove == Colour.White && from == 56) || (SideToMove == Colour.Black && from == 0))
                    {
                        CastleQueenside[SideToMove]++;
                    }
                    else if ((SideToMove == Colour.White && from == 63) || (SideToMove == Colour.Black && from == 7))
                    {
                        CastleKingside[SideToMove]++;
                    }
                    break;

                // For king move, restore castling on both sides if applicable.
                case Piece.King:
                    CastleQueenside[SideToMove]++;
                    CastleKingside[SideToMove]++;
                    break;
                }
                break;

            // Rewind castling.
            case Piece.King:
                CastleQueenside[SideToMove]++;
                CastleKingside[SideToMove]++;
                Int32 rookFrom;
                Int32 rookTo;
                if (to < from)
                {
                    rookFrom = Rank(to) * 8;
                    rookTo   = 3 + Rank(to) * 8;
                }
                else
                {
                    rookFrom = 7 + Rank(to) * 8;
                    rookTo   = 5 + Rank(to) * 8;
                }
                Bitboard[SideToMove | Piece.Rook] ^= (1UL << rookFrom) | (1UL << rookTo);
                Bitboard[SideToMove] ^= (1UL << rookFrom) | (1UL << rookTo);
                OccupiedBitboard     ^= (1UL << rookFrom) | (1UL << rookTo);
                Square[rookFrom]      = SideToMove | Piece.Rook;
                Square[rookTo]        = Piece.Empty;
                break;

            // Rewind en passant.
            case Piece.Pawn:
                Square[File(to) + Rank(from) * 8] = special;
                Bitboard[special]        ^= 1UL << (File(to) + Rank(from) * 8);
                Bitboard[1 - SideToMove] ^= 1UL << (File(to) + Rank(from) * 8);
                OccupiedBitboard         ^= 1UL << (File(to) + Rank(from) * 8);
                Material[1 - SideToMove] += Engine.PieceValue[special];
                break;

            // Rewind pawn promotion.
            default:
                Bitboard[piece]      ^= 1UL << to;
                Bitboard[special]    ^= 1UL << to;
                Material[SideToMove] -= Engine.PieceValue[special] - Engine.PieceValue[piece];
                break;
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Makes the given move on the position.
        /// </summary>
        /// <param name="move">The move to make.</param>
        public void Make(Int32 move)
        {
            Int32 from    = Move.From(move);
            Int32 to      = Move.To(move);
            Int32 piece   = Move.Piece(move);
            Int32 capture = Move.Capture(move);
            Int32 special = Move.Special(move);

            // Update core board state.
            Square[to]            = piece;
            Square[from]          = Piece.Empty;
            Bitboard[piece]      ^= (1UL << from) | (1UL << to);
            Bitboard[SideToMove] ^= (1UL << from) | (1UL << to);
            OccupiedBitboard     ^= (1UL << from) | (1UL << to);

            // Update metainformation.
            ZobristKey ^= Zobrist.PiecePosition[piece][from] ^ Zobrist.PiecePosition[piece][to];
            ZobristKey ^= Zobrist.Colour;
            if (EnPassantSquare != InvalidSquare)
            {
                ZobristKey     ^= Zobrist.EnPassant[EnPassantSquare];
                EnPassantSquare = InvalidSquare;
            }
            FiftyMovesClock++;
            HalfMoves++;

            // Handle capture if applicable.
            switch (capture & Piece.Mask)
            {
            case Piece.Empty:
                break;

            case Piece.Rook:
                if ((SideToMove == Colour.White && to == 0) || (SideToMove == Colour.Black && to == 56))
                {
                    if (CastleQueenside[1 - SideToMove]-- > 0)
                    {
                        ZobristKey ^= Zobrist.CastleQueenside[1 - SideToMove];
                    }
                }
                else if ((SideToMove == Colour.White && to == 7) || (SideToMove == Colour.Black && to == 63))
                {
                    if (CastleKingside[1 - SideToMove]-- > 0)
                    {
                        ZobristKey ^= Zobrist.CastleKingside[1 - SideToMove];
                    }
                }
                goto default;

            default:
                Bitboard[capture]        ^= 1UL << to;
                Bitboard[1 - SideToMove] ^= 1UL << to;
                OccupiedBitboard         |= 1UL << to;
                ZobristKey ^= Zobrist.PiecePosition[capture][to];
                Material[1 - SideToMove] -= Engine.PieceValue[capture];
                FiftyMovesClock           = 0;
                break;
            }

            switch (special & Piece.Mask)
            {
            // Handle regular move (not en passant, castling, or pawn promotion).
            case Piece.Empty:
                switch (piece & Piece.Mask)
                {
                // For pawn move, update fifty moves clock and en passant state.
                case Piece.Pawn:
                    FiftyMovesClock = 0;
                    if ((from - to) * (from - to) == 256)
                    {
                        ZobristKey ^= Zobrist.EnPassant[from];
                        EnPassantHistory[HalfMoves] = EnPassantSquare = (from + to) / 2;
                    }
                    break;

                // For rook move, disable castling on one side.
                case Piece.Rook:
                    if ((SideToMove == Colour.White && from == 56) || (SideToMove == Colour.Black && from == 0))
                    {
                        if (CastleQueenside[SideToMove]-- > 0)
                        {
                            ZobristKey ^= Zobrist.CastleQueenside[SideToMove];
                        }
                    }
                    else if ((SideToMove == Colour.White && from == 63) || (SideToMove == Colour.Black && from == 7))
                    {
                        if (CastleKingside[SideToMove]-- > 0)
                        {
                            ZobristKey ^= Zobrist.CastleKingside[SideToMove];
                        }
                    }
                    break;

                // For king move, disable castling on both sides.
                case Piece.King:
                    if (CastleQueenside[SideToMove]-- > 0)
                    {
                        ZobristKey ^= Zobrist.CastleQueenside[SideToMove];
                    }
                    if (CastleKingside[SideToMove]-- > 0)
                    {
                        ZobristKey ^= Zobrist.CastleKingside[SideToMove];
                    }
                    break;
                }
                break;

            // Handle castling.
            case Piece.King:
                if (CastleQueenside[SideToMove]-- > 0)
                {
                    ZobristKey ^= Zobrist.CastleQueenside[SideToMove];
                }
                if (CastleKingside[SideToMove]-- > 0)
                {
                    ZobristKey ^= Zobrist.CastleKingside[SideToMove];
                }
                Int32 rookFrom;
                Int32 rookTo;
                if (to < from)
                {
                    rookFrom = Rank(to) * 8;
                    rookTo   = 3 + Rank(to) * 8;
                }
                else
                {
                    rookFrom = 7 + Rank(to) * 8;
                    rookTo   = 5 + Rank(to) * 8;
                }
                Bitboard[SideToMove | Piece.Rook] ^= (1UL << rookFrom) | (1UL << rookTo);
                Bitboard[SideToMove] ^= (1UL << rookFrom) | (1UL << rookTo);
                OccupiedBitboard     ^= (1UL << rookFrom) | (1UL << rookTo);
                ZobristKey           ^= Zobrist.PiecePosition[SideToMove | Piece.Rook][rookFrom];
                ZobristKey           ^= Zobrist.PiecePosition[SideToMove | Piece.Rook][rookTo];
                Square[rookFrom]      = Piece.Empty;
                Square[rookTo]        = SideToMove | Piece.Rook;
                break;

            // Handle en passant.
            case Piece.Pawn:
                Square[File(to) + Rank(from) * 8] = Piece.Empty;
                Bitboard[special]        ^= 1UL << (File(to) + Rank(from) * 8);
                Bitboard[1 - SideToMove] ^= 1UL << (File(to) + Rank(from) * 8);
                OccupiedBitboard         ^= 1UL << (File(to) + Rank(from) * 8);
                ZobristKey ^= Zobrist.PiecePosition[special][File(to) + Rank(from) * 8];
                Material[1 - SideToMove] -= Engine.PieceValue[special];
                break;

            // Handle pawn promotion.
            default:
                Bitboard[piece]      ^= 1UL << to;
                Bitboard[special]    ^= 1UL << to;
                ZobristKey           ^= Zobrist.PiecePosition[piece][to];
                ZobristKey           ^= Zobrist.PiecePosition[special][to];
                Material[SideToMove] += Engine.PieceValue[special] - Engine.PieceValue[piece];
                Square[to]            = special;
                break;
            }

            SideToMove = 1 - SideToMove;
            FiftyMovesHistory[HalfMoves] = FiftyMovesClock;
            ZobristKeyHistory[HalfMoves] = ZobristKey;
        }
Esempio n. 6
0
        /// <summary>
        /// Returns the dynamic value of the position as determined by a recursive
        /// search that terminates upon reaching a quiescent position.
        /// </summary>
        /// <param name="position">The position to search on.</param>
        /// <param name="ply">The number of plies from the root position.</param>
        /// <param name="alpha">The lower bound on the value of the best move.</param>
        /// <param name="beta">The upper bound on the value of the best move.</param>
        /// <returns>The value of the termination position given optimal play.</returns>
        private Int32 Quiescence(Position position, Int32 ply, Int32 alpha, Int32 beta)
        {
            _totalNodes++;
            _quiescenceNodes++;

            // Evaluate the position statically. Check for upper bound cutoff and lower
            // bound improvement.
            Int32 value = Evaluate(position);

            if (value >= beta)
            {
                return(value);
            }
            if (value > alpha)
            {
                alpha = value;
            }

            // Initialize variables and generate the pseudo-legal moves to be
            // considered. Perform basic move ordering and sort the moves.
            Int32 colour = position.SideToMove;

            Int32[] moves      = _generatedMoves[ply];
            Int32   movesCount = position.PseudoQuiescenceMoves(moves);

            for (Int32 i = 0; i < movesCount; i++)
            {
                _moveValues[i] = MoveOrderingValue(moves[i]);
            }
            Sort(moves, _moveValues, movesCount);

            // Go through the move list.
            for (Int32 i = 0; i < movesCount; i++)
            {
                _movesSearched++;
                Int32 move = moves[i];

                // Consider the move only if it doesn't immediately lose material. This
                // improves efficiency.
                if ((Move.Piece(move) & Piece.Mask) <= (Move.Capture(move) & Piece.Mask) || EvaluateStaticExchange(position, move) >= 0)
                {
                    // Make the move.
                    position.Make(move);

                    // Search the move if it is legal. This is equivalent to not leaving the
                    // king in check.
                    if (!position.InCheck(colour))
                    {
                        value = -Quiescence(position, ply + 1, -beta, -alpha);

                        // Check for upper bound cutoff and lower bound improvement.
                        if (value >= beta)
                        {
                            position.Unmake(move);
                            return(value);
                        }
                        if (value > alpha)
                        {
                            alpha = value;
                        }
                    }

                    // Unmake the move.
                    position.Unmake(move);
                }
            }
            return(alpha);
        }