Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 16 Nov 2019 20:00:42 +0000 (21:00 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 16 Nov 2019 20:00:42 +0000 (21:00 +0100)
bench: 4552352

15 files changed:
1  2 
src/Makefile
src/bitbase.cpp
src/bitboard.h
src/endgame.cpp
src/evaluate.cpp
src/movegen.cpp
src/pawns.cpp
src/position.cpp
src/position.h
src/psqt.cpp
src/search.cpp
src/syzygy/tbprobe.cpp
src/thread.cpp
src/thread.h
src/types.h

diff --cc src/Makefile
Simple merge
diff --cc src/bitbase.cpp
Simple merge
diff --cc src/bitboard.h
@@@ -162,9 -119,12 +162,15 @@@ inline Bitboard  operator^( Bitboard  b
  inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
  inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
  
 +inline Bitboard  operator-( Bitboard  b, Square s) { return b & ~square_bb(s); }
 +inline Bitboard& operator-=(Bitboard& b, Square s) { return b &= ~square_bb(s); }
 +
+ inline Bitboard  operator&(Square s, Bitboard b) { return b & s; }
+ inline Bitboard  operator|(Square s, Bitboard b) { return b | s; }
+ inline Bitboard  operator^(Square s, Bitboard b) { return b ^ s; }
+ inline Bitboard  operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); }
  constexpr bool more_than_one(Bitboard b) {
    return b & (b - 1);
  }
@@@ -458,13 -315,7 +464,13 @@@ inline Square lsb(Bitboard b) 
  
  inline Square msb(Bitboard b) {
    assert(b);
 -  return Square(63 ^ __builtin_clzll(b));
 +#ifdef LARGEBOARDS
 +  if (b >> 64)
-       return Square(SQUARE_BIT_MASK ^ __builtin_clzll(b >> 64));
-   return Square(SQUARE_BIT_MASK ^ (__builtin_clzll(b) + 64));
++      return Square(int(SQUARE_BIT_MASK) ^ __builtin_clzll(b >> 64));
++  return Square(int(SQUARE_BIT_MASK) ^ (__builtin_clzll(b) + 64));
 +#else
-   return Square(SQUARE_BIT_MASK ^ __builtin_clzll(b));
++  return Square(int(SQUARE_BIT_MASK) ^ __builtin_clzll(b));
 +#endif
  }
  
  #elif defined(_MSC_VER)  // MSVC
diff --cc src/endgame.cpp
@@@ -574,8 -413,8 +574,8 @@@ ScaleFactor Endgame<KQKRPs>::operator()
        &&  relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
        &&  relative_rank(weakSide, rsq) == RANK_3
        && (  pos.pieces(weakSide, PAWN)
-           & pos.attacks_from<KING>(weakSide, kingSq)
-           & pos.attacks_from<PAWN>(strongSide, rsq)))
 -          & pos.attacks_from<KING>(kingSq)
++          & pos.attacks_from<KING>(kingSq, weakSide)
+           & pos.attacks_from<PAWN>(rsq, strongSide)))
            return SCALE_FACTOR_DRAW;
  
    return SCALE_FACTOR_NONE;
@@@ -856,14 -695,14 +856,14 @@@ ScaleFactor Endgame<KBPPKB>::operator()
      if (   ksq == blockSq1
          && opposite_colors(ksq, wbsq)
          && (   bbsq == blockSq2
-             || (pos.attacks_from<BISHOP>(weakSide, blockSq2) & pos.pieces(weakSide, BISHOP))
 -            || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
++            || (pos.attacks_from<BISHOP>(blockSq2, weakSide) & pos.pieces(weakSide, BISHOP))
              || distance<Rank>(psq1, psq2) >= 2))
          return SCALE_FACTOR_DRAW;
  
      else if (   ksq == blockSq2
               && opposite_colors(ksq, wbsq)
               && (   bbsq == blockSq1
-                  || (pos.attacks_from<BISHOP>(weakSide, blockSq1) & pos.pieces(weakSide, BISHOP))))
 -                 || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakSide, BISHOP))))
++                 || (pos.attacks_from<BISHOP>(blockSq1, weakSide) & pos.pieces(weakSide, BISHOP))))
          return SCALE_FACTOR_DRAW;
      else
          return SCALE_FACTOR_NONE;
@@@ -931,7 -770,7 +931,7 @@@ ScaleFactor Endgame<KNPKB>::operator()(
  
    // King needs to get close to promoting pawn to prevent knight from blocking.
    // Rules for this are very tricky, so just approximate.
-   if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(weakSide, bishopSq))
 -  if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
++  if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq, weakSide))
        return ScaleFactor(distance(weakKingSq, pawnSq));
  
    return SCALE_FACTOR_NONE;
