Пример #1
0
        // evaluate_outposts() evaluates bishop and knight outpost squares
        public static Score evaluate_outposts(Position pos, EvalInfo ei, Square s, PieceType Pt, Color Us)
        {
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            Debug.Assert(Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.KNIGHT);

            // Initial bonus based on square
            Value bonus = Outpost[Pt == PieceTypeS.BISHOP ? 1 : 0][Types.relative_square(Us, s)];

            // Increase bonus if supported by pawn, especially if the opponent has
            // no minor piece which can trade with the outpost piece.
            if (bonus != 0 && (ei.attackedBy[Us][PieceTypeS.PAWN] & BitBoard.SquareBB[s]) != 0)
            {
                if (0 == pos.pieces_color_piecetype(Them, PieceTypeS.KNIGHT) &&
                    0 == (BitBoard.squares_of_color(s) & pos.pieces_color_piecetype(Them, PieceTypeS.BISHOP)))
                {
                    bonus += bonus + bonus / 2;
                }
                else
                {
                    bonus += bonus / 2;
                }
            }
            return(Types.make_score(bonus, bonus));
        }
Пример #2
0
        // init_eval_info() initializes king bitboards for given color adding
        // pawn attacks. To be done at the beginning of the evaluation.
        public static void init_eval_info(Position pos, EvalInfo ei, Color Us)
        {
            Color  Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);
            Square Down = (Us == ColorS.WHITE ? SquareS.DELTA_S : SquareS.DELTA_N);

            ei.pinnedPieces[Us] = pos.pinned_pieces(Us);

            Bitboard b = ei.attackedBy[Them][PieceTypeS.KING] = pos.attacks_from_square_piecetype(pos.king_square(Them), PieceTypeS.KING);

            ei.attackedBy[Us][PieceTypeS.ALL_PIECES] = ei.attackedBy[Us][PieceTypeS.PAWN] = ei.pi.pawn_attacks(Us);

            // Init king safety tables only if we are going to use them
            if (pos.count(Us, PieceTypeS.QUEEN) != 0 && pos.non_pawn_material(Us) > ValueS.QueenValueMg + ValueS.PawnValueMg)
            {
                ei.kingRing[Them] = b | BitBoard.shift_bb(b, Down);
                b &= ei.attackedBy[Us][PieceTypeS.PAWN];
                ei.kingAttackersCount[Us]           = (b != 0) ? Bitcount.popcount_Max15(b) : 0;
                ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
            }
            else
            {
                ei.kingRing[Them]         = 0;
                ei.kingAttackersCount[Us] = 0;
            }
        }
Пример #3
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))));
        }
Пример #4
0
        // evaluate_threats() assigns bonuses according to the type of attacking piece
        // and the type of attacked one.
        public static Score evaluate_threats(Position pos, EvalInfo ei, Color Us, bool Trace)
        {
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            Bitboard b, weakEnemies;
            Score    score = ScoreS.SCORE_ZERO;

            // Enemies not defended by a pawn and under our attack
            weakEnemies = pos.pieces_color(Them)
                          & ~ei.attackedBy[Them][PieceTypeS.PAWN]
                          & ei.attackedBy[Us][PieceTypeS.ALL_PIECES];

            // Add a bonus according if the attacking pieces are minor or major
            if (weakEnemies != 0)
            {
                b = weakEnemies & (ei.attackedBy[Us][PieceTypeS.PAWN] | ei.attackedBy[Us][PieceTypeS.KNIGHT] | ei.attackedBy[Us][PieceTypeS.BISHOP]);
                if (b != 0)
                {
                    score += Threat[0][Types.type_of_piece(pos.piece_on(BitBoard.lsb(b)))];
                }

                b = weakEnemies & (ei.attackedBy[Us][PieceTypeS.ROOK] | ei.attackedBy[Us][PieceTypeS.QUEEN]);
                if (b != 0)
                {
                    score += Threat[1][Types.type_of_piece(pos.piece_on(BitBoard.lsb(b)))];
                }

                b = weakEnemies & ~ei.attackedBy[Them][PieceTypeS.ALL_PIECES];
                if (b != 0)
                {
                    score += BitBoard.more_than_one(b) ? Hanging[Us != pos.side_to_move()?1:0] * Bitcount.popcount_Max15(b)
                        : Hanging[Us == pos.side_to_move()?1:0];
                }
            }

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

            return(score);
        }
