예제 #1
0
            /// Entry::shelter_storm() calculates shelter and storm penalties for the file
            /// the king is on, as well as the two adjacent files.
            public Value shelter_storm(Position pos, Square ksq, Color Us)
            {
                Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

                Value    safety = Pawns.MaxSafetyBonus;
                Bitboard b = pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.in_front_bb(Us, Types.rank_of(ksq)) | BitBoard.rank_bb_square(ksq));
                Bitboard ourPawns = b & pos.pieces_color(Us);
                Bitboard theirPawns = b & pos.pieces_color(Them);
                Rank     rkUs, rkThem;
                File     kf = Math.Max(FileS.FILE_B, Math.Min(FileS.FILE_G, Types.file_of(ksq)));

                for (File f = kf - 1; f <= kf + 1; ++f)
                {
                    b    = ourPawns & BitBoard.file_bb_file(f);
                    rkUs = b != 0 ? Types.relative_rank_square(Us, BitBoard.backmost_sq(Us, b)) : RankS.RANK_1;

                    b      = theirPawns & BitBoard.file_bb_file(f);
                    rkThem = b != 0 ? Types.relative_rank_square(Us, BitBoard.frontmost_sq(Them, b)) : RankS.RANK_1;

                    if ((MiddleEdges & BitBoard.SquareBB[Types.make_square(f, rkThem)]) != 0 &&
                        Types.file_of(ksq) == f &&
                        Types.relative_rank_square(Us, ksq) == rkThem - 1)
                    {
                        safety += 200;
                    }
                    else
                    {
                        safety -= ShelterWeakness[rkUs]
                                  + StormDanger[rkUs == RankS.RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
                    }
                }

                return(safety);
            }
예제 #2
0
            /// Entry::do_king_safety() calculates a bonus for king safety. It is called only
            /// when king square changes, which is about 20% of total king_safety() calls.
            public Score do_king_safety(Position pos, Square ksq, Color Us)
            {
                kingSquares[Us]    = ksq;
                castlingRights[Us] = pos.can_castle_color(Us);
                minKPdistance[Us]  = 0;

                Bitboard pawns = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN);

                if (pawns != 0)
                {
                    while (0 == (BitBoard.DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns))
                    {
                    }
                }

                if (Types.relative_rank_square(Us, ksq) > RankS.RANK_4)
                {
                    return(Types.make_score(0, -16 * minKPdistance[Us]));
                }

                Value bonus = shelter_storm(pos, ksq, Us);

                // If we can castle use the bonus after the castle if is bigger
                if (pos.can_castle_castleright((new MakeCastlingS(Us, CastlingSideS.KING_SIDE)).right) != 0)
                {
                    bonus = Math.Max(bonus, shelter_storm(pos, Types.relative_square(Us, SquareS.SQ_G1), Us));
                }

                if (pos.can_castle_castleright((new MakeCastlingS(Us, CastlingSideS.QUEEN_SIDE)).right) != 0)
                {
                    bonus = Math.Max(bonus, shelter_storm(pos, Types.relative_square(Us, SquareS.SQ_C1), Us));
                }

                return(Types.make_score(bonus, -16 * minKPdistance[Us]));
            }
예제 #3
0
        /// 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);
        }
예제 #4
0
        // evaluate_unstoppable_pawns() scores the most advanced among the passed and
        // candidate pawns. In case opponent has no pieces but pawns, this is somewhat
        // related to the possibility that pawns are unstoppable.
        public static Score evaluate_unstoppable_pawns(Position pos, Color us, EvalInfo ei)
        {
            Bitboard b = ei.pi.passed_pawns(us) | ei.pi.candidate_pawns(us);

            if (0 == b || pos.non_pawn_material(Types.notColor(us)) != 0)
            {
                return(ScoreS.SCORE_ZERO);
            }

            return(Unstoppable * (Types.relative_rank_square(us, BitBoard.frontmost_sq(us, b))));
        }