@@@ -133,10 -125,12 +133,15 @@@ namespace 
    constexpr Score PassedRank[RANK_NB] = {
      S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
    };
+   
+   // OutpostRank[Rank] contains a bonus according to the rank of the outpost
+   constexpr Score OutpostRank[RANK_NB] = {
+     S(0, 0), S(0, 0), S(0, 0), S(28, 18), S(30, 24), S(32, 19)
+   };
  
 +  // KingProximity contains a penalty according to distance from king
 +  constexpr Score KingProximity = S(1, 3);
 +
    // Assorted bonuses and penalties
    constexpr Score BishopPawns        = S(  3,  7);
    constexpr Score CorneredBishop     = S( 50, 50);
    void Evaluation<T>::initialize() {
  
      constexpr Color     Them = (Us == WHITE ? BLACK : WHITE);
-     constexpr Direction Up   = (Us == WHITE ? NORTH : SOUTH);
-     constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+     constexpr Direction Up   = pawn_push(Us);
+     constexpr Direction Down = -Up;
 -    constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
 +    Bitboard LowRanks = rank_bb(relative_rank(Us, RANK_2, pos.max_rank())) | rank_bb(relative_rank(Us, RANK_3, pos.max_rank()));
  
 -    const Square ksq = pos.square<KING>(Us);
 +    const Square ksq = pos.count<KING>(Us) ? pos.square<KING>(Us) : SQ_NONE;
  
      Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
  
  
      // Squares occupied by those pawns, by our king or queen or controlled by
      // enemy pawns are excluded from the mobility area.
 -    mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them));
 +    if (pos.must_capture())
 +        mobilityArea[Us] = AllSquares;
 +    else
 +        mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them) | shift<Down>(pos.pieces(Them, SHOGI_PAWN)));
  
      // Initialize attackedBy[] for king and pawns
-     attackedBy[Us][KING] = pos.count<KING>(Us) ? pos.attacks_from<KING>(Us, ksq) : Bitboard(0);
 -    attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
++    attackedBy[Us][KING] = pos.count<KING>(Us) ? pos.attacks_from<KING>(ksq, Us) : Bitboard(0);
      attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
 -    attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
 -    attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
 +    attackedBy[Us][SHOGI_PAWN] = shift<Up>(pos.pieces(Us, SHOGI_PAWN));
 +    attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN] | attackedBy[Us][SHOGI_PAWN];
 +    attackedBy2[Us]            =  (attackedBy[Us][KING] & attackedBy[Us][PAWN])
 +                                | (attackedBy[Us][KING] & attackedBy[Us][SHOGI_PAWN])
 +                                | (attackedBy[Us][PAWN] & attackedBy[Us][SHOGI_PAWN])
 +                                | dblAttackByPawn;
  
      // Init our king safety tables
-     kingRing[Us] = attackedBy[Us][KING];
-     if (pos.count<KING>(Us) && relative_rank(Us, ksq, pos.max_rank()) == RANK_1)
-         kingRing[Us] |= shift<Up>(kingRing[Us]);
-     if (pos.count<KING>(Us) && file_of(ksq) == pos.max_file())
-         kingRing[Us] |= shift<WEST>(kingRing[Us]);
-     else if (pos.count<KING>(Us) && file_of(ksq) == FILE_A)
-         kingRing[Us] |= shift<EAST>(kingRing[Us]);
 -    Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G),
 -                           clamp(rank_of(ksq), RANK_2, RANK_7));
 -    kingRing[Us] = s | PseudoAttacks[KING][s];
