Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 27 Jul 2019 11:35:33 +0000 (13:35 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 27 Jul 2019 11:41:20 +0000 (13:41 +0200)
bench: 3559377

14 files changed:
1  2 
Readme.md
src/Makefile
src/bitboard.cpp
src/evaluate.cpp
src/material.cpp
src/movepick.cpp
src/pawns.cpp
src/position.cpp
src/position.h
src/psqt.cpp
src/search.cpp
src/syzygy/tbprobe.cpp
src/types.h
src/ucioption.cpp

diff --cc Readme.md
Simple merge
diff --cc src/Makefile
Simple merge
@@@ -353,67 -71,46 +353,67 @@@ void Bitboards::init() 
    for (unsigned i = 0; i < (1 << 16); ++i)
        PopCnt16[i] = std::bitset<16>(i).count();
  
 -  for (Square s = SQ_A1; s <= SQ_H8; ++s)
 -      SquareBB[s] = (1ULL << s);
 +  for (Square s = SQ_A1; s <= SQ_MAX; ++s)
 +      SquareBB[s] = make_bitboard(s);
  
 -  for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
 -      for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
 +  for (File f = FILE_A; f <= FILE_MAX; ++f)
 +      for (Rank r = RANK_1; r <= RANK_MAX; ++r)
 +          BoardSizeBB[f][r] = forward_file_bb(BLACK, make_square(f, r)) | SquareBB[make_square(f, r)] | (f > FILE_A ? BoardSizeBB[f - 1][r] : Bitboard(0));
 +
 +  for (Square s1 = SQ_A1; s1 <= SQ_MAX; ++s1)
 +      for (Square s2 = SQ_A1; s2 <= SQ_MAX; ++s2)
                SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
  
 -  int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
 +  // Piece moves
 +  std::vector<Direction> RookDirections = { NORTH,  EAST,  SOUTH,  WEST };
 +  std::vector<Direction> BishopDirections = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
 +
 +#ifdef PRECOMPUTED_MAGICS
 +  init_magics(RookTable, RookMagics, RookDirections, RookMagicInit);
 +  init_magics(BishopTable, BishopMagics, BishopDirections, BishopMagicInit);
 +#else
 +  init_magics(RookTable, RookMagics, RookDirections);
 +  init_magics(BishopTable, BishopMagics, BishopDirections);
 +#endif
  
-   for (Color c = WHITE; c <= BLACK; ++c)
+   for (Color c : { WHITE, BLACK })
 -      for (PieceType pt : { PAWN, KNIGHT, KING })
 -          for (Square s = SQ_A1; s <= SQ_H8; ++s)
 -              for (int i = 0; steps[pt][i]; ++i)
 +      for (PieceType pt = PAWN; pt <= KING; ++pt)
 +      {
 +          const PieceInfo* pi = pieceMap.find(pt)->second;
 +
 +          for (Square s = SQ_A1; s <= SQ_MAX; ++s)
 +          {
 +              for (Direction d : pi->stepsCapture)
                {
 -                  Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]);
 +                  Square to = s + Direction(c == WHITE ? d : -d);
  
 -                  if (is_ok(to) && distance(s, to) < 3)
 +                  if (is_ok(to) && distance(s, to) < 4)
                    {
 -                      if (pt == PAWN)
 -                          PawnAttacks[c][s] |= to;
 -                      else
 -                          PseudoAttacks[pt][s] |= to;
 +                      PseudoAttacks[c][pt][s] |= to;
 +                      LeaperAttacks[c][pt][s] |= to;
                    }
                }
 +              for (Direction d : pi->stepsQuiet)
 +              {
 +                  Square to = s + Direction(c == WHITE ? d : -d);
  
 -  Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
 -  Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
 -
 -  init_magics(RookTable, RookMagics, RookDirections);
 -  init_magics(BishopTable, BishopMagics, BishopDirections);
 +                  if (is_ok(to) && distance(s, to) < 4)
 +                  {
 +                      PseudoMoves[c][pt][s] |= to;
 +                      LeaperMoves[c][pt][s] |= to;
 +                  }
 +              }
 +              PseudoAttacks[c][pt][s] |= sliding_attack(pi->sliderCapture, s, 0, c);
 +              PseudoMoves[c][pt][s] |= sliding_attack(pi->sliderQuiet, s, 0, c);
 +          }
 +      }
  
 -  for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
 +  for (Square s1 = SQ_A1; s1 <= SQ_MAX; ++s1)
    {
 -      PseudoAttacks[QUEEN][s1]  = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
 -      PseudoAttacks[QUEEN][s1] |= PseudoAttacks[  ROOK][s1] = attacks_bb<  ROOK>(s1, 0);
 -
        for (PieceType pt : { BISHOP, ROOK })
 -          for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
 -              if (PseudoAttacks[pt][s1] & s2)
 -                  LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
 +          for (Square s2 = SQ_A1; s2 <= SQ_MAX; ++s2)
 +              if (PseudoAttacks[WHITE][pt][s1] & s2)
 +                  LineBB[s1][s2] = (attacks_bb(WHITE, pt, s1, 0) & attacks_bb(WHITE, pt, s2, 0)) | s1 | s2;
    }
  }
  
