Exemple #1
0
    internal static ExtMoveArrayWrapper generate_castling(
        CastlingRight Cr,
        bool Checks,
        bool Chess960,
        Position pos,
        ExtMoveArrayWrapper moveList,
        ColorT us,
        CheckInfo ci)
    {
        var KingSide = (Cr == CastlingRight.WHITE_OO || Cr == CastlingRight.BLACK_OO);

        if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
        {
            return moveList;
        }

        // After castling, the rook and king final positions are the same in Chess960
        // as they would be in standard chess.
        var kfrom = pos.square(PieceType.KING, us);
        var rfrom = pos.castling_rook_square(Cr);
        var kto = Square.relative_square(us, KingSide ? Square.SQ_G1 : Square.SQ_C1);
        var enemies = pos.pieces_Ct(Color.opposite(us));

        Debug.Assert(pos.checkers() == 0);

        var K = Chess960 ? kto > kfrom ? Square.DELTA_W : Square.DELTA_E : KingSide ? Square.DELTA_W : Square.DELTA_E;

        for (var s = kto; s != kfrom; s += K)
        {
            if ((pos.attackers_to(s) & enemies) != 0)
            {
                return moveList;
            }
        }

        // Because we generate only legal castling moves we need to verify that
        // when moving the castling rook we do not discover some hidden checker.
        // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
        if (Chess960
            && ((Utils.attacks_bb_PtSBb(PieceType.ROOK, kto, Bitboard.XorWithSquare(pos.pieces(), rfrom))
                & pos.pieces_CtPtPt(Color.opposite(us), PieceType.ROOK, PieceType.QUEEN)))!= 0)
        {
            return moveList;
        }

        var m = Move.make(MoveType.CASTLING, kfrom, rfrom);

        if (Checks && !pos.gives_check(m, ci))
        {
            return moveList;
        }

        moveList.Add(m);
        return moveList;
    }
Exemple #2
0
        /// Entry::shelter_storm() calculates shelter and storm penalties for the file
        /// the king is on, as well as the two adjacent files.
        private ValueT shelter_storm(ColorT Us, Position pos, SquareT ksq)
        {
            const int NoFriendlyPawn = 0;
            const int Unblocked = 1;
            const int BlockedByPawn = 2;
            const int BlockedByKing = 3;
            var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE);

            var b = pos.pieces_Pt(PieceType.PAWN) & (Utils.in_front_bb(Us, Square.rank_of(ksq)) | Utils.rank_bb_St(ksq));
            var ourPawns = b & pos.pieces_Ct(Us);
            var theirPawns = b & pos.pieces_Ct(Them);
            var safety = MaxSafetyBonus;
            var center = File.Create(Math.Max(File.FILE_B, Math.Min(File.FILE_G, Square.file_of(ksq))));

            for (var f = center - 1; f <= (int)center + 1; ++f)
            {
                b = ourPawns & Utils.file_bb_Ft(File.Create(f));
                var rkUs = b != 0 ? Rank.relative_rank_CtSt(Us, Utils.backmost_sq(Us, b)) : Rank.RANK_1;

                b = theirPawns & Utils.file_bb_Ft(File.Create(f));
                var rkThem = b != 0 ? Rank.relative_rank_CtSt(Us, Utils.frontmost_sq(Them, b)) : Rank.RANK_1;

                safety -= ShelterWeakness[Math.Min(f, File.FILE_H - f)][rkUs]
                          + StormDanger[
                              f == (int)Square.file_of(ksq) && rkThem == Rank.relative_rank_CtSt(Us, ksq) + 1
                                  ? BlockedByKing
                                  : (int)rkUs == Rank.RANK_1
                                      ? NoFriendlyPawn
                                      : rkThem == rkUs + 1 ? BlockedByPawn : Unblocked][Math.Min(f, File.FILE_H - f)][rkThem];
            }

            return safety;
        }
Exemple #3
0
 // Helper used to detect a given material distribution
 private static bool is_KXK(Position pos, ColorT us)
 {
     return !Bitboard.more_than_one(pos.pieces_Ct(Color.opposite(us))) && pos.non_pawn_material(us) >= Value.RookValueMg;
 }