++    if (!pos.count<KING>(Us))
++        kingRing[Us] = Bitboard(0);
++    else
++    {
++    Square s = make_square(clamp(file_of(ksq), FILE_B, File(pos.max_file() - 1)),
++                           clamp(rank_of(ksq), RANK_2, Rank(pos.max_rank() - 1)));
++    kingRing[Us] = s | PseudoAttacks[Us][KING][s];
++    }
  
      kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
      kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
  
  
    // Evaluation::pieces() scores pieces of a given color and type
 -  template<Tracing T> template<Color Us, PieceType Pt>
 -  Score Evaluation<T>::pieces() {
 +  template<Tracing T> template<Color Us>
 +  Score Evaluation<T>::pieces(PieceType Pt) {
  
      constexpr Color     Them = (Us == WHITE ? BLACK : WHITE);
-     constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+     constexpr Direction Down = -pawn_push(Us);
      constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
                                                     : Rank5BB | Rank4BB | Rank3BB);
 -    const Square* pl = pos.squares<Pt>(Us);
 +    const Square* pl = pos.squares(Us, Pt);
  
      Bitboard b, bb;
      Score score = SCORE_ZERO;
      int kingFlankAttacks = popcount(b1) + popcount(b2);
  
      kingDanger +=        kingAttackersCount[Them] * kingAttackersWeight[Them]
 -                 + 185 * popcount(kingRing[Us] & weak)
 +                 +       kingAttackersCountInHand[Them] * kingAttackersWeight[Them]
 +                 +       kingAttackersCount[Them] * kingAttackersWeightInHand[Them]
-                  + 69  * kingAttacksCount[Them] * (2 + 8 * pos.check_counting() + pos.captures_to_hand()) / 2
 +                 + 185 * popcount(kingRing[Us] & weak) * (1 + pos.captures_to_hand() + pos.check_counting())
-                  - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
-                  -  35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
                   + 148 * popcount(unsafeChecks)
                   +  98 * popcount(pos.blockers_for_king(Us))
 -                 +  69 * kingAttacksCount[Them]
++                 +  69 * kingAttacksCount[Them] * (2 + 8 * pos.check_counting() + pos.captures_to_hand()) / 2
+                  +   3 * kingFlankAttacks * kingFlankAttacks / 8
+                  +       mg_value(mobility[Them] - mobility[Us])
 -                 - 873 * !pos.count<QUEEN>(Them)
 +                 - 873 * !(pos.major_pieces(Them) || pos.captures_to_hand() || pos.xiangqi_general()) / (1 + pos.check_counting())
+                  - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
+                  -  35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
                   -   6 * mg_value(score) / 8
-                  +       mg_value(mobility[Them] - mobility[Us])
-                  +   3 * kingFlankAttacks * kingFlankAttacks / 8
                   -   7;
  
      // Transform the kingDanger units into a Score, and subtract it from the evaluation
          Square s = pos.square<QUEEN>(Them);
          safe = mobilityArea[Us] & ~stronglyProtected;
  
-         b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(Us, s);
 -        b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(s);
++        b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(s, Us);
  
          score += KnightOnQueen * popcount(b & safe);
  
-         b =  (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(Us, s))
-            | (attackedBy[Us][ROOK  ] & pos.attacks_from<ROOK  >(Us, s));
 -        b =  (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s))
 -           | (attackedBy[Us][ROOK  ] & pos.attacks_from<ROOK  >(s));
++        b =  (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s, Us))
++           | (attackedBy[Us][ROOK  ] & pos.attacks_from<ROOK  >(s, Us));
  
          score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
      }
    Score Evaluation<T>::passed() const {
  
      constexpr Color     Them = (Us == WHITE ? BLACK : WHITE);
-     constexpr Direction Up   = (Us == WHITE ? NORTH : SOUTH);
+     constexpr Direction Up   = pawn_push(Us);
  
      auto king_proximity = [&](Color c, Square s) {
 -      return std::min(distance(pos.square<KING>(c), s), 5);
 +      return pos.count<KING>(c) ? std::min(distance(pos.square<KING>(c), s), 5) : 5;
      };
  
      Bitboard b, bb, squaresToQueen, unsafeSquares;
  
          assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
  
 -        int r = relative_rank(Us, s);
 +        int r = std::max(RANK_8 - std::max(pos.promotion_rank() - relative_rank(Us, s, pos.max_rank()), 0), 0);
-         File f = file_of(s);
  
          Score bonus = PassedRank[r];
  
              || (pos.pieces(PAWN) & (s + Up)))
              bonus = bonus / 2;
  
-         score += bonus - PassedFile * std::min(f, File(pos.max_file() - f));
 -        score += bonus - PassedFile * map_to_queenside(file_of(s));
++        score += bonus - PassedFile * std::min(file_of(s), File(pos.max_file() - file_of(s)));
 +    }
 +
 +    // Scale by maximum promotion piece value
 +    Value maxMg = VALUE_ZERO, maxEg = VALUE_ZERO;
 +    for (PieceType pt : pos.promotion_piece_types())
 +    {
 +        maxMg = std::max(maxMg, PieceValue[MG][pt]);
 +        maxEg = std::max(maxEg, PieceValue[EG][pt]);
 +    }
 +    score = make_score(mg_value(score) * int(maxMg - PawnValueMg) / (QueenValueMg - PawnValueMg),
 +                       eg_value(score) * int(maxEg - PawnValueEg) / (QueenValueEg - PawnValueEg));
 +
 +    // Score passed shogi pawns
 +    const Square* pl = pos.squares(Us, SHOGI_PAWN);
 +    Square s;
 +
 +    PieceType pt = pos.promoted_piece_type(SHOGI_PAWN);
 +    if (pt != NO_PIECE_TYPE)
 +    {
 +        while ((s = *pl++) != SQ_NONE)
 +        {
 +            if ((pos.pieces(Them, SHOGI_PAWN) & forward_file_bb(Us, s)) || relative_rank(Us, s, pos.max_rank()) == pos.max_rank())
 +                continue;
 +
 +            Square blockSq = s + Up;
 +            int d = std::max(pos.promotion_rank() - relative_rank(Us, s, pos.max_rank()), 1);
 +            d += !!(attackedBy[Them][ALL_PIECES] & ~attackedBy2[Us] & blockSq);
 +            score += make_score(PieceValue[MG][pt], PieceValue[EG][pt]) / (4 * d * d);
 +        }
      }
  
      if (T)
              && pos.non_pawn_material() == 2 * BishopValueMg)
              sf = 16 + 4 * pe->passed_count();
          else
 -            sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide));
 +            sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide)));
  