@@@ -131,20 -123,10 +131,13 @@@ namespace 
  
    // PassedRank[Rank] contains a bonus according to the rank of a passed pawn
    constexpr Score PassedRank[RANK_NB] = {
-     S(0, 0), S(5, 18), S(12, 23), S(10, 31), S(57, 62), S(163, 167), S(271, 250)
-   };
-   // PassedFile[File] contains a bonus according to the file of a passed pawn
-   constexpr Score PassedFile[FILE_NB] = {
-     S( -1,  7), S( 0,  9), S(-9, -8), S(-30,-14),
-     S(-30,-14), S(-9, -8), S( 0,  9), S( -1,  7)
+     S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
    };
  
 +  // KingProximity contains a penalty according to distance from king
 +  constexpr Score KingProximity = S(1, 3);
 +
    // Assorted bonuses and penalties
-   constexpr Score AttacksOnSpaceArea = S(  4,  0);
    constexpr Score BishopPawns        = S(  3,  7);
    constexpr Score CorneredBishop     = S( 50, 50);
    constexpr Score FlankAttacks       = S(  8,  0);
      b &= ~attackedBy[Them][PAWN] & safe;
  
      // Bonus for safe pawn threats on the next move
-     b = (pawn_attacks_bb<Us>(b) | shift<Up>(shift<Up>(pos.pieces(Us, SHOGI_PAWN)))) & pos.pieces(Them);
 -    b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
++    b = (pawn_attacks_bb<Us>(b) | shift<Up>(shift<Up>(pos.pieces(Us, SHOGI_PAWN)))) & nonPawnEnemies;
      score += ThreatByPawnPush * popcount(b);
  
      // Our safe or protected pawns
      constexpr Direction Up   = (Us == WHITE ? NORTH : SOUTH);
  
      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, defendedSquares, unsafeSquares;