Exemple #4
0
    internal static ExtMoveArrayWrapper generate_pawn_moves(
        ColorT Us,
        GenType Type,
        Position pos,
        ExtMoveArrayWrapper moveList,
        BitboardT target,
        CheckInfo ci)
    {
        // Compute our parametrized parameters at compile time, named according to
        // the point of view of white side.
        var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE);
        var TRank8BB = (Us == Color.WHITE ? Bitboard.Rank8BB : Bitboard.Rank1BB);
        var TRank7BB = (Us == Color.WHITE ? Bitboard.Rank7BB : Bitboard.Rank2BB);
        var TRank3BB = (Us == Color.WHITE ? Bitboard.Rank3BB : Bitboard.Rank6BB);
        var Up = (Us == Color.WHITE ? Square.DELTA_N : Square.DELTA_S);
        var Right = (Us == Color.WHITE ? Square.DELTA_NE : Square.DELTA_SW);
        var Left = (Us == Color.WHITE ? Square.DELTA_NW : Square.DELTA_SE);

        var emptySquares = Bitboard.Create(0);

        var pawnsOn7 = pos.pieces_CtPt(Us, PieceType.PAWN) & TRank7BB;
        var pawnsNotOn7 = pos.pieces_CtPt(Us, PieceType.PAWN) & ~TRank7BB;

        var enemies = (Type == GenType.EVASIONS
            ? pos.pieces_Ct(Them) & target
            : Type == GenType.CAPTURES ? target : pos.pieces_Ct(Them));

        // Single and double pawn pushes, no promotions
        if (Type != GenType.CAPTURES)
        {
            emptySquares = (Type == GenType.QUIETS || Type == GenType.QUIET_CHECKS ? target : ~pos.pieces());

            var b1 = Bitboard.shift_bb(Up, pawnsNotOn7) & emptySquares;
            var b2 = Bitboard.shift_bb(Up, b1 & TRank3BB) & emptySquares;

            if (Type == GenType.EVASIONS) // Consider only blocking squares
            {
                b1 &= target;
                b2 &= target;
            }

            if (Type == GenType.QUIET_CHECKS)
            {
                b1 &= pos.attacks_from_PS(PieceType.PAWN, ci.ksq, Them);
                b2 &= pos.attacks_from_PS(PieceType.PAWN, ci.ksq, Them);

                // Add pawn pushes which give discovered check. This is possible only
                // if the pawn is not on the same file as the enemy king, because we
                // don't generate captures. Note that a possible discovery check
                // promotion has been already generated amongst the captures.
                if ((pawnsNotOn7 & ci.dcCandidates) != 0)
                {
                    var dc1 = Bitboard.shift_bb(Up, pawnsNotOn7 & ci.dcCandidates) & emptySquares
                              & ~Utils.file_bb_St(ci.ksq);
                    var dc2 = Bitboard.shift_bb(Up, dc1 & TRank3BB) & emptySquares;

                    b1 |= dc1;
                    b2 |= dc2;
                }
            }

            while (b1 != 0)
            {
                var to = Utils.pop_lsb(ref b1);
                (moveList).Add(Move.make_move(to - Up, to));
            }

            while (b2 != 0)
            {
                var to = Utils.pop_lsb(ref b2);
                (moveList).Add(Move.make_move(to - Up - Up, to));
            }
        }

        // Promotions and underpromotions
        if (pawnsOn7 != 0 && (Type != GenType.EVASIONS || ((target & TRank8BB) != 0)))
        {
            if (Type == GenType.CAPTURES)
            {
                emptySquares = ~pos.pieces();
            }

            if (Type == GenType.EVASIONS)
            {
                emptySquares &= target;
            }

            var b1 = Bitboard.shift_bb(Right, pawnsOn7) & enemies;
            var b2 = Bitboard.shift_bb(Left, pawnsOn7) & enemies;
            var b3 = Bitboard.shift_bb(Up, pawnsOn7) & emptySquares;

            while (b1 != 0)
            {
                moveList = make_promotions(Type, Right, moveList, Utils.pop_lsb(ref b1), ci);
            }

            while (b2 != 0)
            {
                moveList = make_promotions(Type, Left, moveList, Utils.pop_lsb(ref b2), ci);
            }

            while (b3 != 0)
            {
                moveList = make_promotions(Type, Up, moveList, Utils.pop_lsb(ref b3), ci);
            }
        }

        // Standard and en-passant captures
        if (Type == GenType.CAPTURES || Type == GenType.EVASIONS || Type == GenType.NON_EVASIONS)
        {
            var b1 = Bitboard.shift_bb(Right, pawnsNotOn7) & enemies;
            var b2 = Bitboard.shift_bb(Left, pawnsNotOn7) & enemies;

            while (b1 != 0)
            {
                var to = Utils.pop_lsb(ref b1);
                (moveList).Add(Move.make_move(to - Right, to));
            }

            while (b2 != 0)
            {
                var to = Utils.pop_lsb(ref b2);
                (moveList).Add(Move.make_move(to - Left, to));
            }

            if (pos.ep_square() != Square.SQ_NONE)
            {
                Debug.Assert(Square.rank_of(pos.ep_square()) == Rank.relative_rank_CtRt(Us, Rank.RANK_6));

                // An en passant capture can be an evasion only if the checking piece
                // is the double pushed pawn and so is in the target. Otherwise this
                // is a discovery check and we are forced to do otherwise.
                if (Type == GenType.EVASIONS && Bitboard.AndWithSquare(target, (pos.ep_square() - Up))==0)
                {
                    return moveList;
                }

                b1 = pawnsNotOn7 & pos.attacks_from_PS(PieceType.PAWN, pos.ep_square(), Them);

                Debug.Assert(b1 != 0);

                while (b1 != 0)
                {
                    (moveList).Add(Move.make(MoveType.ENPASSANT, Utils.pop_lsb(ref b1), pos.ep_square()));
                }
            }
        }

        return moveList;
    }