-         sf = std::max(0, sf - (pos.rule50_count() - 12) / 4  );
+         sf = std::max(0, sf - (pos.rule50_count() - 12) / 4);
      }
  
      return ScaleFactor(sf);
diff --cc src/movegen.cpp
@@@ -84,23 -54,17 +84,25 @@@ namespace 
  
      // Compute some compile time parameters relative to the white side
      constexpr Color     Them     = (Us == WHITE ? BLACK      : WHITE);
-     constexpr Direction Up       = (Us == WHITE ? NORTH      : SOUTH);
-     constexpr Direction Down     = (Us == WHITE ? SOUTH      : NORTH);
 -    constexpr Bitboard  TRank7BB = (Us == WHITE ? Rank7BB    : Rank2BB);
 -    constexpr Bitboard  TRank3BB = (Us == WHITE ? Rank3BB    : Rank6BB);
+     constexpr Direction Up       = pawn_push(Us);
++    constexpr Direction Down     = -pawn_push(Us);
      constexpr Direction UpRight  = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
      constexpr Direction UpLeft   = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
  
 -    const Square ksq = pos.square<KING>(Them);
++    const Square ksq = pos.count<KING>(Them) ? pos.square<KING>(Them) : SQ_NONE;
++
++    Bitboard TRank8BB = pos.mandatory_pawn_promotion() ? rank_bb(relative_rank(Us, pos.promotion_rank(), pos.max_rank()))
++                                                       : promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank());
++    Bitboard TRank7BB = shift<Down>(TRank8BB);
 +    // Define squares a pawn can pass during a double step
 +    Bitboard  TRank3BB = rank_bb(relative_rank(Us, Rank(pos.double_step_rank() + 1), pos.max_rank()));
 +    if (pos.first_rank_double_steps())
 +        TRank3BB |= rank_bb(relative_rank(Us, RANK_2, pos.max_rank()));
 +
      Bitboard emptySquares;
  