예제 #5
0
        public ScaleFactor KRPKB(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 1));
            Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0));

            // Test for a rook pawn
            if ((pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.FileABB | BitBoard.FileHBB)) != 0)
            {
                Square ksq  = pos.king_square(weakSide);
                Square bsq  = pos.list(weakSide, PieceTypeS.BISHOP)[0];
                Square psq  = pos.list(strongSide, PieceTypeS.PAWN)[0];
                Rank   rk   = Types.relative_rank_square(strongSide, psq);
                Square push = Types.pawn_push(strongSide);

                // If the pawn is on the 5th rank and the pawn (currently) is on
                // the same color square as the bishop then there is a chance of
                // a fortress. Depending on the king position give a moderate
                // reduction or a stronger one if the defending king is near the
                // corner but not trapped there.
                if (rk == RankS.RANK_5 && !Types.opposite_colors(bsq, psq))
                {
                    int d = BitBoard.square_distance(psq + 3 * push, ksq);

                    if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push))
                    {
                        return(24);
                    }
                    else
                    {
                        return(48);
                    }
                }

                // When the pawn has moved to the 6th rank we can be fairly sure
                // it's drawn if the bishop attacks the square in front of the
                // pawn from a reasonable distance and the defending king is near
                // the corner
                if (rk == RankS.RANK_6 &&
                    BitBoard.square_distance(psq + 2 * push, ksq) <= 1 &&
                    (BitBoard.PseudoAttacks[PieceTypeS.BISHOP][bsq] & BitBoard.SquareBB[(psq + push)]) != 0 &&
                    BitBoard.file_distance(bsq, psq) >= 2)
                {
                    return(8);
                }
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
예제 #6
0
        /// KBP vs KN. There is a single rule: 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.
        public ScaleFactor KBPKN(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 1));
            Debug.Assert(verify_material(pos, weakSide, ValueS.KnightValueMg, 0));

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

            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);
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
예제 #7
0
        /// KQ vs KP. In general, this is a win for the stronger side, but there are a
        /// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
        /// with a king positioned next to it can be a draw, so in that case, we only
        /// use the distance between the kings.
        public Value KQKP(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.QueenValueMg, 0));
            Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 1));

            Square winnerKSq = pos.king_square(strongSide);
            Square loserKSq  = pos.king_square(weakSide);
            Square pawnSq    = pos.list(weakSide, PieceTypeS.PAWN)[0];

            Value result = (PushClose[BitBoard.square_distance(winnerKSq, loserKSq)]);

            if (Types.relative_rank_square(weakSide, pawnSq) != RankS.RANK_7 ||
                BitBoard.square_distance(loserKSq, pawnSq) != 1 ||
                0 == ((BitBoard.FileABB | BitBoard.FileCBB | BitBoard.FileFBB | BitBoard.FileHBB) & BitBoard.SquareBB[pawnSq]))
            {
                result += ValueS.QueenValueEg - ValueS.PawnValueEg;
            }

            return(strongSide == pos.side_to_move() ? result : -result);
        }
예제 #8
0
        /// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on
        /// the third rank defended by a pawn.
        public ScaleFactor KQKRPs(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.QueenValueMg, 0));
            Debug.Assert(pos.count(weakSide, PieceTypeS.ROOK) == 1);
            Debug.Assert(pos.count(weakSide, PieceTypeS.PAWN) >= 1);

            Square kingSq = pos.king_square(weakSide);
            Square rsq    = pos.list(weakSide, PieceTypeS.ROOK)[0];

            if (Types.relative_rank_square(weakSide, kingSq) <= RankS.RANK_2 &&
                Types.relative_rank_square(weakSide, pos.king_square(strongSide)) >= RankS.RANK_4 &&
                Types.relative_rank_square(weakSide, rsq) == RankS.RANK_3 &&
                (pos.pieces_color_piecetype(weakSide, PieceTypeS.PAWN)
                 & pos.attacks_from_square_piecetype(kingSq, PieceTypeS.KING)
                 & pos.attacks_from_pawn(rsq, strongSide)) != 0)
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
예제 #9
0
        /// KRPP vs KRP. There is just a single rule: if the stronger side has no passed
        /// pawns and the defending king is actively placed, the position is drawish.
        public ScaleFactor KRPPKRP(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 2));
            Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 1));

            Square wpsq1 = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square wpsq2 = pos.list(strongSide, PieceTypeS.PAWN)[1];
            Square bksq  = pos.king_square(weakSide);

            // Does the stronger side have a passed pawn?
            if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2))
            {
                return(ScaleFactorS.SCALE_FACTOR_NONE);
            }

            Rank r = Math.Max(Types.relative_rank_square(strongSide, wpsq1), Types.relative_rank_square(strongSide, wpsq2));

            if (BitBoard.file_distance(bksq, wpsq1) <= 1 &&
                BitBoard.file_distance(bksq, wpsq2) <= 1 &&
                Types.relative_rank_square(strongSide, bksq) > r)
            {
                switch (r)
                {
                case RankS.RANK_2: return(10);

                case RankS.RANK_3: return(10);

                case RankS.RANK_4: return(15);

                case RankS.RANK_5: return(20);

                case RankS.RANK_6: return(40);

                default: Debug.Assert(false); break;
                }
            }
            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