Exemple #5
0
    /// generate
    /// EVASIONS
    ///     generates all pseudo-legal check evasions when the side
    ///     to move is in check. Returns a pointer to the end of the move list.
    private static ExtMoveArrayWrapper generate_EVASIONS(Position pos, ExtMoveArrayWrapper moveList)
    {
        Debug.Assert(pos.checkers() != 0);

        var us = pos.side_to_move();
        var ksq = pos.square(PieceType.KING, us);
        var sliderAttacks = Bitboard.Create(0);
        var sliders = pos.checkers() & ~pos.pieces_PtPt(PieceType.KNIGHT, PieceType.PAWN);

        // Find all the squares attacked by slider checkers. We will remove them from
        // the king evasions in order to skip known illegal moves, which avoids any
        // useless legality checks later on.
        while (sliders != 0)
        {
            var checksq1 = Utils.pop_lsb(ref sliders);
            sliderAttacks |= Bitboard.XorWithSquare(Utils.LineBB[checksq1, ksq], checksq1);
        }

        // Generate evasions for king, capture and non capture moves
        var b = pos.attacks_from_PtS(PieceType.KING, ksq) & ~pos.pieces_Ct(us) & ~sliderAttacks;
        while (b != 0)
        {
            (moveList).Add(Move.make_move(ksq, Utils.pop_lsb(ref b)));
        }

        if (Bitboard.more_than_one(pos.checkers()))
        {
            return moveList; // Double check, only a king move can save the day
        }

        // Generate blocking evasions or captures of the checking piece
        var checksq = Utils.lsb(pos.checkers());
        var target = Bitboard.OrWithSquare(Utils.between_bb(checksq, ksq), checksq);

        return us == Color.WHITE
            ? generate_all(Color.WHITE, GenType.EVASIONS, pos, moveList, target)
            : generate_all(Color.BLACK, GenType.EVASIONS, pos, moveList, target);
    }
Exemple #6
0
    internal static ExtMoveArrayWrapper generate(GenType Type, Position pos, ExtMoveArrayWrapper moveList)
    {
        switch (Type)
        {
            case GenType.EVASIONS:
                return generate_EVASIONS(pos, moveList);
            case GenType.LEGAL:
                return generate_LEGAL(pos, moveList);
            case GenType.QUIET_CHECKS:
                return generate_QUIET_CHECKS(pos, moveList);
        }

        Debug.Assert(Type == GenType.CAPTURES || Type == GenType.QUIETS || Type == GenType.NON_EVASIONS);
        Debug.Assert(pos.checkers() == 0);

        var us = pos.side_to_move();

        var target = Type == GenType.CAPTURES
            ? pos.pieces_Ct(Color.opposite(us))
            : Type == GenType.QUIETS
                ? ~pos.pieces()
                : Type == GenType.NON_EVASIONS ? ~pos.pieces_Ct(us) : Bitboard.Create(0);

        return us == Color.WHITE
            ? generate_all(Color.WHITE, Type, pos, moveList, target)
            : generate_all(Color.BLACK, Type, pos, moveList, target);
    }