-     Bitboard TRank8BB = pos.mandatory_pawn_promotion() ? rank_bb(relative_rank(Us, pos.promotion_rank(), pos.max_rank()))
-                                                        : promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank());
-     Bitboard TRank7BB = shift<Down>(TRank8BB);
      Bitboard pawnsOn7    = pos.pieces(Us, PAWN) &  TRank7BB;
 -    Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
 +    Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & (pos.mandatory_pawn_promotion() ? ~TRank7BB : AllSquares);
  
      Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
                          Type == CAPTURES ? target : pos.pieces(Them));
              b2 &= target;
          }
  
 -        if (Type == QUIET_CHECKS)
 +        if (Type == QUIET_CHECKS && pos.count<KING>(Them))
          {
-             Square ksq = pos.square<KING>(Them);
-             b1 &= pos.attacks_from<PAWN>(Them, ksq);
-             b2 &= pos.attacks_from<PAWN>(Them, ksq);
+             b1 &= pos.attacks_from<PAWN>(ksq, Them);
+             b2 &= pos.attacks_from<PAWN>(ksq, Them);
  
              // Add pawn pushes which give discovered check. This is possible only
              // if the pawn is not on the same file as the enemy king, because we
      constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
  
      moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
 -    moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
 -    moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
 -    moveList = generate_moves<  ROOK, Checks>(pos, moveList, Us, target);
 -    moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
 -
 -    if (Type != QUIET_CHECKS && Type != EVASIONS)
 +    for (PieceType pt = PieceType(PAWN + 1); pt < KING; ++pt)
 +        moveList = generate_moves<Checks>(pos, moveList, Us, pt, target);
 +    // generate drops
 +    if (pos.piece_drops() && Type != CAPTURES && pos.count_in_hand(Us, ALL_PIECES))
 +        for (PieceType pt = PAWN; pt <= KING; ++pt)
 +            moveList = generate_drops<Us, Checks>(pos, moveList, pt, target & ~pos.pieces(~Us));
 +
 +    if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count<KING>(Us))
      {
          Square ksq = pos.square<KING>(Us);
-         Bitboard b = pos.attacks_from<KING>(Us, ksq) & target;
 -        Bitboard b = pos.attacks_from<KING>(ksq) & target;
++        Bitboard b = pos.attacks_from<KING>(ksq, Us) & target;
          while (b)
 -            *moveList++ = make_move(ksq, pop_lsb(&b));
 +            moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, ksq, pop_lsb(&b));
  
          if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
          {
@@@ -450,18 -319,7 +450,18 @@@ ExtMove* generate<EVASIONS>(const Posit
    Color us = pos.side_to_move();
    Square ksq = pos.square<KING>(us);
    Bitboard sliderAttacks = 0;
 -  Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
 +  Bitboard sliders = pos.checkers();
 +
 +  // Consider all evasion moves for special pieces
 +  if (sliders & (pos.pieces(CANNON, BANNER) | pos.pieces(HORSE, ELEPHANT)))
 +  {
 +      Bitboard target = pos.board_bb() & ~pos.pieces(us);
-       Bitboard b = pos.attacks_from<KING>(us, ksq) & target;
++      Bitboard b = pos.attacks_from<KING>(ksq, us) & target;
 +      while (b)
 +          moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
 +      return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
 +                         : generate_all<BLACK, EVASIONS>(pos, moveList, target);
 +  }
  
    // Find all the squares attacked by slider checkers. We will remove them from
    // the king evasions in order to skip known illegal moves, which avoids any
    }
  
    // Generate evasions for king, capture and non capture moves
-   Bitboard b = pos.attacks_from<KING>(us, ksq) & ~pos.pieces(us) & ~sliderAttacks;
 -  Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
++  Bitboard b = pos.attacks_from<KING>(ksq, us) & ~pos.pieces(us) & ~sliderAttacks;
    while (b)
 -      *moveList++ = make_move(ksq, pop_lsb(&b));
 +      moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
  
    if (more_than_one(pos.checkers()))
        return moveList; // Double check, only a king move can save the day
diff --cc src/pawns.cpp
@@@ -107,9 -107,8 +107,9 @@@ namespace 
  
          // A pawn is backward when it is behind all pawns of the same color on
          // the adjacent files and cannot safely advance.
 -        backward =  !(neighbours & forward_ranks_bb(Them, s + Up))
 -                  && (leverPush | blocked);
 +        backward =   is_ok(s + Up)
 +                  && !(neighbours & forward_ranks_bb(Them, s + Up))
-                   && (stoppers & (leverPush | blocked));
++                  && (stoppers & blocked);
  
          // Compute additional span if pawn is not backward nor blocked
          if (!backward && !blocked)
@@@ -999,20 -618,18 +999,20 @@@ bool Position::pseudo_legal(const Move 
    {
        // We have already handled promotion moves, so destination
        // cannot be on the 8th/1st rank.
 -      if ((Rank8BB | Rank1BB) & to)
 +      if (mandatory_pawn_promotion() && rank_of(to) == relative_rank(us, promotion_rank(), max_rank()))
            return false;
  
-       if (   !(attacks_from<PAWN>(us, from) & pieces(~us) & to) // Not a capture
+       if (   !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
            && !((from + pawn_push(us) == to) && empty(to))       // Not a single push
            && !(   (from + 2 * pawn_push(us) == to)              // Not a double push
 -               && (rank_of(from) == relative_rank(us, RANK_2))
 +               && (rank_of(from) == relative_rank(us, double_step_rank(), max_rank())
 +                   || (first_rank_double_steps() && rank_of(from) == relative_rank(us, RANK_1, max_rank())))
                 && empty(to)
 -               && empty(to - pawn_push(us))))
 +               && empty(to - pawn_push(us))
 +               && double_step_enabled()))
            return false;
    }
 -  else if (!(attacks_from(type_of(pc), from) & to))
 +  else if (!((capture(m) ? attacks_from(us, type_of(pc), from) : moves_from(us, type_of(pc), from)) & to))
        return false;
  
    // Evasions generator already takes care to avoid some kind of illegal moves
@@@ -1292,9 -825,8 +1292,9 @@@ void Position::do_move(Move m, StateInf
    if (type_of(pc) == PAWN)
    {
        // Set en-passant square if the moved pawn can be captured
 -      if (   (int(to) ^ int(from)) == 16
 +      if (   std::abs(int(to) - int(from)) == 2 * NORTH
 +          && relative_rank(us, rank_of(from), max_rank()) == double_step_rank()
-           && (attacks_from<PAWN>(us, to - pawn_push(us)) & pieces(them, PAWN)))
+           && (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
        {
            st->epSquare = to - pawn_push(us);
            k ^= Zobrist::enpassant[file_of(st->epSquare)];
diff --cc src/position.h
@@@ -194,13 -112,11 +194,13 @@@ public
  
    // Attacks to/from a given square
    Bitboard attackers_to(Square s) const;
 +  Bitboard attackers_to(Square s, Color c) const;
    Bitboard attackers_to(Square s, Bitboard occupied) const;
 -  Bitboard attacks_from(PieceType pt, Square s) const;
 -  template<PieceType> Bitboard attacks_from(Square s) const;
 +  Bitboard attackers_to(Square s, Bitboard occupied, Color c) const;
 +  Bitboard attacks_from(Color c, PieceType pt, Square s) const;
-   template<PieceType> Bitboard attacks_from(Color c, Square s) const;
+   template<PieceType> Bitboard attacks_from(Square s, Color c) const;
 -  Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
 +  Bitboard moves_from(Color c, PieceType pt, Square s) const;
 +  Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners, Color c) const;
  
    // Properties of moves
    bool legal(Move m) const;
@@@ -777,16 -285,21 +777,16 @@@ inline Square Position::castling_rook_s
  }
  
  template<PieceType Pt>
- inline Bitboard Position::attacks_from(Color c, Square s) const {
 -inline Bitboard Position::attacks_from(Square s) const {
 -  static_assert(Pt != PAWN, "Pawn attacks need color");
 -
 -  return  Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
 -        : Pt == QUEEN  ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
 -        : PseudoAttacks[Pt][s];
++inline Bitboard Position::attacks_from(Square s, Color c) const {
 +  return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, Pt);
  }
  
 -template<>
 -inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
 -  return PawnAttacks[c][s];
 +inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const {
 +  return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
  }
  
 -inline Bitboard Position::attacks_from(PieceType pt, Square s) const {
 -  return attacks_bb(pt, s, byTypeBB[ALL_PIECES]);
 +inline Bitboard Position::moves_from(Color c, PieceType pt, Square s) const {
 +  return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
  }
  
  inline Bitboard Position::attackers_to(Square s) const {
@@@ -925,7 -430,7 +925,7 @@@ inline void Position::move_piece(Piece 
  
    // index[from] is not updated and becomes stale. This works as long as index[]
    // is accessed just by known occupied squares.
-   Bitboard fromTo = square_bb(from) ^ square_bb(to);
 -  Bitboard fromTo = from | to;
++  Bitboard fromTo = square_bb(from) ^ to; // from == to needs to cancel out
    byTypeBB[ALL_PIECES] ^= fromTo;
    byTypeBB[type_of(pc)] ^= fromTo;
    byColorBB[color_of(pc)] ^= fromTo;
diff --cc src/psqt.cpp
@@@ -91,18 -77,17 +91,18 @@@ constexpr Score Bonus[PIECE_TYPE_NB][RA
     { S(-4,-38), S(10,-18), S( 6,-12), S( 8,  1) },
     { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
     { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
 -  },
 -  { // King
 +  }
 +};
 +
 +constexpr Score KingBonus[RANK_NB][int(FILE_NB) / 2] = {
-    { S(272,  0), S(325, 41), S(273, 80), S(190, 93) },
-    { S(277, 57), S(305, 98), S(241,138), S(183,131) },
-    { S(198, 86), S(253,138), S(168,165), S(120,173) },
-    { S(169,103), S(191,152), S(136,168), S(108,169) },
-    { S(145, 98), S(176,166), S(112,197), S( 69,194) },
-    { S(122, 87), S(159,164), S( 85,174), S( 36,189) },
-    { S( 87, 40), S(120, 99), S( 64,128), S( 25,141) },
-    { S( 64,  5), S( 87, 60), S( 49, 75), S(  0, 75) }
+    { S(271,  1), S(327, 45), S(270, 85), S(192, 76) },
+    { S(278, 53), S(303,100), S(230,133), S(174,135) },
+    { S(195, 88), S(258,130), S(169,169), S(120,175) },
+    { S(164,103), S(190,156), S(138,172), S( 98,172) },
+    { S(154, 96), S(179,166), S(105,199), S( 70,199) },
+    { S(123, 92), S(145,172), S( 81,184), S( 31,191) },
+    { S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
+    { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
 -  }
  };
  
  constexpr Score PBonus[RANK_NB][FILE_NB] =
diff --cc src/search.cpp
@@@ -680,9 -659,9 +681,9 @@@ namespace 
      // search to overwrite a previous full search TT value, so we use a different
      // position key in case of an excluded move.
      excludedMove = ss->excludedMove;
 -    posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash
 +    posKey = pos.key() ^ (Key(excludedMove) << 16); // Isn't a very good hash
      tte = TT.probe(posKey, ttHit);
-     ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
+     ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
      ttMove =  rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
              : ttHit    ? tte->move() : MOVE_NONE;
      ttPv = PvNode || (ttHit && tte->is_pv());
      }
  
      // Step 11. Internal iterative deepening (~2 Elo)
 -    if (depth >= 7 && !ttMove)
 +    if (depth >= (7 - 2 * pos.captures_to_hand()) && !ttMove)
      {
 -        search<NT>(pos, ss, alpha, beta, depth - 7, cutNode);
 +        search<NT>(pos, ss, alpha, beta, depth - (7 - 2 * pos.captures_to_hand()), cutNode);
  
          tte = TT.probe(posKey, ttHit);
-         ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
+         ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
          ttMove = ttHit ? tte->move() : MOVE_NONE;
      }
  
@@@ -1072,13 -1022,8 +1065,9 @@@ moves_loop: // When in check, search st
  
            if (   !captureOrPromotion
                && !givesCheck
 -              && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg))
 +              && (!pos.must_capture() || !pos.attackers_to(to_sq(move), ~us))
-               && (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg || pos.count<ALL_PIECES>(us) == pos.count<PAWN>(us)))
++              && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg || pos.count<ALL_PIECES>(us) == pos.count<PAWN>(us)))
            {
-               // Move count based pruning
-               if (moveCountPruning)
-                   continue;
                // Reduced depth of the next LMR search
                int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0);
  
  
      if (!moveCount)
          bestValue = excludedMove ? alpha
 -                   :     inCheck ? mated_in(ss->ply) : VALUE_DRAW;
 +                   :     inCheck ? pos.checkmate_value(ss->ply) : pos.stalemate_value(ss->ply);
-     else if (bestMove)
-     {
-         // Quiet best move: update move sorting heuristics
-         if (!pos.capture_or_promotion(bestMove))
-             update_quiet_stats(pos, ss, bestMove, quietsSearched, quietCount,
-                                stat_bonus(depth + (bestValue > beta + PawnValueMg)));
-         update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + 1));
  
-         // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted
-         if (   ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0]))
-             && !priorCapture)
-                 update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
+     else if (bestMove)
+         update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq,
+                          quietsSearched, quietCount, capturesSearched, captureCount, depth);
  