+     Bitboard b, bb, squaresToQueen, unsafeSquares;
      Score score = SCORE_ZERO;
  
      b = pe->passed_pawns(Us);
  
          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];
  
          if (r > RANK_3)
          {
-             int w = (r-2) * (r-2) + 2;
+             int w = 5 * r - 13;
              Square blockSq = s + Up;
  
 -            // Adjust bonus based on the king's proximity
 -            bonus += make_score(0, (  king_proximity(Them, blockSq) * 5
 -                                    - king_proximity(Us,   blockSq) * 2) * w);
 +            // Skip bonus for antichess variants
 +            if (pos.extinction_value() != VALUE_MATE)
 +            {
 +                // Adjust bonus based on the king's proximity
 +                bonus += make_score(0, (  king_proximity(Them, blockSq) * 5
 +                                        - king_proximity(Us,   blockSq) * 2) * w);
  
 -            // If blockSq is not the queening square then consider also a second push
 -            if (r != RANK_7)
 -                bonus -= make_score(0, king_proximity(Us, blockSq + Up) * w);
 +                // If blockSq is not the queening square then consider also a second push
 +                if (r != RANK_7)
 +                    bonus -= make_score(0, king_proximity(Us, blockSq + Up) * w);
 +            }
  
              // If the pawn is free to advance, then increase the bonus
              if (pos.empty(blockSq))
          // Scale down bonus for candidate passers which need more than one
          // pawn push to become passed, or have a pawn in front of them.
          if (   !pos.pawn_passed(Us, s + Up)
-             || (pos.pieces(PAWN) & forward_file_bb(Us, s)))
+             || (pos.pieces(PAWN) & (s + Up)))
              bonus = bonus / 2;
  
-         score += bonus + PassedFile[file_of(s)];
 -        score += bonus - PassedFile * std::min(f, ~f);
++        score += bonus - PassedFile * std::min(f, File(pos.max_file() - f));
 +    }
 +
 +    // 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)
      behind |= shift<Down>(behind);
      behind |= shift<Down+Down>(behind);
  
 +    if (pawnsOnly)
 +    {
 +        safe = behind & ~attackedBy[Them][ALL_PIECES];
 +        behind = 0;
 +    }
-     int bonus = popcount(safe) + popcount(behind & safe);
+     int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
      int weight = pos.count<ALL_PIECES>(Us) - 1;
      Score score = make_score(bonus * weight * weight / 16, 0);
  
@@@ -164,14 -140,7 +164,14 @@@ Entry* probe(const Position& pos) 
    if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
        return e;
  
-   for (Color c = WHITE; c <= BLACK; ++c)
+   for (Color c : { WHITE, BLACK })
 +      if (is_KFsPsK(pos, c))
 +      {
 +          e->evaluationFunction = &EvaluateKFsPsK[c];
 +          return e;
 +      }
 +