예제 #10
0
        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);
        }
예제 #11
0
        /// KBPP vs KB. It detects a few basic draws with opposite-colored bishops
        public ScaleFactor KBPPKB(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 2));
            Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0));

            Square wbsq = pos.list(strongSide, PieceTypeS.BISHOP)[0];
            Square bbsq = pos.list(weakSide, PieceTypeS.BISHOP)[0];

            if (!Types.opposite_colors(wbsq, bbsq))
            {
                return(ScaleFactorS.SCALE_FACTOR_NONE);
            }

            Square ksq = pos.king_square(weakSide);
            Square psq1 = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square psq2 = pos.list(strongSide, PieceTypeS.PAWN)[1];
            Rank   r1 = Types.rank_of(psq1);
            Rank   r2 = Types.rank_of(psq2);
            Square blockSq1, blockSq2;

            if (Types.relative_rank_square(strongSide, psq1) > Types.relative_rank_square(strongSide, psq2))
            {
                blockSq1 = psq1 + Types.pawn_push(strongSide);
                blockSq2 = Types.make_square(Types.file_of(psq2), Types.rank_of(psq1));
            }
            else
            {
                blockSq1 = psq2 + Types.pawn_push(strongSide);
                blockSq2 = Types.make_square(Types.file_of(psq1), Types.rank_of(psq2));
            }

            switch (BitBoard.file_distance(psq1, psq2))
            {
            case 0:
                // Both pawns are on the same file. It's an easy draw if the defender firmly
                // controls some square in the frontmost pawn's path.
                if (Types.file_of(ksq) == Types.file_of(blockSq1) &&
                    Types.relative_rank_square(strongSide, ksq) >= Types.relative_rank_square(strongSide, blockSq1) &&
                    Types.opposite_colors(ksq, wbsq))
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
                else
                {
                    return(ScaleFactorS.SCALE_FACTOR_NONE);
                }

            case 1:
                // Pawns on adjacent files. It's a draw if the defender firmly controls the
                // square in front of the frontmost pawn's path, and the square diagonally
                // behind this square on the file of the other pawn.
                if (ksq == blockSq1 &&
                    Types.opposite_colors(ksq, wbsq) &&
                    (bbsq == blockSq2 ||
                     (pos.attacks_from_square_piecetype(blockSq2, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0 ||
                     Math.Abs(r1 - r2) >= 2))
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }

                else if (ksq == blockSq2 &&
                         Types.opposite_colors(ksq, wbsq) &&
                         (bbsq == blockSq1 ||
                          (pos.attacks_from_square_piecetype(blockSq1, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0))
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
                else
                {
                    return(ScaleFactorS.SCALE_FACTOR_NONE);
                }

            default:
                // The pawns are not on the same file or adjacent files. No scaling.
                return(ScaleFactorS.SCALE_FACTOR_NONE);
            }
        }
예제 #12
0
        /// KB and one or more pawns vs K. It checks for draws with rook pawns and
        /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
        /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
        /// will be used.
        public ScaleFactor KBPsK(Position pos)
        {
            Debug.Assert(pos.non_pawn_material(strongSide) == ValueS.BishopValueMg);
            Debug.Assert(pos.count(strongSide, PieceTypeS.PAWN) >= 1);

            // No assertions about the material of weakSide, because we want draws to
            // be detected even when the weaker side has some pawns.

            Bitboard pawns    = pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN);
            File     pawnFile = Types.file_of(pos.list(strongSide, PieceTypeS.PAWN)[0]);

            // All pawns are on a single rook file ?
            if ((pawnFile == FileS.FILE_A || pawnFile == FileS.FILE_H) &&
                0 == (pawns & ~BitBoard.file_bb_file(pawnFile)))
            {
                Square bishopSq   = pos.list(strongSide, PieceTypeS.BISHOP)[0];
                Square queeningSq = Types.relative_square(strongSide, Types.make_square(pawnFile, RankS.RANK_8));
                Square kingSq     = pos.king_square(weakSide);

                if (Types.opposite_colors(queeningSq, bishopSq) &&
                    BitBoard.square_distance(queeningSq, kingSq) <= 1)
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
            }

            // If all the pawns are on the same B or G file, then it's potentially a draw
            if ((pawnFile == FileS.FILE_B || pawnFile == FileS.FILE_G) &&
                0 == (pos.pieces_piecetype(PieceTypeS.PAWN) & ~BitBoard.file_bb_file(pawnFile)) &&
                pos.non_pawn_material(weakSide) == 0 &&
                pos.count(weakSide, PieceTypeS.PAWN) >= 1)
            {
                // Get weakSide pawn that is closest to the home rank
                Square weakPawnSq = BitBoard.backmost_sq(weakSide, pos.pieces_color_piecetype(weakSide, PieceTypeS.PAWN));

                Square strongKingSq = pos.king_square(strongSide);
                Square weakKingSq   = pos.king_square(weakSide);
                Square bishopSq     = pos.list(strongSide, PieceTypeS.BISHOP)[0];

                // There's potential for a draw if our pawn is blocked on the 7th rank,
                // the bishop cannot attack it or they only have one pawn left
                if (Types.relative_rank_square(strongSide, weakPawnSq) == RankS.RANK_7 &&
                    (pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN) & BitBoard.SquareBB[(weakPawnSq + Types.pawn_push(weakSide))]) != 0 &&
                    (Types.opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide, PieceTypeS.PAWN) == 1))
                {
                    int strongKingDist = BitBoard.square_distance(weakPawnSq, strongKingSq);
                    int weakKingDist   = BitBoard.square_distance(weakPawnSq, weakKingSq);

                    // It's a draw if the weak king is on its back two ranks, within 2
                    // squares of the blocking pawn and the strong king is not
                    // closer. (I think this rule only fails in practically
                    // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
                    // and positions where qsearch will immediately correct the
                    // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w)
                    if (Types.relative_rank_square(strongSide, weakKingSq) >= RankS.RANK_7 &&
                        weakKingDist <= 2 &&
                        weakKingDist <= strongKingDist)
                    {
                        return(ScaleFactorS.SCALE_FACTOR_DRAW);
                    }
                }
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
예제 #13
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]));
        }
