Exemplo n.º 1
0
    internal override ScaleFactor GetScaleFactor(Position pos)
    {
        Debug.Assert(verify_material(pos, strongSide, Value.RookValueMg, 1));
        Debug.Assert(verify_material(pos, weakSide, Value.BishopValueMg, 0));

        // Test for a rook pawn
        if ((pos.pieces_Pt(PieceType.PAWN) & (Bitboard.FileABB | Bitboard.FileHBB))!=0)
        {
            var ksq = pos.square(PieceType.KING, weakSide);
            var bsq = pos.square(PieceType.BISHOP, weakSide);
            var psq = pos.square(PieceType.PAWN, strongSide);
            var rk = Rank.relative_rank_CtSt(strongSide, psq);
            var push = Square.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 == Rank.RANK_5 && !Square.opposite_colors(bsq, psq))
            {
                var d = Utils.distance_Square(psq + 3*push, ksq);

                if (d <= 2 && !(d == 0 && ksq == pos.square(PieceType.KING, strongSide) + 2*push))
                {
                    return (ScaleFactor) (24);
                }
                return (ScaleFactor) (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 == Rank.RANK_6 && Utils.distance_Square(psq + 2*push, ksq) <= 1
                && Bitboard.AndWithSquare(Utils.PseudoAttacks[PieceType.BISHOP, bsq], (psq + push))!=0 && Utils.distance_File(bsq, psq) >= 2)
            {
                return (ScaleFactor) (8);
            }
        }

        return ScaleFactor.SCALE_FACTOR_NONE;
    }
Exemplo n.º 2
0
    internal override ScaleFactor GetScaleFactor(Position pos)
    {
        Debug.Assert(pos.non_pawn_material(strongSide) == Value.BishopValueMg);
        Debug.Assert(pos.count(PieceType.PAWN, strongSide) >= 1);

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

        var pawns = pos.pieces_CtPt(strongSide, PieceType.PAWN);
        var pawnsFile = Square.file_of(Utils.lsb(pawns));

        // All pawns are on a single rook file?
        if ((pawnsFile == File.FILE_A || pawnsFile == File.FILE_H) && (pawns & ~Utils.file_bb_Ft(pawnsFile))==0)
        {
            var bishopSq = pos.square(PieceType.BISHOP, strongSide);
            var queeningSq = Square.relative_square(strongSide, Square.make_square(pawnsFile, Rank.RANK_8));
            var kingSq = pos.square(PieceType.KING, weakSide);

            if (Square.opposite_colors(queeningSq, bishopSq) && Utils.distance_Square(queeningSq, kingSq) <= 1)
            {
                return ScaleFactor.SCALE_FACTOR_DRAW;
            }
        }

        // If all the pawns are on the same B or G file, then it's potentially a draw
        if ((pawnsFile == File.FILE_B || pawnsFile == File.FILE_G)
            && (pos.pieces_Pt(PieceType.PAWN) & ~Utils.file_bb_Ft(pawnsFile))==0 && pos.non_pawn_material(weakSide) == 0
            && pos.count(PieceType.PAWN, weakSide) >= 1)
        {
            // Get weakSide pawn that is closest to the home rank
            var weakPawnSq = Utils.backmost_sq(weakSide, pos.pieces_CtPt(weakSide, PieceType.PAWN));

            var strongKingSq = pos.square(PieceType.KING, strongSide);
            var weakKingSq = pos.square(PieceType.KING, weakSide);
            var bishopSq = pos.square(PieceType.BISHOP, strongSide);

            // 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 (Rank.relative_rank_CtSt(strongSide, weakPawnSq) == Rank.RANK_7
                && Bitboard.AndWithSquare(pos.pieces_CtPt(strongSide, PieceType.PAWN), (weakPawnSq + Square.pawn_push(weakSide)))!=0
                && (Square.opposite_colors(bishopSq, weakPawnSq) || pos.count(PieceType.PAWN, strongSide) == 1))
            {
                var strongKingDist = Utils.distance_Square(weakPawnSq, strongKingSq);
                var weakKingDist = Utils.distance_Square(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 (Rank.relative_rank_CtSt(strongSide, weakKingSq) >= Rank.RANK_7 && weakKingDist <= 2
                    && weakKingDist <= strongKingDist)
                {
                    return ScaleFactor.SCALE_FACTOR_DRAW;
                }
            }
        }

        return ScaleFactor.SCALE_FACTOR_NONE;
    }