Пример #5
0
        // evaluate_space() computes the space evaluation for a given side. The
        // space evaluation is a simple bonus based on the number of safe squares
        // available for minor pieces on the central four files on ranks 2--4. Safe
        // squares one, two or three squares behind a friendly pawn are counted
        // twice. Finally, the space bonus is scaled by a weight taken from the
        // material hash table. The aim is to improve play on game opening.
        public static int evaluate_space(Position pos, EvalInfo ei, Color Us)
        {
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            // Find the safe squares for our pieces inside the area defined by
            // SpaceMask[]. A square is unsafe if it is attacked by an enemy
            // pawn, or if it is undefended and attacked by an enemy piece.
            Bitboard safe = SpaceMask[Us]
                            & ~pos.pieces_color_piecetype(Us, PieceTypeS.PAWN)
                            & ~ei.attackedBy[Them][PieceTypeS.PAWN]
                            & (ei.attackedBy[Us][PieceTypeS.ALL_PIECES] | ~ei.attackedBy[Them][PieceTypeS.ALL_PIECES]);

            // Find all squares which are at most three squares behind some friendly pawn
            Bitboard behind = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN);

            behind |= (Us == ColorS.WHITE ? behind >> 8 : behind << 8);
            behind |= (Us == ColorS.WHITE ? behind >> 16 : behind << 16);

            // Since SpaceMask[Us] is fully on our half of the board
            Debug.Assert((UInt32)(safe >> (Us == ColorS.WHITE ? 32 : 0)) == 0);

            // Count safe + (behind & safe) with a single popcount
            return(Bitcount.popcount((Us == ColorS.WHITE ? safe << 32 : safe >> 32) | (behind & safe)));
        }
