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