Exemplo n.º 3
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;
        }
Exemplo n.º 4
0
    /// Material::probe() looks up the current position's material configuration in
    /// the material hash table. It returns a pointer to the Entry if the position
    /// is found. Otherwise a new Entry is computed and stored there, so we don't
    /// have to recompute all when the same material configuration occurs again.
    internal static MaterialEntry probe(Position pos)
    {
        var key = pos.material_key();
        MaterialEntry e;
        if (!pos.this_thread().materialTable.TryGetValue(key, out e))
        {
            e = new MaterialEntry();
            pos.this_thread().materialTable.Add(key, e);
        }
        else if (e.key == key)
        {
            return e;
        }

        e.reset();

        e.key = key;
        e.factor[Color.WHITE] = e.factor[Color.BLACK] = (ushort) ScaleFactor.SCALE_FACTOR_NORMAL;
        e.gamePhase = pos.game_phase();

        // Let's look if we have a specialized evaluation function for this particular
        // material configuration. Firstly we look for a fixed configuration one, then
        // for a generic one if the previous search failed.
        if ((e.evaluationFunction = pos.this_thread().endgames.probeEndgameValue(key)) != null)
        {
            return e;
        }

        foreach (var c in Color.AllColors)
        {
            if (is_KXK(pos, c))
            {
                e.evaluationFunction = EvaluateKXK[c];
                return e;
            }
        }

        // OK, we didn't find any special evaluation function for the current material
        // configuration. Is there a suitable specialized scaling function?
        EndgameScaleFactor sf;

        if ((sf = pos.this_thread().endgames.probeEndgameScaleFactor(key)) != null)
        {
            e.scalingFunction[sf.strong_side()] = sf; // Only strong color assigned
            return e;
        }

        // We didn't find any specialized scaling function, so fall back on generic
        // ones that refer to more than one material distribution. Note that in this
        // case we don't return after setting the function.
        foreach (var c in Color.AllColors)
        {
            if (is_KBPsKs(pos, c))
            {
                e.scalingFunction[c] = ScaleKBPsK[c];
            }
            else if (is_KQKRPs(pos, c))
            {
                e.scalingFunction[c] = ScaleKQKRPs[c];
            }
        }

        var npm_w = pos.non_pawn_material(Color.WHITE);
        var npm_b = pos.non_pawn_material(Color.BLACK);

        if (npm_w + npm_b == Value.VALUE_ZERO && (pos.pieces_Pt(PieceType.PAWN) != 0)) // Only pawns on the board
        {
            if (pos.count(PieceType.PAWN, Color.BLACK) == 0)
            {
                Debug.Assert(pos.count(PieceType.PAWN, Color.WHITE) >= 2);

                e.scalingFunction[Color.WHITE] = ScaleKPsK[Color.WHITE];
            }
            else if (pos.count(PieceType.PAWN, Color.WHITE) == 0)
            {
                Debug.Assert(pos.count(PieceType.PAWN, Color.BLACK) >= 2);

                e.scalingFunction[Color.BLACK] = ScaleKPsK[Color.BLACK];
            }
            else if (pos.count(PieceType.PAWN, Color.WHITE) == 1 && pos.count(PieceType.PAWN, Color.BLACK) == 1)
            {
                // This is a special case because we set scaling functions
                // for both colors instead of only one.
                e.scalingFunction[Color.WHITE] = ScaleKPKP[Color.WHITE];
                e.scalingFunction[Color.BLACK] = ScaleKPKP[Color.BLACK];
            }
        }

        // Zero or just one pawn makes it difficult to win, even with a small material
        // advantage. This catches some trivial draws like KK, KBK and KNK and gives a
        // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
        if (pos.count(PieceType.PAWN, Color.WHITE) == 0 && npm_w - npm_b <= Value.BishopValueMg)
        {
            e.factor[Color.WHITE] =
                (ushort)
                    (npm_w < Value.RookValueMg
                        ? (ushort) ScaleFactor.SCALE_FACTOR_DRAW
                        : npm_b <= Value.BishopValueMg ? 4 : 14);
        }

        if (pos.count(PieceType.PAWN, Color.BLACK) == 0 && npm_b - npm_w <= Value.BishopValueMg)
        {
            e.factor[Color.BLACK] =
                (ushort)
                    (npm_b < Value.RookValueMg
                        ? (ushort) ScaleFactor.SCALE_FACTOR_DRAW
                        : npm_w <= Value.BishopValueMg ? 4 : 14);
        }

        if (pos.count(PieceType.PAWN, Color.WHITE) == 1 && npm_w - npm_b <= Value.BishopValueMg)
        {
            e.factor[Color.WHITE] = (ushort) ScaleFactor.SCALE_FACTOR_ONEPAWN;
        }

        if (pos.count(PieceType.PAWN, Color.BLACK) == 1 && npm_b - npm_w <= Value.BishopValueMg)
        {
            e.factor[Color.BLACK] = (ushort) ScaleFactor.SCALE_FACTOR_ONEPAWN;
        }

        // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
        // for the bishop pair "extended piece", which allows us to be more flexible
        // in defining bishop pair bonuses.
        int[][] PieceCount =
        {
            new[]
            {
                pos.count(PieceType.BISHOP, Color.WHITE) > 1 ? 1 : 0,
                pos.count(PieceType.PAWN, Color.WHITE), pos.count(PieceType.KNIGHT, Color.WHITE),
                pos.count(PieceType.BISHOP, Color.WHITE), pos.count(PieceType.ROOK, Color.WHITE),
                pos.count(PieceType.QUEEN, Color.WHITE)
            },
            new[]
            {
                pos.count(PieceType.BISHOP, Color.BLACK) > 1 ? 1 : 0,
                pos.count(PieceType.PAWN, Color.BLACK), pos.count(PieceType.KNIGHT, Color.BLACK),
                pos.count(PieceType.BISHOP, Color.BLACK), pos.count(PieceType.ROOK, Color.BLACK),
                pos.count(PieceType.QUEEN, Color.BLACK)
            }
        };

        e.value = (short) ((imbalance(Color.WHITE, PieceCount) - imbalance(Color.BLACK, PieceCount))/16);
        return e;
    }