-   for (Color c = WHITE; c <= BLACK; ++c)
++  for (Color c : { WHITE, BLACK })
        if (is_KXK(pos, c))
        {
            e->evaluationFunction = &EvaluateKXK[c];
Simple merge
diff --cc src/pawns.cpp
@@@ -101,48 -98,45 +98,47 @@@ namespace 
          // Flag the pawn
          opposed    = theirPawns & forward_file_bb(Us, s);
          stoppers   = theirPawns & passed_pawn_span(Us, s);
 -        lever      = theirPawns & PawnAttacks[Us][s];
 -        leverPush  = theirPawns & PawnAttacks[Us][s + Up];
 -        doubled    = ourPawns   & (s - Up);
 +        lever      = theirPawns & PseudoAttacks[Us][PAWN][s];
 +        leverPush  = relative_rank(Them, s, pos.max_rank()) > RANK_1 ? theirPawns & PseudoAttacks[Us][PAWN][s + Up] : Bitboard(0);
 +        doubled    = r > RANK_1 ? ourPawns & (s - Up) : Bitboard(0);
          neighbours = ourPawns   & adjacent_files_bb(s);
          phalanx    = neighbours & rank_bb(s);
 -        support    = neighbours & rank_bb(s - Up);
 +        support    = r > RANK_1 ? neighbours & rank_bb(s - Up) : Bitboard(0);
  
-         // A pawn is backward when it is behind all pawns of the same color
-         // on the adjacent files and cannot be safely advanced.
-         backward =   relative_rank(Them, s, pos.max_rank()) > RANK_1
-                   && !(ourPawns & pawn_attack_span(Them, s + Up))
+         // A pawn is backward when it is behind all pawns of the same color on
+         // the adjacent files and cannot safely advance. Phalanx and isolated
+         // pawns will be excluded when the pawn is scored.
+         backward =  !(neighbours & forward_ranks_bb(Them, s))
++                  && is_ok(s + Up)
                    && (stoppers & (leverPush | (s + Up)));
  
-         // Passed pawns will be properly scored in evaluation because we need
-         // full attack info to evaluate them. Include also not passed pawns
-         // which could become passed after one or two pawn pushes when are
-         // not attacked more times than defended.
-         if (   !(stoppers ^ lever) ||
-               (!(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush)))
+         // A pawn is passed if one of the three following conditions is true:
+         // (a) there is no stoppers except some levers
+         // (b) the only stoppers are the leverPush, but we outnumber them
+         // (c) there is only one front stopper which can be levered.
+         passed =   !(stoppers ^ lever)
+                 || (   !(stoppers ^ leverPush)
+                     && popcount(phalanx) >= popcount(leverPush))
 -                || (   stoppers == square_bb(s + Up) && r >= RANK_5
++                || (   is_ok(s + Up) && stoppers == square_bb(s + Up) && r >= RANK_5
+                     && (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
+         // Passed pawns will be properly scored later in evaluation when we have
+         // full attack info.
+         if (passed)
              e->passedPawns[Us] |= s;
  
          // Score this pawn
          if (support | phalanx)
          {
 -            int v =  Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1)
 +            int v =  Connected[r] * (phalanx ? 3 : 2)  * (r == RANK_2 && pos.captures_to_hand() ? 3 : 1) / (opposed ? 2 : 1)
                     + 17 * popcount(support);
 -
 +            if (r >= RANK_4 && pos.count<PAWN>(Us) > popcount(pos.board_bb()) / 4)
 +                v = std::max(v, popcount(support | phalanx) * 50) / (opposed ? 2 : 1);
              score += make_score(v, v * (r - 2) / 4);
          }
          else if (!neighbours)
 -            score -= Isolated + WeakUnopposed * int(!opposed);
 +            score -= Isolated * (1 + 2 * pos.must_capture()) + WeakUnopposed * int(!opposed);
  
          else if (backward)
              score -= Backward + WeakUnopposed * int(!opposed);
              score -= Doubled;
      }
  
+     // Penalize the unsupported and non passed pawns attacked twice by the enemy
+     b =   ourPawns
+         & doubleAttackThem
+         & ~(e->pawnAttacks[Us] | e->passedPawns[Us]);
+     score -= WeakLever * popcount(b);
 +    // Double pawn evaluation if there are no non-pawn pieces
 +    if (pos.count<ALL_PIECES>(Us) == pos.count<PAWN>(Us))
 +        score = score * 2;
 +
 +    const Square* pl_shogi = pos.squares<SHOGI_PAWN>(Us);
 +
 +    ourPawns   = pos.pieces(Us,   SHOGI_PAWN);
 +    theirPawns = pos.pieces(Them, SHOGI_PAWN);
 +
 +    // Loop through all shogi pawns of the current color and score each one
 +    while ((s = *pl_shogi++) != SQ_NONE)
 +    {
 +        assert(pos.piece_on(s) == make_piece(Us, SHOGI_PAWN));
 +
 +        neighbours = ourPawns & adjacent_files_bb(s);
 +
 +        if (!neighbours)
 +            score -= Isolated / 2;
 +    }
 +
      return score;
    }
  
@@@ -205,34 -185,34 +207,34 @@@ Entry* probe(const Position& pos) 
  template<Color Us>
  void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) {
  
-   constexpr Color     Them = (Us == WHITE ? BLACK : WHITE);
+   constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
  
 -  Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
 +  Bitboard b = pos.pieces(PAWN, SHOGI_PAWN) & ~forward_ranks_bb(Them, ksq);
    Bitboard ourPawns = b & pos.pieces(Us);
    Bitboard theirPawns = b & pos.pieces(Them);
  
-   Value bonus[] = { Value(5), Value(5) };
+   Score bonus = make_score(5, 5);
  
 -  File center = clamp(file_of(ksq), FILE_B, FILE_G);
 +  File center = clamp(file_of(ksq), FILE_B, File(pos.max_file() - 1));
    for (File f = File(center - 1); f <= File(center + 1); ++f)
    {
        b = ourPawns & file_bb(f);
 -      Rank ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
 +      Rank ourRank = b ? relative_rank(Us, frontmost_sq(Them, b), pos.max_rank()) : RANK_1;
  
        b = theirPawns & file_bb(f);
 -      Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
 +      Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b), pos.max_rank()) : RANK_1;
  
 -      int d = std::min(f, ~f);
 -      bonus += make_score(ShelterStrength[d][ourRank], 0);
 +      int d = std::min(std::min(f, File(pos.max_file() - f)), FILE_D);