Exemple #7
0
    // evaluate_passed_pawns() evaluates the passed pawns of the given color
    private static ScoreT evaluate_passed_pawns(ColorT Us, bool DoTrace, Position pos, EvalInfo ei)
    {
        var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE);

        var score = Score.SCORE_ZERO;

        var b = ei.pi.passed_pawns(Us);

        while (b!=0)
        {
            var s = Utils.pop_lsb(ref b);

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

            int r = Rank.relative_rank_CtSt(Us, s) - Rank.RANK_2;
            var rr = r*(r - 1);

            ValueT mbonus = Passed[(int) Phase.MG][r], ebonus = Passed[(int) Phase.EG][r];

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

                // Adjust bonus based on the king's proximity
                ebonus += Utils.distance_Square(pos.square(PieceType.KING, Them), blockSq)*5*rr
                          - Utils.distance_Square(pos.square(PieceType.KING, Us), blockSq)*2*rr;

                // If blockSq is not the queening square then consider also a second push
                if (Rank.relative_rank_CtSt(Us, blockSq) != Rank.RANK_8)
                {
                    ebonus -= Utils.distance_Square(pos.square(PieceType.KING, Us), blockSq + Square.pawn_push(Us))*rr;
                }

                // If the pawn is free to advance, then increase the bonus
                if (pos.empty(blockSq))
                {
                    // If there is a rook or queen attacking/defending the pawn from behind,
                    // consider all the squaresToQueen. Otherwise consider only the squares
                    // in the pawn's path attacked or occupied by the enemy.
                    BitboardT squaresToQueen;
                    BitboardT unsafeSquares;
                    var defendedSquares = unsafeSquares = squaresToQueen = Utils.forward_bb(Us, s);

                    var bb = Utils.forward_bb(Them, s) & pos.pieces_PtPt(PieceType.ROOK, PieceType.QUEEN)
                             & pos.attacks_from_PtS(PieceType.ROOK, s);

                    if ((pos.pieces_Ct(Us) & bb)==0)
                    {
                        defendedSquares &= ei.attackedBy[Us, PieceType.ALL_PIECES];
                    }

                    if ((pos.pieces_Ct(Them) & bb) == 0)
                    {
                        unsafeSquares &= ei.attackedBy[Them, PieceType.ALL_PIECES] | pos.pieces_Ct(Them);
                    }

                    // If there aren't any enemy attacks, assign a big bonus. Otherwise
                    // assign a smaller bonus if the block square isn't attacked.
                    var k = unsafeSquares == 0 ? 18 : Bitboard.AndWithSquare(unsafeSquares, blockSq)==0 ? 8 : 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 (Bitboard.AndWithSquare(defendedSquares, blockSq)!=0)
                    {
                        k += 4;
                    }

                    mbonus += k*rr;
                    ebonus += k*rr;
                }
                else if (Bitboard.AndWithSquare(pos.pieces_Ct(Us), blockSq)!=0)
                {
                    mbonus += rr*3 + r*2 + 3;
                    ebonus += rr + r*2;
                }
            } // rr != 0

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

            score += Score.make_score(mbonus, ebonus) + PassedFile[Square.file_of(s)];
        }

        if (DoTrace)
        {
            add_IdxCtSt((int) Term.PASSED, Us, Score.Multiply(score, Weights[PassedPawns]));
        }

        // Add the scores to the middlegame and endgame eval
        return Score.Multiply(score, Weights[PassedPawns]);
    }