Exemplo n.º 5
0
    /// evaluate() is the main evaluation function. It returns a static evaluation
    /// of the position always from the point of view of the side to move.
    internal static ValueT evaluate(bool DoTrace, Position pos)
    {
        Debug.Assert(pos.checkers() == 0);

        var ei = new EvalInfo();
        ScoreT[] mobility = {Score.SCORE_ZERO, Score.SCORE_ZERO};

        // Initialize score by reading the incrementally updated scores included
        // in the position object (material + piece square tables).
        // Score is computed from the point of view of white.
        var score = pos.psq_score();

        // Probe the material hash table
        var me = Material.probe(pos);
        score += me.imbalance();

        // If we have a specialized evaluation function for the current material
        // configuration, call it and return.
        if (me.specialized_eval_exists())
        {
            return me.evaluate(pos);
        }

        // Probe the pawn hash table
        ei.pi = Pawns.probe(pos);
        score += Score.Multiply(ei.pi.pawns_score(), Weights[PawnStructure]);

        // Initialize attack and king safety bitboards
        ei.attackedBy[Color.WHITE, PieceType.ALL_PIECES] =
            ei.attackedBy[Color.BLACK, PieceType.ALL_PIECES] = Bitboard.Create(0);
        init_eval_info(Color.WHITE, pos, ei);
        init_eval_info(Color.BLACK, pos, ei);

        // Pawns blocked or on ranks 2 and 3. Will be excluded from the mobility area
        BitboardT[] blockedPawns =
        {
            pos.pieces_CtPt(Color.WHITE, PieceType.PAWN)
            & (Bitboard.shift_bb(Square.DELTA_S, pos.pieces()) | Bitboard.Rank2BB
               | Bitboard.Rank3BB),
            pos.pieces_CtPt(Color.BLACK, PieceType.PAWN)
            & (Bitboard.shift_bb(Square.DELTA_N, pos.pieces()) | Bitboard.Rank7BB
               | Bitboard.Rank6BB)
        };

        // Do not include in mobility squares protected by enemy pawns, or occupied
        // by our blocked pawns or king.
        BitboardT[] mobilityArea =
        {
            ~(Bitboard.OrWithSquare(ei.attackedBy[Color.BLACK, PieceType.PAWN] | blockedPawns[Color.WHITE]
              , pos.square(PieceType.KING, Color.WHITE))),
            ~(Bitboard.OrWithSquare(ei.attackedBy[Color.WHITE, PieceType.PAWN] | blockedPawns[Color.BLACK]
              , pos.square(PieceType.KING, Color.BLACK)))
        };

        // Evaluate pieces and mobility
        score += evaluate_pieces(PieceType.KNIGHT, Color.WHITE, DoTrace, pos, ei, mobility, mobilityArea);
        score += Score.Multiply(mobility[Color.WHITE] - mobility[Color.BLACK], Weights[Mobility]);

        // Evaluate kings after all other pieces because we need complete attack
        // information when computing the king safety evaluation.
        score += evaluate_king(Color.WHITE, DoTrace, pos, ei) - evaluate_king(Color.BLACK, DoTrace, pos, ei);

        // Evaluate tactical threats, we need full attack information including king
        score += evaluate_threats(Color.WHITE, DoTrace, pos, ei) - evaluate_threats(Color.BLACK, DoTrace, pos, ei);

        // Evaluate passed pawns, we need full attack information including king
        score += evaluate_passed_pawns(Color.WHITE, DoTrace, pos, ei)
                 - evaluate_passed_pawns(Color.BLACK, DoTrace, pos, ei);

        // If both sides have only pawns, score for potential unstoppable pawns
        if (pos.non_pawn_material(Color.WHITE) == 0 && pos.non_pawn_material(Color.BLACK) == 0)
        {
            BitboardT b;
            if ((b = ei.pi.passed_pawns(Color.WHITE)) != 0)
            {
                score += Rank.relative_rank_CtSt(Color.WHITE, Utils.frontmost_sq(Color.WHITE, b)) * Unstoppable;
            }

            if ((b = ei.pi.passed_pawns(Color.BLACK)) != 0)
            {
                score -= Rank.relative_rank_CtSt(Color.BLACK, Utils.frontmost_sq(Color.BLACK, b)) * Unstoppable;
            }
        }

        // Evaluate space for both sides, only during opening
        if (pos.non_pawn_material(Color.WHITE) + pos.non_pawn_material(Color.BLACK) >= 12222)
        {
            score += Score.Multiply(evaluate_space(Color.WHITE, pos, ei) - evaluate_space(Color.BLACK, pos, ei), Weights[Space]);
        }

        // Scale winning side if position is more drawish than it appears
        var strongSide = Score.eg_value(score) > Value.VALUE_DRAW ? Color.WHITE : Color.BLACK;
        var sf = me.scale_factor(pos, strongSide);

        // If we don't already have an unusual scale factor, check for certain
        // types of endgames, and use a lower scale for those.
        if (me.game_phase() < Phase.PHASE_MIDGAME
            && (sf == ScaleFactor.SCALE_FACTOR_NORMAL || sf == ScaleFactor.SCALE_FACTOR_ONEPAWN))
        {
            if (pos.opposite_bishops())
            {
                // Endgame with opposite-colored bishops and no other pieces (ignoring pawns)
                // is almost a draw, in case of KBP vs KB is even more a draw.
                if (pos.non_pawn_material(Color.WHITE) == Value.BishopValueMg
                    && pos.non_pawn_material(Color.BLACK) == Value.BishopValueMg)
                {
                    sf = Bitboard.more_than_one(pos.pieces_Pt(PieceType.PAWN)) ? (ScaleFactor) (31) : (ScaleFactor) (9);
                }

                // Endgame with opposite-colored bishops, but also other pieces. Still
                // a bit drawish, but not as drawish as with only the two bishops.
                else
                {
                    sf = (ScaleFactor) (46*(int) sf/(int) ScaleFactor.SCALE_FACTOR_NORMAL);
                }
            }
            // Endings where weaker side can place his king in front of the opponent's
            // pawns are drawish.
            else if (Math.Abs(Score.eg_value(score)) <= Value.BishopValueEg && ei.pi.pawn_span(strongSide) <= 1
                     && !pos.pawn_passed(Color.opposite(strongSide), pos.square(PieceType.KING, Color.opposite(strongSide))))
            {
                sf = ei.pi.pawn_span(strongSide) != 0 ? (ScaleFactor) (51) : (ScaleFactor) (37);
            }
        }

        // Scale endgame by number of pawns
        var p = pos.count(PieceType.PAWN, Color.WHITE) + pos.count(PieceType.PAWN, Color.BLACK);
        var vEg = 1 + Math.Abs(Score.eg_value(score));
        sf = (ScaleFactor) (Math.Max((int) sf/2, (int) sf - 8*(int) ScaleFactor.SCALE_FACTOR_NORMAL*(12 - p)/vEg));

        // Interpolate between a middlegame and a (scaled by 'sf') endgame score
        var v = Score.mg_value(score)*(int) (me.game_phase())
                + Score.eg_value(score)*(Phase.PHASE_MIDGAME - me.game_phase())*(int) sf
                /(int) ScaleFactor.SCALE_FACTOR_NORMAL;

        v /= (int) (Phase.PHASE_MIDGAME);

        // In case of tracing add all single evaluation terms
        if (DoTrace)
        {
            add_IdxSt((int) Term.MATERIAL, pos.psq_score());
            add_IdxSt((int) Term.IMBALANCE, me.imbalance());
            add_IdxSt(PieceType.PAWN, ei.pi.pawns_score());
            add_IdxStSt(
                (int) Term.MOBILITY,
                Score.Multiply(mobility[Color.WHITE], Weights[Mobility]),
                Score.Multiply(mobility[Color.BLACK], Weights[Mobility]));
            add_IdxStSt(
                (int) Term.SPACE,
                Score.Multiply(evaluate_space(Color.WHITE, pos, ei), Weights[Space]),
                Score.Multiply(evaluate_space(Color.BLACK, pos, ei), Weights[Space]));
            add_IdxSt((int) Term.TOTAL, score);
        }

        return (pos.side_to_move() == Color.WHITE ? v : -v) + Tempo; // Side to move point of view
    }