-       bonus[MG] += ShelterStrength[d][ourRank] * (1 + (pos.captures_to_hand() && ourRank <= RANK_2));
++      bonus += make_score(ShelterStrength[d][ourRank], 0) * (1 + (pos.captures_to_hand() && ourRank <= RANK_2));
  
        if (ourRank && (ourRank == theirRank - 1))
-           bonus[MG] -= 82 * (theirRank == RANK_3), bonus[EG] -= 82 * (theirRank == RANK_3);
+           bonus -= make_score(82 * (theirRank == RANK_3), 82 * (theirRank == RANK_3));
        else
-           bonus[MG] -= UnblockedStorm[d][theirRank];
+           bonus -= make_score(UnblockedStorm[d][theirRank], 0);
    }
  
-   if (bonus[MG] > mg_value(shelter))
-       shelter = make_score(bonus[MG], bonus[EG]);
+   if (mg_value(bonus) > mg_value(shelter))
+       shelter = bonus;
  }
  
  
@@@ -159,12 -146,11 +159,12 @@@ void Position::init() 
  
    PRNG rng(1070372);
  
-   for (Color c = WHITE; c <= BLACK; ++c)
 -  for (Piece pc : Pieces)
 -      for (Square s = SQ_A1; s <= SQ_H8; ++s)
 -          Zobrist::psq[pc][s] = rng.rand<Key>();
++  for (Color c : {WHITE, BLACK})
 +      for (PieceType pt = PAWN; pt <= KING; ++pt)
 +          for (Square s = SQ_A1; s <= SQ_MAX; ++s)
 +              Zobrist::psq[make_piece(c, pt)][s] = rng.rand<Key>();
  
 -  for (File f = FILE_A; f <= FILE_H; ++f)
 +  for (File f = FILE_A; f <= FILE_MAX; ++f)
        Zobrist::enpassant[f] = rng.rand<Key>();
  
    for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
    Zobrist::side = rng.rand<Key>();
    Zobrist::noPawns = rng.rand<Key>();
  
-   for (Color c = WHITE; c <= BLACK; ++c)
++  for (Color c : {WHITE, BLACK})
 +      for (int n = 0; n < CHECKS_NB; ++n)
 +          Zobrist::checks[c][n] = rng.rand<Key>();
 +
-   for (Color c = WHITE; c <= BLACK; ++c)
++  for (Color c : {WHITE, BLACK})
 +      for (PieceType pt = PAWN; pt <= KING; ++pt)
 +          for (int n = 0; n < SQUARE_NB; ++n)
 +              Zobrist::inHand[make_piece(c, pt)][n] = rng.rand<Key>();
 +
    // Prepare the cuckoo tables
    std::memset(cuckoo, 0, sizeof(cuckoo));
    std::memset(cuckooMove, 0, sizeof(cuckooMove));
    int count = 0;
-   for (Color c = WHITE; c <= BLACK; ++c)
 -  for (Piece pc : Pieces)
 -      for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
 -          for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
 -              if (PseudoAttacks[type_of(pc)][s1] & s2)