Пример #6
0
        // do_evaluate() is the evaluation entry point, called directly from evaluate()
        public static Value do_evaluate(Position pos, bool Trace)
        {
            Debug.Assert(0 == pos.checkers());

            EvalInfo ei = new EvalInfo();
            Score    score;

            Score[] mobility   = new Score[] { ScoreS.SCORE_ZERO, ScoreS.SCORE_ZERO };
            Thread  thisThread = pos.this_thread();

            // Initialize score by reading the incrementally updated scores included
            // in the position object (material + piece square tables) and adding a
            // Tempo bonus. Score is computed from the point of view of white.
            score = pos.psq_score() + (pos.side_to_move() == ColorS.WHITE ? Tempo : -Tempo);

            // Probe the material hash table
            ei.mi  = Material.probe(pos, thisThread.materialTable, thisThread.endgames);
            score += ei.mi.material_value();

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

            // Probe the pawn hash table
            ei.pi  = Pawns.probe(pos, thisThread.pawnsTable);
            score += apply_weight(ei.pi.pawns_value(), Weights[EvalWeightS.PawnStructure]);

            // Initialize attack and king safety bitboards
            init_eval_info(pos, ei, ColorS.WHITE);
            init_eval_info(pos, ei, ColorS.BLACK);

            ei.attackedBy[ColorS.WHITE][PieceTypeS.ALL_PIECES] |= ei.attackedBy[ColorS.WHITE][PieceTypeS.KING];
            ei.attackedBy[ColorS.BLACK][PieceTypeS.ALL_PIECES] |= ei.attackedBy[ColorS.BLACK][PieceTypeS.KING];

            // Do not include in mobility squares protected by enemy pawns or occupied by our pawns or king
            Bitboard[] mobilityArea = new Bitboard[] { ~(ei.attackedBy[ColorS.BLACK][PieceTypeS.PAWN] | pos.pieces_color_piecetype(ColorS.WHITE, PieceTypeS.PAWN, PieceTypeS.KING)),
                                                       ~(ei.attackedBy[ColorS.WHITE][PieceTypeS.PAWN] | pos.pieces_color_piecetype(ColorS.BLACK, PieceTypeS.PAWN, PieceTypeS.KING)) };
            // Evaluate pieces and mobility
            score += evaluate_pieces(pos, ei, mobility, mobilityArea, PieceTypeS.KNIGHT, ColorS.WHITE, Trace);
            score += Eval.apply_weight(mobility[ColorS.WHITE] - mobility[ColorS.BLACK], Weights[EvalWeightS.Mobility]);

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

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

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

            // If one side has only a king, check whether exists any unstoppable passed pawn
            if (0 == pos.non_pawn_material(ColorS.WHITE) || 0 == pos.non_pawn_material(ColorS.BLACK))
            {
                score += evaluate_unstoppable_pawns(pos, ColorS.WHITE, ei)
                         - evaluate_unstoppable_pawns(pos, ColorS.BLACK, ei);
            }

            // Evaluate space for both sides, only in middle-game.
            if (ei.mi.space_weight() != 0)
            {
                int s = evaluate_space(pos, ei, ColorS.WHITE) - evaluate_space(pos, ei, ColorS.BLACK);
                score += Eval.apply_weight(s * ei.mi.space_weight(), Weights[EvalWeightS.Space]);
            }

            // Scale winning side if position is more drawish that what it appears
            ScaleFactor sf = Types.eg_value(score) > ValueS.VALUE_DRAW ? ei.mi.scale_factor(pos, ColorS.WHITE)
                                                                       : ei.mi.scale_factor(pos, ColorS.BLACK);

            // If we don't already have an unusual scale factor, check for opposite
            // colored bishop endgames, and use a lower scale for those.
            if (ei.mi.game_phase() < PhaseS.PHASE_MIDGAME &&
                pos.opposite_bishops() &&
                (sf == ScaleFactorS.SCALE_FACTOR_NORMAL || sf == ScaleFactorS.SCALE_FACTOR_ONEPAWN))
            {
                // Ignoring any pawns, do both sides only have a single bishop and no
                // other pieces?
                if (pos.non_pawn_material(ColorS.WHITE) == ValueS.BishopValueMg &&
                    pos.non_pawn_material(ColorS.BLACK) == ValueS.BishopValueMg)
                {
                    // Check for KBP vs KB with only a single pawn that is almost
                    // certainly a draw or at least two pawns.
                    bool one_pawn = (pos.count(ColorS.WHITE, PieceTypeS.PAWN) + pos.count(ColorS.BLACK, PieceTypeS.PAWN) == 1);
                    sf = one_pawn ? (8) : (32);
                }
                else
                {
                    // Endgame with opposite-colored bishops, but also other pieces. Still
                    // a bit drawish, but not as drawish as with only the two bishops.
                    sf = (50 * sf / ScaleFactorS.SCALE_FACTOR_NORMAL);
                }
            }

            // Interpolate between a middlegame and a (scaled by 'sf') endgame score
            Value v = Types.mg_value(score) * (ei.mi.game_phase())
                      + Types.eg_value(score) * (PhaseS.PHASE_MIDGAME - ei.mi.game_phase()) * sf / ScaleFactorS.SCALE_FACTOR_NORMAL;

            v /= (PhaseS.PHASE_MIDGAME);

            // In case of tracing add all single evaluation contributions for both white and black
            if (Trace)
            {
                //Tracing.add_term(Tracing.PST, pos.psq_score());
                //Tracing.add_term(Tracing.IMBALANCE, ei.mi.material_value());
                //Tracing.add_term(PAWN, ei.pi.pawns_value());
                //Tracing.add_term(Tracing.MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
                //                                   , apply_weight(mobility[BLACK], Weights[Mobility]));
                //Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
                //Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
                //Tracing.add_term(Tracing.SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
                //Tracing.add_term(Tracing.TOTAL, score);
                //Tracing.ei = ei;
                //Tracing.sf = sf;
            }

            return(pos.side_to_move() == ColorS.WHITE ? v : -v);
        }