-     }
      // Bonus for prior countermove that caused the fail low
      else if (   (depth >= 3 || PvNode)
               && !priorCapture)
    }
  
  
-   // update_continuation_histories() updates histories of the move pairs formed
-   // by moves at ply -1, -2, and -4 with current move.
+   // update_all_stats() updates stats at the end of search() when a bestMove is found
  
-   void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
+   void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq,
+                         Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) {
  
-     for (int i : {1, 2, 4, 6})
-         if (is_ok((ss-i)->currentMove))
-             (*(ss-i)->continuationHistory)[history_slot(pc)][to] << bonus;
-   }
+     int bonus1, bonus2;
+     Color us = pos.side_to_move();
+     Thread* thisThread = pos.this_thread();
+     CapturePieceToHistory& captureHistory = thisThread->captureHistory;
+     Piece moved_piece = pos.moved_piece(bestMove);
+     PieceType captured = type_of(pos.piece_on(to_sq(bestMove)));
+     bonus1 = stat_bonus(depth + 1);
+     bonus2 = bestValue > beta + PawnValueMg ? bonus1               // larger bonus
+                                             : stat_bonus(depth);   // smaller bonus
+     if (!pos.capture_or_promotion(bestMove))
+     {
+         update_quiet_stats(pos, ss, bestMove, bonus2);
+         // Decrease all the non-best quiet moves
+         for (int i = 0; i < quietCount; ++i)
+         {
+             thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2;
+             update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bonus2);
+         }
+     }
+     else
+         captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1;
  