++  for (Color c : {WHITE, BLACK})
 +      for (PieceType pt = KNIGHT; pt <= QUEEN || pt == KING; pt != QUEEN ? ++pt : pt = KING)
 +      {
 +      Piece pc = make_piece(c, pt);
 +      for (Square s1 = SQ_A1; s1 <= SQ_MAX; ++s1)
 +          for (Square s2 = Square(s1 + 1); s2 <= SQ_MAX; ++s2)
 +              if (PseudoAttacks[WHITE][type_of(pc)][s1] & s2)
                {
                    Move move = make_move(s1, s2);
                    Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side;
@@@ -518,21 -399,9 +518,21 @@@ void Position::set_state(StateInfo* si
  
    si->key ^= Zobrist::castling[si->castlingRights];
  
-   for (Color c = WHITE; c <= BLACK; ++c)
 -  for (Piece pc : Pieces)
 -      for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
 -          si->materialKey ^= Zobrist::psq[pc][cnt];
++  for (Color c : {WHITE, BLACK})
 +      for (PieceType pt = PAWN; pt <= KING; ++pt)
 +      {
 +          Piece pc = make_piece(c, pt);
 +
 +          for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
 +              si->materialKey ^= Zobrist::psq[pc][cnt];
 +
 +          if (piece_drops())
 +              si->key ^= Zobrist::inHand[pc][pieceCountInHand[c][pt]];
 +      }
 +
 +  if (max_check_count())
-       for (Color c = WHITE; c <= BLACK; ++c)
++      for (Color c : {WHITE, BLACK})
 +          si->key ^= Zobrist::checks[c][si->checksGiven[c]];
  }
  
  
@@@ -596,16 -452,6 +596,16 @@@ const string Position::fen() const 
            ss << '/';
    }
  
 +  // pieces in hand
 +  if (piece_drops())
 +  {
 +      ss << '[';
-       for (Color c = WHITE; c <= BLACK; ++c)
++      for (Color c : {WHITE, BLACK})
 +          for (PieceType pt = KING; pt >= PAWN; --pt)
 +              ss << std::string(pieceCountInHand[c][pt], piece_to_char()[make_piece(c, pt)]);
 +      ss << ']';
 +  }
 +
    ss << (sideToMove == WHITE ? " w " : " b ");
  
    if (can_castle(WHITE_OO))
@@@ -1940,21 -1288,19 +1940,21 @@@ bool Position::pos_is_ok() const 
    if (std::memcmp(&si, st, sizeof(StateInfo)))
        assert(0 && "pos_is_ok: State");
  
-   for (Color c = WHITE; c <= BLACK; ++c)
 -  for (Piece pc : Pieces)
 -  {
 -      if (   pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
 -          || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
 -          assert(0 && "pos_is_ok: Pieces");
 -
 -      for (int i = 0; i < pieceCount[pc]; ++i)
 -          if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
 -              assert(0 && "pos_is_ok: Index");
 -  }
++  for (Color c : {WHITE, BLACK})
 +      for (PieceType pt = PAWN; pt <= KING; ++pt)
 +      {
 +          Piece pc = make_piece(c, pt);
 +          if (   pieceCount[pc] != popcount(pieces(c, pt))
 +              || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
 +              assert(0 && "pos_is_ok: Pieces");
 +
 +          for (int i = 0; i < pieceCount[pc]; ++i)
 +              if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
 +                  assert(0 && "pos_is_ok: Index");
 +      }
  
-   for (Color c = WHITE; c <= BLACK; ++c)
-       for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
+   for (Color c : { WHITE, BLACK })
+       for (CastlingSide s : {KING_SIDE, QUEEN_SIDE})
        {
            if (!can_castle(c | s))
                continue;
diff --cc src/position.h
@@@ -724,6 -317,10 +725,10 @@@ inline Bitboard Position::check_squares
    return st->checkSquares[pt];
  }
  
+ inline bool Position::is_discovery_check_on_king(Color c, Move m) const {
 -  return st->blockersForKing[c] & from_sq(m);
++  return is_ok(from_sq(m)) && st->blockersForKing[c] & from_sq(m);
+ }
  inline bool Position::pawn_passed(Color c, Square s) const {
    return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
  }
diff --cc src/psqt.cpp
Simple merge
diff --cc src/search.cpp
@@@ -1020,6 -1012,6 +1030,12 @@@ moves_loop: // When in check, search st
                 && pos.pawn_passed(us, to_sq(move)))
            extension = ONE_PLY;
  
++      // Losing chess capture extension
++      else if (    pos.must_capture()
++               &&  pos.capture(move)
++               &&  MoveList<CAPTURES>(pos).size() == 1)
++          extension = ONE_PLY;
++
        // Calculate new depth for this move
        newDepth = depth - ONE_PLY + extension;
  
  
            if (   !captureOrPromotion
                && !givesCheck
 -              && (!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)))
            {
-               // Move count based pruning (~30 Elo)
+               // Move count based pruning
                if (moveCountPruning)
                    continue;
  
                    continue;
  
                // Prune moves with negative SEE (~10 Elo)
-               if (!pos.must_capture() && !pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth)))
 -              if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth)))