Exemple #8
0
    // evaluate_threats() assigns bonuses according to the type of attacking piece
    // and the type of attacked one.

    private static ScoreT evaluate_threats(ColorT Us, bool DoTrace, Position pos, EvalInfo ei)
    {
        var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE);
        var Up = (Us == Color.WHITE ? Square.DELTA_N : Square.DELTA_S);
        var Left = (Us == Color.WHITE ? Square.DELTA_NW : Square.DELTA_SE);
        var Right = (Us == Color.WHITE ? Square.DELTA_NE : Square.DELTA_SW);
        var TRank2BB = (Us == Color.WHITE ? Bitboard.Rank2BB : Bitboard.Rank7BB);
        var TRank7BB = (Us == Color.WHITE ? Bitboard.Rank7BB : Bitboard.Rank2BB);

        const int Defended = 0;
        const int Weak = 1;
        const int Minor = 0;
        const int Rook = 1;

        BitboardT b;
        var score = Score.SCORE_ZERO;

        // Non-pawn enemies attacked by a pawn
        var weak = (pos.pieces_Ct(Them) ^ pos.pieces_CtPt(Them, PieceType.PAWN)) & ei.attackedBy[Us, PieceType.PAWN];

        if (weak!=0)
        {
            b = pos.pieces_CtPt(Us, PieceType.PAWN)
                & (~ei.attackedBy[Them, PieceType.ALL_PIECES] | ei.attackedBy[Us, PieceType.ALL_PIECES]);

            var safeThreats = (Bitboard.shift_bb(Right, b) | Bitboard.shift_bb(Left, b)) & weak;

            if ((weak ^ safeThreats)!=0)
            {
                score += ThreatenedByHangingPawn;
            }

            while (safeThreats!=0)
            {
                score += ThreatenedByPawn[Piece.type_of(pos.piece_on(Utils.pop_lsb(ref safeThreats)))];
            }
        }

        // Non-pawn enemies defended by a pawn
        var defended = (pos.pieces_Ct(Them) ^ pos.pieces_CtPt(Them, PieceType.PAWN)) & ei.attackedBy[Them, PieceType.PAWN];

        // Add a bonus according to the kind of attacking pieces
        if (defended!=0)
        {
            b = defended & (ei.attackedBy[Us, PieceType.KNIGHT] | ei.attackedBy[Us, PieceType.BISHOP]);
            while (b!=0)
            {
                score += Threat[Defended][Minor][Piece.type_of(pos.piece_on(Utils.pop_lsb(ref b)))];
            }

            b = defended & ei.attackedBy[Us, PieceType.ROOK];
            while (b!=0)
            {
                score += Threat[Defended][Rook][Piece.type_of(pos.piece_on(Utils.pop_lsb(ref b)))];
            }
        }

        // Enemies not defended by a pawn and under our attack
        weak = pos.pieces_Ct(Them) & ~ei.attackedBy[Them, PieceType.PAWN] & ei.attackedBy[Us, PieceType.ALL_PIECES];

        // Add a bonus according to the kind of attacking pieces
        if (weak!=0)
        {
            b = weak & (ei.attackedBy[Us, PieceType.KNIGHT] | ei.attackedBy[Us, PieceType.BISHOP]);
            while (b!=0)
            {
                score += Threat[Weak][Minor][Piece.type_of(pos.piece_on(Utils.pop_lsb(ref b)))];
            }

            b = weak & ei.attackedBy[Us, PieceType.ROOK];
            while (b!=0)
            {
                score += Threat[Weak][Rook][Piece.type_of(pos.piece_on(Utils.pop_lsb(ref b)))];
            }

            b = weak & ~ei.attackedBy[Them, PieceType.ALL_PIECES];
            if (b!=0)
            {
                score += Hanging*Bitcount.popcount_Max15(b);
            }

            b = weak & ei.attackedBy[Us, PieceType.KING];
            if (b!=0)
            {
                score += Bitboard.more_than_one(b) ? KingOnMany : KingOnOne;
            }
        }

        // Bonus if some pawns can safely push and attack an enemy piece
        b = pos.pieces_CtPt(Us, PieceType.PAWN) & ~TRank7BB;
        b = Bitboard.shift_bb(Up, b | (Bitboard.shift_bb(Up, b & TRank2BB) & ~pos.pieces()));

        b &= ~pos.pieces() & ~ei.attackedBy[Them, PieceType.PAWN]
             & (ei.attackedBy[Us, PieceType.ALL_PIECES] | ~ei.attackedBy[Them, PieceType.ALL_PIECES]);

        b = (Bitboard.shift_bb(Left, b) | Bitboard.shift_bb(Right, b)) & pos.pieces_Ct(Them)
            & ~ei.attackedBy[Us, PieceType.PAWN];

        if (b!=0)
        {
            score += Bitcount.popcount_Max15(b)*PawnAttackThreat;
        }

        if (DoTrace)
        {
            add_IdxCtSt((int) Term.THREAT, Us, score);
        }

        return score;
    }