Exemplo n.º 6
0
    // evaluate_pieces() assigns bonuses and penalties to the pieces of a given color

    private static ScoreT evaluate_pieces(
        PieceTypeT pieceType,
        ColorT Us,
        bool DoTrace,
        Position pos,
        EvalInfo ei,
        ScoreT[] mobility,
        BitboardT[] mobilityArea)
    {
        int Pt = pieceType;
        if (Pt == PieceType.KING)
        {
            return Score.SCORE_ZERO;
        }
        var score = Score.SCORE_ZERO;

        var NextPt = (Us == Color.WHITE ? pieceType : pieceType + 1);
        var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE);
        
        ei.attackedBy[Us, Pt] = Bitboard.Create(0);

        for(var idx=0; idx<16;idx++)
        {
            var s = pos.square(pieceType, Us, idx);
            if (s == Square.SQ_NONE)
            {
                break;
            }
            // Find attacked squares, including x-ray attacks for bishops and rooks
            var b = Pt == PieceType.BISHOP
                ? Utils.attacks_bb_PtSBb(PieceType.BISHOP, s, pos.pieces() ^ pos.pieces_CtPt(Us, PieceType.QUEEN))
                : Pt == PieceType.ROOK
                    ? Utils.attacks_bb_PtSBb(
                        PieceType.ROOK,
                        s,
                        pos.pieces() ^ pos.pieces_CtPtPt(Us, PieceType.ROOK, PieceType.QUEEN))
                    : pos.attacks_from_PtS(pieceType, s);

            if (Bitboard.AndWithSquare(ei.pinnedPieces[Us], s)!=0)
            {
                b &= Utils.LineBB[pos.square(PieceType.KING, Us), s];
            }

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

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

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

            var mob = Pt == PieceType.QUEEN
                ? Bitcount.popcount_Full(b & mobilityArea[Us])
                : Bitcount.popcount_Max15(b & mobilityArea[Us]);

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

            if (Pt == PieceType.BISHOP || Pt == PieceType.KNIGHT)
            {
                // Bonus for outpost square
                if (Rank.relative_rank_CtSt(Us, s) >= Rank.RANK_4 && Rank.relative_rank_CtSt(Us, s) <= Rank.RANK_6
                    && (pos.pieces_CtPt(Them, PieceType.PAWN) & Utils.pawn_attack_span(Us, s))==0)
                {
                    score +=
                        Outpost[Pt == PieceType.BISHOP ? 1 : 0][Bitboard.AndWithSquare(ei.attackedBy[Us, PieceType.PAWN], s)!=0 ? 1 : 0];
                }

                // Bonus when behind a pawn
                if (Rank.relative_rank_CtSt(Us, s) < Rank.RANK_5 && Bitboard.AndWithSquare(pos.pieces_Pt(PieceType.PAWN), (s + Square.pawn_push(Us)))!=0)
                {
                    score += MinorBehindPawn;
                }

                // Penalty for pawns on same color square of bishop
                if (Pt == PieceType.BISHOP)
                {
                    score -= BishopPawns*ei.pi.pawns_on_same_color_squares(Us, s);
                }

                // 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 == PieceType.BISHOP && pos.is_chess960()
                    && (s == Square.relative_square(Us, Square.SQ_A1) || s == Square.relative_square(Us, Square.SQ_H1)))
                {
                    var d = Square.pawn_push(Us) + (Square.file_of(s) == File.FILE_A ? Square.DELTA_E : Square.DELTA_W);
                    if (pos.piece_on(s + d) == Piece.make_piece(Us, PieceType.PAWN))
                    {
                        score -= !pos.empty(s + d + Square.pawn_push(Us))
                            ? TrappedBishopA1H1*4
                            : pos.piece_on(s + d + d) == Piece.make_piece(Us, PieceType.PAWN)
                                ? TrappedBishopA1H1*2
                                : TrappedBishopA1H1;
                    }
                }
            }

            if (Pt == PieceType.ROOK)
            {
                // Bonus for aligning with enemy pawns on the same rank/file
                if (Rank.relative_rank_CtSt(Us, s) >= Rank.RANK_5)
                {
                    var alignedPawns = pos.pieces_CtPt(Them, PieceType.PAWN) & Utils.PseudoAttacks[PieceType.ROOK, s];
                    if (alignedPawns!=0)
                    {
                        score += Bitcount.popcount_Max15(alignedPawns)*RookOnPawn;
                    }
                }

                // Bonus when on an open or semi-open file
                if (ei.pi.semiopen_file(Us, Square.file_of(s)) != 0)
                {
                    score += ei.pi.semiopen_file(Them, Square.file_of(s)) != 0 ? RookOnOpenFile : RookOnSemiOpenFile;
                }

                // Penalize when trapped by the king, even more if king cannot castle
                if (mob <= 3 && 0 == ei.pi.semiopen_file(Us, Square.file_of(s)))
                {
                    var ksq = pos.square(PieceType.KING, Us);

                    if (((Square.file_of(ksq) < File.FILE_E) == (Square.file_of(s) < Square.file_of(ksq)))
                        && (Square.rank_of(ksq) == Square.rank_of(s) || Rank.relative_rank_CtSt(Us, ksq) == Rank.RANK_1)
                        && 0 == ei.pi.semiopen_side(Us, Square.file_of(ksq), Square.file_of(s) < Square.file_of(ksq)))
                    {
                        score -= (TrappedRook - Score.make_score(mob*22, 0))*(1 + (pos.can_castle(Us) == 0 ? 1 : 0));
                    }
                }
            }
        }

        if (DoTrace)
        {
            add_IdxCtSt(Pt, Us, score);
        }
        // Recursively call evaluate_pieces() of next piece type until KING excluded
        return score - evaluate_pieces(NextPt, Them, DoTrace, pos, ei, mobility, mobilityArea);
    }