++              if (!pos.must_capture() && !pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth)))
                    continue;
            }
-           else if ((!givesCheck || !extension)
-                   && !pos.must_capture()
-                   && !pos.see_ge(move, -(PawnValueEg + 120 * pos.captures_to_hand()) * (depth / ONE_PLY))) // (~20 Elo)
+           else if (  (!givesCheck || !extension)
 -                   && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo)
++                   && !pos.must_capture()
++                   && !pos.see_ge(move, -(PawnValueEg + 120 * pos.captures_to_hand()) * (depth / ONE_PLY))) // (~20 Elo)
                    continue;
        }
  
                    r -= 2 * ONE_PLY;
  
                ss->statScore =  thisThread->mainHistory[us][from_to(move)]
 -                             + (*contHist[0])[movedPiece][to_sq(move)]
 -                             + (*contHist[1])[movedPiece][to_sq(move)]
 -                             + (*contHist[3])[movedPiece][to_sq(move)]
 +                             + (*contHist[0])[history_slot(movedPiece)][to_sq(move)]
 +                             + (*contHist[1])[history_slot(movedPiece)][to_sq(move)]
 +                             + (*contHist[3])[history_slot(movedPiece)][to_sq(move)]
                               - 4000;
  
+               // Reset statScore to zero if negative and most stats shows >= 0
+               if (    ss->statScore < 0
 -                  && (*contHist[0])[movedPiece][to_sq(move)] >= 0
 -                  && (*contHist[1])[movedPiece][to_sq(move)] >= 0
++                  && (*contHist[0])[history_slot(movedPiece)][to_sq(move)] >= 0
++                  && (*contHist[1])[history_slot(movedPiece)][to_sq(move)] >= 0
+                   && thisThread->mainHistory[us][from_to(move)] >= 0)
+                   ss->statScore = 0;
                // Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
                if (ss->statScore >= 0 && (ss-1)->statScore < 0)
                    r -= ONE_PLY;
Simple merge
diff --cc src/types.h
Simple merge
@@@ -91,8 -73,9 +91,10 @@@ void init(OptionsMap& o) 
    o["Slow Mover"]            << Option(84, 10, 1000);
    o["nodestime"]             << Option(0, 0, 10000);
    o["UCI_Chess960"]          << Option(false);
 +  o["UCI_Variant"]           << Option("chess", variants.get_keys(), on_variant_change);
    o["UCI_AnalyseMode"]       << Option(false);
+   o["UCI_LimitStrength"]     << Option(false);
+   o["UCI_Elo"]               << Option(1350, 1350, 2850);
    o["SyzygyPath"]            << Option("<empty>", on_tb_path);
    o["SyzygyProbeDepth"]      << Option(1, 1, 100);
    o["Syzygy50MoveRule"]      << Option(true);