Exemple #9
0
    // evaluate_king() assigns bonuses and penalties to a king of a given color

    private static ScoreT evaluate_king(ColorT Us, bool DoTrace, Position pos, EvalInfo ei)
    {
        var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE);

        var ksq = pos.square(PieceType.KING, Us);

        // King shelter and enemy pawns storm
        var score = ei.pi.king_safety(Us, pos, ksq);

        // Main king safety evaluation
        if (ei.kingAttackersCount[Them] != 0)
        {
            // Find the attacked squares around the king which have no defenders
            // apart from the king itself
            var undefended = ei.attackedBy[Them, PieceType.ALL_PIECES] & ei.attackedBy[Us, PieceType.KING]
                                  & ~(ei.attackedBy[Us, PieceType.PAWN] | ei.attackedBy[Us, PieceType.KNIGHT]
                                      | ei.attackedBy[Us, PieceType.BISHOP] | ei.attackedBy[Us, PieceType.ROOK]
                                      | ei.attackedBy[Us, PieceType.QUEEN]);

            // Initialize the 'attackUnits' variable, which is used later on as an
            // index into the KingDanger[] array. The initial value is based on the
            // number and types of the enemy's attacking pieces, the number of
            // attacked and undefended squares around our king and the quality of
            // the pawn shelter (current 'score' value).
            var attackUnits = Math.Min(72, ei.kingAttackersCount[Them] *ei.kingAttackersWeight[Them])
                              + 9*ei.kingAdjacentZoneAttacksCount[Them] + 27*Bitcount.popcount_Max15(undefended)
                              + 11*((ulong)ei.pinnedPieces[Us] != 0 ? 1 : 0)
                              - 64*(pos.count(PieceType.QUEEN, Them) == 0 ? 1 : 0) - Score.mg_value(score)/8;

            // Analyse the enemy's safe queen contact checks. Firstly, find the
            // undefended squares around the king reachable by the enemy queen...
            var b = undefended & ei.attackedBy[Them, PieceType.QUEEN] & ~pos.pieces_Ct(Them);
            if (b!=0)
            {
                // ...and then remove squares not supported by another enemy piece
                b &= ei.attackedBy[Them, PieceType.PAWN] | ei.attackedBy[Them, PieceType.KNIGHT]
                     | ei.attackedBy[Them, PieceType.BISHOP] | ei.attackedBy[Them, PieceType.ROOK];

                if (b!=0)
                {
                    attackUnits += QueenContactCheck*Bitcount.popcount_Max15(b);
                }
            }

            // Analyse the enemy's safe distance checks for sliders and knights
            var safe = ~(ei.attackedBy[Us, PieceType.ALL_PIECES] | pos.pieces_Ct(Them));

            var b1 = pos.attacks_from_PtS(PieceType.ROOK, ksq) & safe;
            var b2 = pos.attacks_from_PtS(PieceType.BISHOP, ksq) & safe;

            // Enemy queen safe checks
            b = (b1 | b2) & ei.attackedBy[Them, PieceType.QUEEN];
            if (b!=0)
            {
                attackUnits += QueenCheck*Bitcount.popcount_Max15(b);
                score -= Checked;
            }

            // Enemy rooks safe checks
            b = b1 & ei.attackedBy[Them, PieceType.ROOK];
            if (b!=0)
            {
                attackUnits += RookCheck*Bitcount.popcount_Max15(b);
                score -= Checked;
            }

            // Enemy bishops safe checks
            b = b2 & ei.attackedBy[Them, PieceType.BISHOP];
            if (b!=0)
            {
                attackUnits += BishopCheck*Bitcount.popcount_Max15(b);
                score -= Checked;
            }

            // Enemy knights safe checks
            b = pos.attacks_from_PtS(PieceType.KNIGHT, ksq) & ei.attackedBy[Them, PieceType.KNIGHT] & safe;
            if (b!=0)
            {
                attackUnits += KnightCheck*Bitcount.popcount_Max15(b);
                score -= Checked;
            }

            // Finally, extract the king danger score from the KingDanger[]
            // array and subtract the score from evaluation.
            score -= KingDanger[Math.Max(Math.Min(attackUnits, 399), 0)];
        }

        if (DoTrace)
        {
            add_IdxCtSt(PieceType.KING, Us, score);
        }

        return score;
    }