/// KBP vs KB. There are two rules: if the defending king is somewhere along the
        /// path of the pawn, and the square of the king is not of the same color as the
        /// stronger side's bishop, it's a draw. If the two bishops have opposite color,
        /// it's almost always a draw.
        public ScaleFactor KBPKB(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 1));
            Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0));

            Square pawnSq           = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square strongerBishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0];
            Square weakerBishopSq   = pos.list(weakSide, PieceTypeS.BISHOP)[0];
            Square weakerKingSq     = pos.king_square(weakSide);

            // Case 1: Defending king blocks the pawn, and cannot be driven away
            if (Types.file_of(weakerKingSq) == Types.file_of(pawnSq) &&
                Types.relative_rank_square(strongSide, pawnSq) < Types.relative_rank_square(strongSide, weakerKingSq) &&
                (Types.opposite_colors(weakerKingSq, strongerBishopSq) ||
                 Types.relative_rank_square(strongSide, weakerKingSq) <= RankS.RANK_6))
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            // Case 2: Opposite colored bishops
            if (Types.opposite_colors(strongerBishopSq, weakerBishopSq))
            {
                // We assume that the position is drawn in the following three situations:
                //
                //   a. The pawn is on rank 5 or further back.
                //   b. The defending king is somewhere in the pawn's path.
                //   c. The defending bishop attacks some square along the pawn's path,
                //      and is at least three squares away from the pawn.
                //
                // These rules are probably not perfect, but in practice they work
                // reasonably well.

                if (Types.relative_rank_square(strongSide, pawnSq) <= RankS.RANK_5)
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
                else
                {
                    Bitboard path = BitBoard.forward_bb(strongSide, pawnSq);

                    if ((path & pos.pieces_color_piecetype(weakSide, PieceTypeS.KING)) != 0)
                    {
                        return(ScaleFactorS.SCALE_FACTOR_DRAW);
                    }

                    if (((pos.attacks_from_square_piecetype(weakerBishopSq, PieceTypeS.BISHOP) & path) != 0) &&
                        BitBoard.square_distance(weakerBishopSq, pawnSq) >= 3)
                    {
                        return(ScaleFactorS.SCALE_FACTOR_DRAW);
                    }
                }
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
        /// KNP vs KB. If knight can block bishop from taking pawn, it's a win.
        /// Otherwise the position is drawn.
        public ScaleFactor KNPKB(Position pos)
        {
            Square pawnSq       = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square bishopSq     = pos.list(weakSide, PieceTypeS.BISHOP)[0];
            Square weakerKingSq = pos.king_square(weakSide);

            // King needs to get close to promoting pawn to prevent knight from blocking.
            // Rules for this are very tricky, so just approximate.
            if ((BitBoard.forward_bb(strongSide, pawnSq) & pos.attacks_from_square_piecetype(bishopSq, PieceTypeS.BISHOP)) != 0)
            {
                return(BitBoard.square_distance(weakerKingSq, pawnSq));
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
        public static Score evaluate(Position pos, Pawns.Entry e, Color Us)
        {
            Color  Them  = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);
            Square Up    = (Us == ColorS.WHITE ? SquareS.DELTA_N : SquareS.DELTA_S);
            Square Right = (Us == ColorS.WHITE ? SquareS.DELTA_NE : SquareS.DELTA_SW);
            Square Left  = (Us == ColorS.WHITE ? SquareS.DELTA_NW : SquareS.DELTA_SE);

            Bitboard b, p, doubled;
            Square   s;
            File     f;
            Rank     r;
            bool     passed, isolated, opposed, connected, backward, candidate, unsupported;
            Score    value = ScoreS.SCORE_ZERO;

            Square[] pl    = pos.list(Us, PieceTypeS.PAWN);
            int      plPos = 0;

            Bitboard ourPawns   = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN);
            Bitboard theirPawns = pos.pieces_color_piecetype(Them, PieceTypeS.PAWN);

            e.passedPawns[Us]   = e.candidatePawns[Us] = 0;
            e.kingSquares[Us]   = SquareS.SQ_NONE;
            e.semiopenFiles[Us] = 0xFF;
            e.pawnAttacks[Us]   = BitBoard.shift_bb(ourPawns, Right) | BitBoard.shift_bb(ourPawns, Left);
            e.pawnsOnSquares[Us][ColorS.BLACK] = Bitcount.popcount_Max15(ourPawns & BitBoard.DarkSquares);
            e.pawnsOnSquares[Us][ColorS.WHITE] = pos.count(Us, PieceTypeS.PAWN) - e.pawnsOnSquares[Us][ColorS.BLACK];

            // Loop through all pawns of the current color and score each pawn
            while ((s = pl[plPos++]) != SquareS.SQ_NONE)
            {
                Debug.Assert(pos.piece_on(s) == Types.make_piece(Us, PieceTypeS.PAWN));

                f = Types.file_of(s);


                // This file cannot be semi-open
                e.semiopenFiles[Us] &= ~(1 << f);

                // Previous rank
                p = BitBoard.rank_bb_square(s - Types.pawn_push(Us));

                // Our rank plus previous one
                b = BitBoard.rank_bb_square(s) | p;

                // Flag the pawn as passed, isolated, doubled,
                // unsupported or connected (but not the backward one).
                connected   = (ourPawns & BitBoard.adjacent_files_bb(f) & b) != 0;
                unsupported = (0 == (ourPawns & BitBoard.adjacent_files_bb(f) & p));
                isolated    = (0 == (ourPawns & BitBoard.adjacent_files_bb(f)));
                doubled     = ourPawns & BitBoard.forward_bb(Us, s);
                opposed     = (theirPawns & BitBoard.forward_bb(Us, s)) != 0;
                passed      = (0 == (theirPawns & BitBoard.passed_pawn_mask(Us, s)));

                // Test for backward pawn.
                // If the pawn is passed, isolated, or connected it cannot be
                // backward. If there are friendly pawns behind on adjacent files
                // or if it can capture an enemy pawn it cannot be backward either.
                if ((passed | isolated | connected) ||
                    (ourPawns & BitBoard.pawn_attack_span(Them, s)) != 0 ||
                    (pos.attacks_from_pawn(s, Us) & theirPawns) != 0)
                {
                    backward = false;
                }
                else
                {
                    // We now know that there are no friendly pawns beside or behind this
                    // pawn on adjacent files. We now check whether the pawn is
                    // backward by looking in the forward direction on the adjacent
                    // files, and picking the closest pawn there.
                    b = BitBoard.pawn_attack_span(Us, s) & (ourPawns | theirPawns);
                    b = BitBoard.pawn_attack_span(Us, s) & BitBoard.rank_bb_square(BitBoard.backmost_sq(Us, b));

                    // If we have an enemy pawn in the same or next rank, the pawn is
                    // backward because it cannot advance without being captured.
                    backward = ((b | BitBoard.shift_bb(b, Up)) & theirPawns) != 0;
                }

                Debug.Assert(opposed | passed | (BitBoard.pawn_attack_span(Us, s) & theirPawns) != 0);

                // A not-passed pawn is a candidate to become passed, if it is free to
                // advance and if the number of friendly pawns beside or behind this
                // pawn on adjacent files is higher than or equal to the number of
                // enemy pawns in the forward direction on the adjacent files.
                candidate = !(opposed | passed | backward | isolated) &&
                            (b = BitBoard.pawn_attack_span(Them, s + Types.pawn_push(Us)) & ourPawns) != 0 &&
                            Bitcount.popcount_Max15(b) >= Bitcount.popcount_Max15(BitBoard.pawn_attack_span(Us, s) & theirPawns);

                // Passed pawns will be properly scored in evaluation because we need
                // full attack info to evaluate passed pawns. Only the frontmost passed
                // pawn on each file is considered a true passed pawn.
                if (passed && 0 == doubled)
                {
                    e.passedPawns[Us] |= BitBoard.SquareBB[s];
                }

                // Score this pawn
                if (isolated)
                {
                    value -= Isolated[opposed ? 1 : 0][f];
                }

                if (unsupported && !isolated)
                {
                    value -= UnsupportedPawnPenalty;
                }

                if (doubled != 0)
                {
                    value -= Types.divScore(Doubled[f], BitBoard.rank_distance(s, BitBoard.lsb(doubled)));
                }

                if (backward)
                {
                    value -= Backward[opposed ? 1 : 0][f];
                }

                if (connected)
                {
                    value += Connected[f][Types.relative_rank_square(Us, s)];
                }

                if (candidate)
                {
                    value += CandidatePassed[Types.relative_rank_square(Us, s)];

                    if (0 == doubled)
                    {
                        e.candidatePawns[Us] |= BitBoard.SquareBB[s];
                    }
                }
            }

            // In endgame it's better to have pawns on both wings. So give a bonus according
            // to file distance between left and right outermost pawns.
            if (pos.count(Us, PieceTypeS.PAWN) > 1)
            {
                b      = (Bitboard)(e.semiopenFiles[Us] ^ 0xFF);
                value += PawnsFileSpan * (BitBoard.msb(b) - BitBoard.lsb(b));
            }

            return(value);
        }
Beispiel #4
0
        // evaluate_passed_pawns() evaluates the passed pawns of the given color
        public static Score evaluate_passed_pawns(Position pos, EvalInfo ei, Color Us, bool Trace)
        {
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            Bitboard b, squaresToQueen, defendedSquares, unsafeSquares;
            Score    score = ScoreS.SCORE_ZERO;

            b = ei.pi.passed_pawns(Us);

            while (b != 0)
            {
                Square s = BitBoard.pop_lsb(ref b);

                Debug.Assert(pos.pawn_passed(Us, s));

                int r  = (int)(Types.relative_rank_square(Us, s) - RankS.RANK_2);
                int rr = r * (r - 1);

                // Base bonus based on rank
                Value mbonus = (17 * rr), ebonus = (7 * (rr + r + 1));

                if (rr != 0)
                {
                    Square blockSq = s + Types.pawn_push(Us);

                    // Adjust bonus based on kings proximity
                    ebonus += (BitBoard.square_distance(pos.king_square(Them), blockSq) * 5 * rr)
                              - (BitBoard.square_distance(pos.king_square(Us), blockSq) * 2 * rr);

                    // If blockSq is not the queening square then consider also a second push
                    if (Types.relative_rank_square(Us, blockSq) != RankS.RANK_8)
                    {
                        ebonus -= (BitBoard.square_distance(pos.king_square(Us), blockSq + Types.pawn_push(Us)) * rr);
                    }

                    // If the pawn is free to advance, increase bonus
                    if (pos.empty(blockSq))
                    {
                        squaresToQueen = BitBoard.forward_bb(Us, s);

                        // If there is an enemy rook or queen attacking the pawn from behind,
                        // add all X-ray attacks by the rook or queen. Otherwise consider only
                        // the squares in the pawn's path attacked or occupied by the enemy.
                        if ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 &&
                            (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0)
                        {
                            unsafeSquares = squaresToQueen;
                        }
                        else
                        {
                            unsafeSquares = squaresToQueen & (ei.attackedBy[Them][PieceTypeS.ALL_PIECES] | pos.pieces_color(Them));
                        }

                        if ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 &&
                            (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0)
                        {
                            defendedSquares = squaresToQueen;
                        }
                        else
                        {
                            defendedSquares = squaresToQueen & ei.attackedBy[Us][PieceTypeS.ALL_PIECES];
                        }

                        // If there aren't any enemy attacks, assign a big bonus. Otherwise
                        // assign a smaller bonus if the block square isn't attacked.
                        int k = 0 == unsafeSquares? 15 : 0 == (unsafeSquares & BitBoard.SquareBB[blockSq]) ? 9 : 0;


                        // If the path to queen is fully defended, assign a big bonus.
                        // Otherwise assign a smaller bonus if the block square is defended.
                        if (defendedSquares == squaresToQueen)
                        {
                            k += 6;
                        }

                        else if ((defendedSquares & BitBoard.SquareBB[blockSq]) != 0)
                        {
                            k += 4;
                        }

                        mbonus += (k * rr); ebonus += (k * rr);
                    }
                } // rr != 0

                if (pos.count(Us, PieceTypeS.PAWN) < pos.count(Them, PieceTypeS.PAWN))
                {
                    ebonus += ebonus / 4;
                }

                score += Types.make_score(mbonus, ebonus);
            }

            if (Trace)
            {
                Tracing.terms[Us][TermsS.PASSED] = apply_weight(score, Weights[EvalWeightS.PassedPawns]);
            }

            // Add the scores to the middle game and endgame eval
            return(Eval.apply_weight(score, Weights[EvalWeightS.PassedPawns]));
        }