Пример #1
0
Value Entry::shelter_storm(const Position& pos, Square ksq) {

    const Color Them = (Us == WHITE ? BLACK : WHITE);
    const Bitboard Edges = (FileABB | FileHBB) & (Rank2BB | Rank3BB);

    Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
    Bitboard ourPawns = b & pos.pieces(Us);
    Bitboard theirPawns = b & pos.pieces(Them);
    Value safety = MaxSafetyBonus;
    File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));

    for (File f = kf - File(1); f <= kf + File(1); ++f)
    {
        b = ourPawns & file_bb(f);
        Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;

        b  = theirPawns & file_bb(f);
        Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;

        if (   (Edges & make_square(f, rkThem))
                && file_of(ksq) == f
                && relative_rank(Us, ksq) == rkThem - 1)
            safety += 200;
        else
            safety -=  ShelterWeakness[rkUs]
                       + StormDanger[rkUs   == RANK_1   ? 0 :
                                     rkThem != rkUs + 1 ? 1 : 2][rkThem];
    }

    return safety;
}
Пример #2
0
ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position& pos) const {

  assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
  assert(pos.piece_count(strongerSide, PAWN) == 2);
  assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
  assert(pos.piece_count(weakerSide, PAWN) == 1);

  Square wpsq1 = pos.piece_list(strongerSide, PAWN, 0);
  Square wpsq2 = pos.piece_list(strongerSide, PAWN, 1);
  Square bksq = pos.king_square(weakerSide);

  // Does the stronger side have a passed pawn?
  if (   pos.pawn_is_passed(strongerSide, wpsq1)
      || pos.pawn_is_passed(strongerSide, wpsq2))
      return SCALE_FACTOR_NONE;

  Rank r = Max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2));

  if (   file_distance(bksq, wpsq1) <= 1
      && file_distance(bksq, wpsq2) <= 1
      && relative_rank(strongerSide, bksq) > r)
  {
      switch (r) {
      case RANK_2: return ScaleFactor(10);
      case RANK_3: return ScaleFactor(10);
      case RANK_4: return ScaleFactor(15);
      case RANK_5: return ScaleFactor(20);
      case RANK_6: return ScaleFactor(40);
      default: assert(false);
      }
  }
  return SCALE_FACTOR_NONE;
}
Пример #3
0
Value Entry::shelter_storm(const Position& pos, Square ksq) {

  const Color Them = (Us == WHITE ? BLACK : WHITE);

  enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing };

  Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
  Bitboard ourPawns = b & pos.pieces(Us);
  Bitboard theirPawns = b & pos.pieces(Them);
  Value safety = MaxSafetyBonus;
  File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));

  for (File f = center - File(1); f <= center + File(1); ++f)
  {
      b = ourPawns & file_bb(f);
      Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;

      b  = theirPawns & file_bb(f);
      Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;

      safety -=  ShelterWeakness[std::min(f, FILE_H - f)][rkUs]
               + StormDanger
                 [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing  :
                  rkUs   == RANK_1                                          ? NoFriendlyPawn :
                  rkThem == rkUs + 1                                        ? BlockedByPawn  : Unblocked]
                 [std::min(f, FILE_H - f)][rkThem];
  }

  return safety;
}
Пример #4
0
Value Entry::shelter_storm(const Position& pos, Square ksq) {

  const Color Them = (Us == WHITE ? BLACK : WHITE);

  Value safety = MaxSafetyBonus;
  Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
  Bitboard ourPawns = b & pos.pieces(Us);
  Bitboard theirPawns = b & pos.pieces(Them);
  Rank rkUs, rkThem;
  File kf = file_of(ksq);

  kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf;

  for (int f = kf - 1; f <= kf + 1; f++)
  {
      b = ourPawns & FileBB[f];
      rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
      safety -= ShelterWeakness[rkUs];

      b  = theirPawns & FileBB[f];
      rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
      safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
  }

  return safety;
}
Пример #5
0
Value Entry::evaluate_shelter(const Position& pos, Square ksq) {

  constexpr Color     Them = (Us == WHITE ? BLACK : WHITE);
  constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
  constexpr Bitboard  BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB);

  Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
  Bitboard ourPawns = b & pos.pieces(Us);
  Bitboard theirPawns = b & pos.pieces(Them);

  Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
                 Value(374) : Value(5);

  File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
  for (File f = File(center - 1); f <= File(center + 1); ++f)
  {
      b = ourPawns & file_bb(f);
      int ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : 0;

      b = theirPawns & file_bb(f);
      int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;

      int d = std::min(f, ~f);
      safety += ShelterStrength[d][ourRank];
      safety -= (ourRank && (ourRank == theirRank - 1)) ? BlockedStorm[theirRank]
                                                        : UnblockedStorm[d][theirRank];
  }

  return safety;
}
Пример #6
0
ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {

  assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
  assert(pos.piece_count(strongerSide, BISHOP) == 1);
  assert(pos.piece_count(strongerSide, PAWN) == 1);
  assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
  assert(pos.piece_count(weakerSide, BISHOP) == 1);
  assert(pos.piece_count(weakerSide, PAWN) == 0);

  Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
  Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
  Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP, 0);
  Square weakerKingSq = pos.king_square(weakerSide);

  // Case 1: Defending king blocks the pawn, and cannot be driven away
  if (   square_file(weakerKingSq) == square_file(pawnSq)
      && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
      && (  !same_color_squares(weakerKingSq, strongerBishopSq)
          || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
      return SCALE_FACTOR_ZERO;

  // Case 2: Opposite colored bishops
  if (!same_color_squares(strongerBishopSq, weakerBishopSq))
  {
      // We assume that the position is drawn in the following three situations:
      //
      //   a. The pawn is on rank 5 or further back.
      //   b. The defending king is somewhere in the pawn's path.
      //   c. The defending bishop attacks some square along the pawn's path,
      //      and is at least three squares away from the pawn.
      //
      // These rules are probably not perfect, but in practice they work
      // reasonably well.

      if (relative_rank(strongerSide, pawnSq) <= RANK_5)
          return SCALE_FACTOR_ZERO;
      else
      {
          Bitboard path = squares_in_front_of(strongerSide, pawnSq);

          if (path & pos.pieces(KING, weakerSide))
              return SCALE_FACTOR_ZERO;

          if (  (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
              && square_distance(weakerBishopSq, pawnSq) >= 3)
              return SCALE_FACTOR_ZERO;
      }
  }
  return SCALE_FACTOR_NONE;
}
Пример #7
0
void MovePicker::score<CAPTURES>() {
  // Winning and equal captures in the main search are ordered by MVV, preferring
  // captures near our home rank. Surprisingly, this appears to perform slightly
  // better than SEE-based move ordering: exchanging big pieces before capturing
  // a hanging piece probably helps to reduce the subtree size.
  // In the main search we want to push captures with negative SEE values to the
  // badCaptures[] array, but instead of doing it now we delay until the move
  // has been picked up, saving some SEE calls in case we get a cutoff.
  for (auto& m : *this)
      m.value =  PieceValue[MG][pos.piece_on(to_sq(m))]
               - Value(200 * relative_rank(pos.side_to_move(), to_sq(m)));
}
Пример #8
0
ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {

  assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
  assert(pos.piece_count(strongerSide, BISHOP) == 1);
  assert(pos.piece_count(strongerSide, PAWN) == 1);
  assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
  assert(pos.piece_count(weakerSide, KNIGHT) == 1);
  assert(pos.piece_count(weakerSide, PAWN) == 0);

  Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
  Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
  Square weakerKingSq = pos.king_square(weakerSide);

  if (   square_file(weakerKingSq) == square_file(pawnSq)
      && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
      && (  !same_color_squares(weakerKingSq, strongerBishopSq)
          || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
      return SCALE_FACTOR_ZERO;

  return SCALE_FACTOR_NONE;
}
Пример #9
0
ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {

  assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
  assert(pos.piece_count(strongerSide, QUEEN) == 1);
  assert(pos.piece_count(strongerSide, PAWN) == 0);
  assert(pos.piece_count(weakerSide, ROOK) == 1);
  assert(pos.piece_count(weakerSide, PAWN) >= 1);

  Square kingSq = pos.king_square(weakerSide);
  if (   relative_rank(weakerSide, kingSq) <= RANK_2
      && relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4
      && (pos.pieces(ROOK, weakerSide) & relative_rank_bb(weakerSide, RANK_3))
      && (pos.pieces(PAWN, weakerSide) & relative_rank_bb(weakerSide, RANK_2))
      && (pos.attacks_from<KING>(kingSq) & pos.pieces(PAWN, weakerSide)))
  {
      Square rsq = pos.piece_list(weakerSide, ROOK, 0);
      if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide))
          return SCALE_FACTOR_ZERO;
  }
  return SCALE_FACTOR_NONE;
}
Пример #10
0
Value Entry::shelter_storm(const Position& pos, Square ksq) {

  const Color Them = (Us == WHITE ? BLACK : WHITE);

  Value safety = MaxSafetyBonus;
  Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
  Bitboard ourPawns = b & pos.pieces(Us);
  Bitboard theirPawns = b & pos.pieces(Them);
  Rank rkUs, rkThem;
  File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));

  for (File f = kf - File(1); f <= kf + File(1); ++f)
  {
      b = ourPawns & file_bb(f);
      rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
      safety -= ShelterWeakness[rkUs];

      b  = theirPawns & file_bb(f);
      rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
      safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
  }

  return safety;
}
Пример #11
0
void MovePicker::score() {

  static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");

  for (auto& m : *this)
      if (Type == CAPTURES)
      {
#ifdef ATOMIC
          if (pos.is_atomic())
              m.value = pos.see<ATOMIC_VARIANT>(m);
          else
#endif
#ifdef RACE
          if (pos.is_race())
              m.value =  PieceValue[pos.variant()][MG][pos.piece_on(to_sq(m))]
                       - Value(200 * relative_rank(BLACK, to_sq(m)))
                       + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))] / 8;
          else