예제 #14
0
        // evaluate_pieces() assigns bonuses and penalties to the pieces of a given color
        public static Score evaluate_pieces(Position pos, EvalInfo ei, Score[] mobility, Bitboard[] mobilityArea, PieceType Pt, Color Us, bool Trace)
        {
            if (Us == ColorS.WHITE && Pt == PieceTypeS.KING)
            {
                return(ScoreS.SCORE_ZERO);
            }

            Bitboard b;
            Square   s;
            Score    score = ScoreS.SCORE_ZERO;

            PieceType NextPt = (Us == ColorS.WHITE ? Pt : (Pt + 1));
            Color     Them   = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

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

            ei.attackedBy[Us][Pt] = 0;

            while ((s = pl[plPos++]) != SquareS.SQ_NONE)
            {
                // Find attacked squares, including x-ray attacks for bishops and rooks
                b = Pt == PieceTypeS.BISHOP ? BitBoard.attacks_bb_SBBPT(s, pos.pieces() ^ pos.pieces_color_piecetype(Us, PieceTypeS.QUEEN), PieceTypeS.BISHOP)
                  : Pt == PieceTypeS.ROOK ? BitBoard.attacks_bb_SBBPT(s, pos.pieces() ^ pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN), PieceTypeS.ROOK)
                                    : pos.attacks_from_square_piecetype(s, Pt);

                if ((ei.pinnedPieces[Us] & BitBoard.SquareBB[s]) != 0)
                {
                    b &= BitBoard.LineBB[pos.king_square(Us)][s];
                }

                ei.attackedBy[Us][PieceTypeS.ALL_PIECES] |= ei.attackedBy[Us][Pt] |= b;

                if ((b & ei.kingRing[Them]) != 0)
                {
                    ei.kingAttackersCount[Us]++;
                    ei.kingAttackersWeight[Us] += KingAttackWeights[Pt];
                    Bitboard bb = (b & ei.attackedBy[Them][PieceTypeS.KING]);
                    if (bb != 0)
                    {
                        ei.kingAdjacentZoneAttacksCount[Us] += Bitcount.popcount_Max15(bb);
                    }
                }

                if (Pt == PieceTypeS.QUEEN)
                {
                    b &= ~(ei.attackedBy[Them][PieceTypeS.KNIGHT]
                           | ei.attackedBy[Them][PieceTypeS.BISHOP]
                           | ei.attackedBy[Them][PieceTypeS.ROOK]);
                }

                int mob = (Pt != PieceTypeS.QUEEN ? Bitcount.popcount_Max15(b & mobilityArea[Us])
                                                  : Bitcount.popcount(b & mobilityArea[Us]));

                mobility[Us] += MobilityBonus[Pt][mob];

                // Decrease score if we are attacked by an enemy pawn. The remaining part
                // of threat evaluation must be done later when we have full attack info.
                if ((ei.attackedBy[Them][PieceTypeS.PAWN] & BitBoard.SquareBB[s]) != 0)
                {
                    score -= ThreatenedByPawn[Pt];
                }

                if (Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.KNIGHT)
                {
                    // Penalty for bishop with same coloured pawns
                    if (Pt == PieceTypeS.BISHOP)
                    {
                        score -= BishopPawns * ei.pi.pawns_on_same_color_squares(Us, s);
                    }

                    // Bishop and knight outposts squares
                    if (0 == (pos.pieces_color_piecetype(Them, PieceTypeS.PAWN) & BitBoard.pawn_attack_span(Us, s)))
                    {
                        score += evaluate_outposts(pos, ei, s, Pt, Us);
                    }

                    // Bishop or knight behind a pawn
                    if (Types.relative_rank_square(Us, s) < RankS.RANK_5 &&
                        (pos.pieces_piecetype(PieceTypeS.PAWN) & BitBoard.SquareBB[(s + Types.pawn_push(Us))]) != 0)
                    {
                        score += MinorBehindPawn;
                    }
                }

                if (Pt == PieceTypeS.ROOK)
                {
                    // Rook piece attacking enemy pawns on the same rank/file
                    if (Types.relative_rank_square(Us, s) >= RankS.RANK_5)
                    {
                        Bitboard pawns = pos.pieces_color_piecetype(Them, PieceTypeS.PAWN) & BitBoard.PseudoAttacks[PieceTypeS.ROOK][s];
                        if (pawns != 0)
                        {
                            score += Bitcount.popcount_Max15(pawns) * RookOnPawn;
                        }
                    }

                    // Give a bonus for a rook on a open or semi-open file
                    if (ei.pi.semiopen_file(Us, Types.file_of(s)) != 0)
                    {
                        score += ei.pi.semiopen_file(Them, Types.file_of(s)) != 0 ? RookOpenFile : RookSemiopenFile;
                    }

                    if (mob > 3 || ei.pi.semiopen_file(Us, Types.file_of(s)) != 0)
                    {
                        continue;
                    }

                    Square ksq = pos.king_square(Us);

                    // Penalize rooks which are trapped by a king. Penalize more if the
                    // king has lost its castling capability.
                    if (((Types.file_of(ksq) < FileS.FILE_E) == (Types.file_of(s) < Types.file_of(ksq))) &&
                        (Types.rank_of(ksq) == Types.rank_of(s) || Types.relative_rank_square(Us, ksq) == RankS.RANK_1) &&
                        0 == ei.pi.semiopen_side(Us, Types.file_of(ksq), Types.file_of(s) < Types.file_of(ksq)))
                    {
                        score -= (TrappedRook - Types.make_score(mob * 8, 0)) * (1 + (pos.can_castle_color(Us) == 0 ? 1 : 0));
                    }
                }

                // An important Chess960 pattern: A cornered bishop blocked by a friendly
                // pawn diagonally in front of it is a very serious problem, especially
                // when that pawn is also blocked.
                if (Pt == PieceTypeS.BISHOP &&
                    pos.is_chess960() != 0 &&
                    (s == Types.relative_square(Us, SquareS.SQ_A1) || s == Types.relative_square(Us, SquareS.SQ_H1)))
                {
                    Square d = Types.pawn_push(Us) + (Types.file_of(s) == FileS.FILE_A ? SquareS.DELTA_E : SquareS.DELTA_W);
                    if (pos.piece_on(s + d) == Types.make_piece(Us, PieceTypeS.PAWN))
                    {
                        score -= !pos.empty(s + d + Types.pawn_push(Us)) ? TrappedBishopA1H1 * 4
                            : pos.piece_on(s + d + d) == Types.make_piece(Us, PieceTypeS.PAWN) ? TrappedBishopA1H1 * 2
                                                                            : TrappedBishopA1H1;
                    }
                }
            }

            if (Trace)
            {
                Tracing.terms[Us][Pt] = score;
            }

            return(score - evaluate_pieces(pos, ei, mobility, mobilityArea, NextPt, Them, Trace));
        }