+     // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted
+     if (   ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0]))
+         && !pos.captured_piece())
+             update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1);
  
-   // update_capture_stats() updates move sorting heuristics when a new capture best move is found
+     // Decrease all the non-best capture moves
+     for (int i = 0; i < captureCount; ++i)
+     {
+         moved_piece = pos.moved_piece(capturesSearched[i]);
+         captured = type_of(pos.piece_on(to_sq(capturesSearched[i])));
+         captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -bonus1;
+     }
+   }
  
-   void update_capture_stats(const Position& pos, Move move,
-                             Move* captures, int captureCount, int bonus) {
  
-       CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
-       Piece moved_piece = pos.moved_piece(move);
-       PieceType captured = type_of(pos.piece_on(to_sq(move)));
+   // update_continuation_histories() updates histories of the move pairs formed
+   // by moves at ply -1, -2, and -4 with current move.
  
-       if (pos.capture_or_promotion(move))
-           captureHistory[moved_piece][to_sq(move)][captured] << bonus;
+   void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
  
-       // Decrease all the other played capture moves
-       for (int i = 0; i < captureCount; ++i)
-       {
-           moved_piece = pos.moved_piece(captures[i]);
-           captured = type_of(pos.piece_on(to_sq(captures[i])));
-           captureHistory[moved_piece][to_sq(captures[i])][captured] << -bonus;
-       }
+     for (int i : {1, 2, 4, 6})
+         if (is_ok((ss-i)->currentMove))
 -            (*(ss-i)->continuationHistory)[pc][to] << bonus;
++            (*(ss-i)->continuationHistory)[history_slot(pc)][to] << bonus;
    }
  
  