#endif
          m.value =  PieceValue[pos.variant()][MG][pos.piece_on(to_sq(m))]
                   + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))] / 8;
      }
      else if (Type == QUIETS)
      {
          m.value =  (*mainHistory)[pos.side_to_move()][from_to(m)]
                   + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
                   + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
                   + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)];
#ifdef ANTI
          if (pos.is_anti() && pos.attackers_to(to_sq(m), pos.pieces() ^ from_sq(m)) & pos.pieces(~pos.side_to_move()))
          {
              m.value += (1 << 28);
              if (!(pos.attackers_to(from_sq(m)) & pos.pieces(~pos.side_to_move())))
                  m.value += (1 << 27);
          }
#endif
      }
      else // Type == EVASIONS
      {
          if (pos.capture(m))
              m.value =  PieceValue[pos.variant()][MG][pos.piece_on(to_sq(m))]
                       - Value(type_of(pos.moved_piece(m)));
          else
              m.value =  (*mainHistory)[pos.side_to_move()][from_to(m)]
                       + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
                       - (1 << 28);
      }
}
Пример #12
0
ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {

  assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
  assert(pos.piece_count(strongerSide, BISHOP) == 1);
  assert(pos.piece_count(strongerSide, PAWN) >= 1);

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

  Bitboard pawns = pos.pieces(PAWN, strongerSide);
  File pawnFile = square_file(pos.piece_list(strongerSide, PAWN, 0));

  // All pawns are on a single rook file ?
  if (   (pawnFile == FILE_A || pawnFile == FILE_H)
      && (pawns & ~file_bb(pawnFile)) == EmptyBoardBB)
  {
      Square bishopSq = pos.piece_list(strongerSide, BISHOP, 0);
      Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
      Square kingSq = pos.king_square(weakerSide);

      if (  !same_color_squares(queeningSq, bishopSq)
          && file_distance(square_file(kingSq), pawnFile) <= 1)
      {
          // The bishop has the wrong color, and the defending king is on the
          // file of the pawn(s) or the neighboring file. Find the rank of the
          // frontmost pawn.
          Rank rank;
          if (strongerSide == WHITE)
          {
              for (rank = RANK_7; (rank_bb(rank) & pawns) == EmptyBoardBB; rank--) {}
              assert(rank >= RANK_2 && rank <= RANK_7);
          }
          else
          {
              for (rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++) {}
              rank = Rank(rank ^ 7);  // HACK to get the relative rank
              assert(rank >= RANK_2 && rank <= RANK_7);
          }
          // If the defending king has distance 1 to the promotion square or
          // is placed somewhere in front of the pawn, it's a draw.
          if (   square_distance(kingSq, queeningSq) <= 1
              || relative_rank(strongerSide, kingSq) >= rank)
              return SCALE_FACTOR_ZERO;
      }
  }
  return SCALE_FACTOR_NONE;
}
Пример #13
0
void MovePicker::score<CAPTURES>() {
  // Winning and equal captures in the main search are ordered by MVV.
  // Suprisingly, this appears to perform slightly better than SEE based
  // move ordering. The reason is probably that in a position with a winning
  // capture, capturing a valuable (but sufficiently defended) piece
  // first usually doesn't hurt. The opponent will have to recapture, and
  // the hanging piece will still be hanging (except in the unusual cases
  // where it is possible to recapture with the hanging piece). Exchanging
  // big pieces before capturing a hanging piece probably helps to reduce
  // the subtree size.
  // In main search we want to push captures with negative SEE values to the
  // badCaptures[] array, but instead of doing it now we delay until the move
  // has been picked up in pick_move_from_list(). This way we save some SEE
  // calls in case we get a cutoff.
  for (auto& m : *this)
      m.value =  PieceValue[MG][pos.piece_on(to_sq(m))]
               - Value(200 * relative_rank(pos.side_to_move(), to_sq(m)));
}
Пример #14
0
Score Entry::do_king_safety(const Position& pos, Square ksq) {

    kingSquares[Us] = ksq;
    castlingRights[Us] = pos.can_castle(Us);
    minKPdistance[Us] = 0;

    Bitboard pawns = pos.pieces(Us, PAWN);
    if (pawns)
        while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}

    if (relative_rank(Us, ksq) > RANK_4)
        return make_score(0, -16 * minKPdistance[Us]);

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

    // If we can castle use the bonus after the castling if it is bigger
    if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right))
        bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));

    if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
        bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));

    return make_score(bonus, -16 * minKPdistance[Us]);
}
Пример #15
0
Value Eval::evaluate(const Position& pos) {

  assert(!pos.checkers());

  EvalInfo ei;
  Score score, mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO };

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

  // Probe the material hash table
  ei.me = Material::probe(pos);
  score += ei.me->imbalance();

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

  // Probe the pawn hash table
  ei.pi = Pawns::probe(pos);
  score += ei.pi->pawns_score();

  // Initialize attack and king safety bitboards
  ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0;
  eval_init<WHITE>(pos, ei);
  eval_init<BLACK>(pos, ei);

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

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

  // Evaluate all pieces but king and pawns
  score += evaluate_pieces<DoTrace>(pos, ei, mobility, mobilityArea);
  score += mobility[WHITE] - mobility[BLACK];

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

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

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

  // If both sides have only pawns, score for potential unstoppable pawns
  if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK))
  {
      Bitboard b;
      if ((b = ei.pi->passed_pawns(WHITE)) != 0)
          score += Unstoppable * int(relative_rank(WHITE, frontmost_sq(WHITE, b)));

      if ((b = ei.pi->passed_pawns(BLACK)) != 0)
          score -= Unstoppable * int(relative_rank(BLACK, frontmost_sq(BLACK, b)));
  }

  // Evaluate space for both sides, only during opening
  if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 12222)
      score +=  evaluate_space<WHITE>(pos, ei)
              - evaluate_space<BLACK>(pos, ei);

  // Evaluate position potential for the winning side
  score += evaluate_initiative(pos, ei.pi->pawn_asymmetry(), eg_value(score));

  // Evaluate scale factor for the winning side
  ScaleFactor sf = evaluate_scale_factor(pos, ei, eg_value(score));

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

  v /= int(PHASE_MIDGAME);

  // In case of tracing add all remaining individual evaluation terms
  if (DoTrace)
  {
      Trace::add(MATERIAL, pos.psq_score());
      Trace::add(IMBALANCE, ei.me->imbalance());
      Trace::add(PAWN, ei.pi->pawns_score());
      Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
      Trace::add(SPACE, evaluate_space<WHITE>(pos, ei)
                      , evaluate_space<BLACK>(pos, ei));
      Trace::add(TOTAL, score);
  }

  return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; // Side to move point of view
}
Пример #16
0
ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {

  assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
  assert(pos.piece_count(strongerSide, BISHOP) == 1);
  assert(pos.piece_count(strongerSide, PAWN) == 2);
  assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
  assert(pos.piece_count(weakerSide, BISHOP) == 1);
  assert(pos.piece_count(weakerSide, PAWN) == 0);

  Square wbsq = pos.piece_list(strongerSide, BISHOP, 0);
  Square bbsq = pos.piece_list(weakerSide, BISHOP, 0);

  if (same_color_squares(wbsq, bbsq))
      // Not opposite-colored bishops, no scaling
      return SCALE_FACTOR_NONE;

  Square ksq = pos.king_square(weakerSide);
  Square psq1 = pos.piece_list(strongerSide, PAWN, 0);
  Square psq2 = pos.piece_list(strongerSide, PAWN, 1);
  Rank r1 = square_rank(psq1);
  Rank r2 = square_rank(psq2);
  Square blockSq1, blockSq2;

  if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
  {
      blockSq1 = psq1 + pawn_push(strongerSide);
      blockSq2 = make_square(square_file(psq2), square_rank(psq1));
  }
  else
  {
      blockSq1 = psq2 + pawn_push(strongerSide);
      blockSq2 = make_square(square_file(psq1), square_rank(psq2));
  }

  switch (file_distance(psq1, psq2))
  {
  case 0:
    // Both pawns are on the same file. Easy draw if defender firmly controls
    // some square in the frontmost pawn's path.
    if (   square_file(ksq) == square_file(blockSq1)
        && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1)
        && !same_color_squares(ksq, wbsq))
        return SCALE_FACTOR_ZERO;
    else
        return SCALE_FACTOR_NONE;

  case 1:
    // Pawns on neighboring files. Draw if defender firmly controls the square
    // in front of the frontmost pawn's path, and the square diagonally behind
    // this square on the file of the other pawn.
    if (   ksq == blockSq1
        && !same_color_squares(ksq, wbsq)
        && (   bbsq == blockSq2
            || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
            || rank_distance(r1, r2) >= 2))
        return SCALE_FACTOR_ZERO;

    else if (   ksq == blockSq2
             && !same_color_squares(ksq, wbsq)
             && (   bbsq == blockSq1
                 || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
        return SCALE_FACTOR_ZERO;
    else
        return SCALE_FACTOR_NONE;

  default:
    // The pawns are not on the same file or adjacent files. No scaling.
    return SCALE_FACTOR_NONE;
  }
}