Пример #7
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]));
        }
Пример #8
0
        // evaluate_king() assigns bonuses and penalties to a king of a given color
        public static Score evaluate_king(Position pos, EvalInfo ei, Color Us, bool Trace)
        {
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            Bitboard undefended, b, b1, b2, safe;
            int      attackUnits;
            Square   ksq = pos.king_square(Us);

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

            // 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
                undefended = ei.attackedBy[Them][PieceTypeS.ALL_PIECES]
                             & ei.attackedBy[Us][PieceTypeS.KING]
                             & ~(ei.attackedBy[Us][PieceTypeS.PAWN] | ei.attackedBy[Us][PieceTypeS.KNIGHT]
                                 | ei.attackedBy[Us][PieceTypeS.BISHOP] | ei.attackedBy[Us][PieceTypeS.ROOK]
                                 | ei.attackedBy[Us][PieceTypeS.QUEEN]);

                // Initialize the 'attackUnits' variable, which is used later on as an
                // index to 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).
                attackUnits = Math.Min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
                              + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + Bitcount.popcount_Max15(undefended))
                              + 2 * (ei.pinnedPieces[Us] != 0 ? 1 : 0)
                              - Types.mg_value(score) / 32;

                // Analyse the enemy's safe queen contact checks. Firstly, find the
                // undefended squares around the king that are attacked by the enemy's
                // queen...
                b = undefended & ei.attackedBy[Them][PieceTypeS.QUEEN] & ~pos.pieces_color(Them);
                if (b != 0)
                {
                    // ...and then remove squares not supported by another enemy piece
                    b &= (ei.attackedBy[Them][PieceTypeS.PAWN] | ei.attackedBy[Them][PieceTypeS.KNIGHT]
                          | ei.attackedBy[Them][PieceTypeS.BISHOP] | ei.attackedBy[Them][PieceTypeS.ROOK]);
                    if (b != 0)
                    {
                        attackUnits += QueenContactCheck
                                       * Bitcount.popcount_Max15(b)
                                       * (Them == pos.side_to_move() ? 2 : 1);
                    }
                }

                // Analyse the enemy's safe rook contact checks. Firstly, find the
                // undefended squares around the king that are attacked by the enemy's
                // rooks...
                b = undefended & ei.attackedBy[Them][PieceTypeS.ROOK] & ~pos.pieces_color(Them);

                // Consider only squares where the enemy rook gives check
                b &= BitBoard.PseudoAttacks[PieceTypeS.ROOK][ksq];

                if (b != 0)
                {
                    // ...and then remove squares not supported by another enemy piece
                    b &= (ei.attackedBy[Them][PieceTypeS.PAWN] | ei.attackedBy[Them][PieceTypeS.KNIGHT]
                          | ei.attackedBy[Them][PieceTypeS.BISHOP] | ei.attackedBy[Them][PieceTypeS.QUEEN]);

                    if (b != 0)
                    {
                        attackUnits += RookContactCheck
                                       * Bitcount.popcount_Max15(b)
                                       * (Them == pos.side_to_move() ? 2 : 1);
                    }
                }

                // Analyse enemy's safe distance checks for sliders and knights
                safe = ~(pos.pieces_color(Them) | ei.attackedBy[Us][PieceTypeS.ALL_PIECES]);

                b1 = pos.attacks_from_square_piecetype(ksq, PieceTypeS.ROOK) & safe;
                b2 = pos.attacks_from_square_piecetype(ksq, PieceTypeS.BISHOP) & safe;

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

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

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

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

                // To index KingDanger[] attackUnits must be in [0, 99] range
                attackUnits = Math.Min(99, Math.Max(0, attackUnits));

                // Finally, extract the king danger score from the KingDanger[]
                // array and subtract the score from evaluation.
                score -= KingDanger[Us == Search.RootColor ? 1 : 0][attackUnits];
            }

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

            return(score);
        }
Пример #9
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));
        }