Simple merge
diff --cc src/thread.cpp
@@@ -207,10 -207,10 +207,10 @@@ void ThreadPool::start_thinking(Positio
  
    for (Thread* th : *this)
    {
-       th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0;
+       th->nodes = th->tbHits = th->nmpMinPly = 0;
        th->rootDepth = th->completedDepth = 0;
        th->rootMoves = rootMoves;
 -      th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
 +      th->rootPos.set(pos.variant(), pos.fen(), pos.is_chess960(), &setupStates->back(), th);
    }
  
    setupStates->back() = tmp;
diff --cc src/thread.h
Simple merge
diff --cc src/types.h
@@@ -311,40 -180,10 +311,40 @@@ enum Value : int 
    VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
  
    PawnValueMg   = 128,   PawnValueEg   = 213,
-   KnightValueMg = 782,   KnightValueEg = 865,
-   BishopValueMg = 830,   BishopValueEg = 918,
-   RookValueMg   = 1289,  RookValueEg   = 1378,
-   QueenValueMg  = 2529,  QueenValueEg  = 2687,
+   KnightValueMg = 781,   KnightValueEg = 854,
+   BishopValueMg = 825,   BishopValueEg = 915,
+   RookValueMg   = 1276,  RookValueEg   = 1380,
+   QueenValueMg  = 2538,  QueenValueEg  = 2682,
 +  FersValueMg              = 420,   FersValueEg              = 450,
 +  AlfilValueMg             = 330,   AlfilValueEg             = 300,
 +  FersAlfilValueMg         = 600,   FersAlfilValueEg         = 600,
 +  SilverValueMg            = 630,   SilverValueEg            = 630,
 +  AiwokValueMg             = 2500,  AiwokValueEg             = 2500,
 +  BersValueMg              = 2000,  BersValueEg              = 2000,
 +  ArchbishopValueMg        = 2200,  ArchbishopValueEg        = 2200,
 +  ChancellorValueMg        = 2300,  ChancellorValueEg        = 2600,
 +  AmazonValueMg            = 3000,  AmazonValueEg            = 3000,
 +  KnibisValueMg            = 1100,  KnibisValueEg            = 1200,
 +  BiskniValueMg            = 750,   BiskniValueEg            = 700,
 +  KnirooValueMg            = 900,   KnirooValueEg            = 900,
 +  RookniValueMg            = 900,   RookniValueEg            = 900,
 +  ShogiPawnValueMg         =  90,   ShogiPawnValueEg         = 100,
 +  LanceValueMg             = 350,   LanceValueEg             = 250,
 +  ShogiKnightValueMg       = 350,   ShogiKnightValueEg       = 300,
 +  EuroShogiKnightValueMg   = 400,   EuroShogiKnightValueEg   = 400,
 +  GoldValueMg              = 640,   GoldValueEg              = 640,
 +  DragonHorseValueMg       = 1500,  DragonHorseValueEg       = 1500,
 +  ClobberPieceValueMg      = 300,   ClobberPieceValueEg      = 300,
 +  BreakthroughPieceValueMg = 300,   BreakthroughPieceValueEg = 300,
 +  ImmobilePieceValueMg     = 100,   ImmobilePieceValueEg     = 100,
 +  CannonPieceValueMg       = 800,   CannonPieceValueEg       = 700,
 +  SoldierValueMg           = 150,   SoldierValueEg           = 300,
 +  HorseValueMg             = 500,   HorseValueEg             = 800,
 +  ElephantValueMg          = 300,   ElephantValueEg          = 300,
 +  BannerValueMg            = 3500,  BannerValueEg            = 3500,
 +  WazirValueMg             = 400,   WazirValueEg             = 400,
 +  CommonerValueMg          = 700,   CommonerValueEg          = 900,
 +  CentaurValueMg           = 1500,  CentaurValueEg           = 1500,
  
    MidgameLimit  = 15258, EndgameLimit  = 3915
  };