Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Fri, 25 Oct 2019 09:11:10 +0000 (11:11 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Fri, 25 Oct 2019 09:12:23 +0000 (11:12 +0200)
bench: 4231542

19 files changed:
1  2 
.travis.yml
src/benchmark.cpp
src/evaluate.cpp
src/main.cpp
src/misc.cpp
src/movegen.cpp
src/movepick.cpp
src/movepick.h
src/pawns.cpp
src/position.cpp
src/position.h
src/psqt.cpp
src/search.cpp
src/syzygy/tbprobe.cpp
src/thread.cpp
src/tt.cpp
src/tt.h
src/types.h
src/uci.cpp

diff --cc .travis.yml
Simple merge
Simple merge
@@@ -79,7 -78,7 +79,7 @@@ namespace 
    constexpr Value SpaceThreshold = Value(12222);
  
    // KingAttackWeights[PieceType] contains king attack weights by piece type
-   constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 77, 55, 44, 10, 40 };
 -  constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
++  constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10, 40 };
  
    // Penalties for enemy's safe checks
    constexpr int QueenSafeCheck  = 780;
  
              // Bonus for rook on an open or semi-open file
              if (pos.is_on_semiopen_file(Us, s))
-                 score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))];
+                 score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
  
              // Penalty when trapped by the king, even more if the king cannot castle
 -            else if (mob <= 3)
 +            else if (mob <= 3 && pos.count<KING>(Us))
              {
                  File kf = file_of(pos.square<KING>(Us));
                  if ((kf < FILE_E) == (file_of(s) < kf))
                   -  35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
                   + 148 * popcount(unsafeChecks)
                   +  98 * popcount(pos.blockers_for_king(Us))
 -                 - 873 * !pos.count<QUEEN>(Them)
 +                 - 873 * !(pos.major_pieces(Them) || pos.captures_to_hand()) / (1 + pos.check_counting())
                   -   6 * mg_value(score) / 8
                   +       mg_value(mobility[Them] - mobility[Us])
-                  +   5 * kingFlankAttacks * kingFlankAttacks / 16
+                  +   3 * kingFlankAttacks * kingFlankAttacks / 8
                   -   7;
  
      // Transform the kingDanger units into a Score, and subtract it from the evaluation
diff --cc src/main.cpp
Simple merge
diff --cc src/misc.cpp
Simple merge
diff --cc src/movegen.cpp
@@@ -333,43 -239,10 +333,43 @@@ namespace 
          if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
          {
              if (!pos.castling_impeded(OO) && pos.can_castle(OO))
 -                *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
 +                moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, ksq, pos.castling_rook_square(OO));
  
              if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
 -                *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
 +                moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, ksq, pos.castling_rook_square(OOO));
 +        }
 +    }
 +
 +    // Castling with non-king piece
 +    if (!pos.count<KING>(Us) && Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
 +    {
-         Square from = make_square(FILE_E, relative_rank(Us, pos.castling_rank(), pos.max_rank()));
++        Square from = make_square(FILE_E, pos.castling_rank(Us));
 +        if (!pos.castling_impeded(OO) && pos.can_castle(OO))
 +            moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, from, pos.castling_rook_square(OO));
 +
 +        if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
 +            moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, from, pos.castling_rook_square(OOO));
 +    }
 +
 +    // Special moves
 +    if (pos.cambodian_moves() && pos.gates(Us))
 +    {
 +        if (Type != CAPTURES && Type != EVASIONS && (pos.pieces(Us, KING) & pos.gates(Us)))
 +        {
 +            Square from = pos.square<KING>(Us);
 +            Bitboard b = PseudoAttacks[WHITE][KNIGHT][from] & rank_bb(rank_of(from + (Us == WHITE ? NORTH : SOUTH)))
 +                        & target & ~pos.pieces();
 +            while (b)
 +                moveList = make_move_and_gating<SPECIAL>(pos, moveList, Us, from, pop_lsb(&b));
 +        }
 +
 +        Bitboard b = pos.pieces(Us, FERS) & pos.gates(Us);
 +        while (b)
 +        {
 +            Square from = pop_lsb(&b);
 +            Square to = from + 2 * (Us == WHITE ? NORTH : SOUTH);
 +            if (is_ok(to) && (target & to))
 +                moveList = make_move_and_gating<SPECIAL>(pos, moveList, Us, from, to);
          }
      }
  
@@@ -115,11 -111,11 +115,11 @@@ void MovePicker::score() 
                     + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
  
        else if (Type == QUIETS)
-           m.value =  (*mainHistory)[pos.side_to_move()][from_to(m)]
-                    + (*continuationHistory[0])[history_slot(pos.moved_piece(m))][to_sq(m)]
-                    + (*continuationHistory[1])[history_slot(pos.moved_piece(m))][to_sq(m)]
-                    + (*continuationHistory[3])[history_slot(pos.moved_piece(m))][to_sq(m)]
-                    + (*continuationHistory[5])[history_slot(pos.moved_piece(m))][to_sq(m)] / 2;
+           m.value =      (*mainHistory)[pos.side_to_move()][from_to(m)]
 -                   + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
 -                   + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
 -                   + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
 -                   +     (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)];
++                   + 2 * (*continuationHistory[0])[history_slot(pos.moved_piece(m))][to_sq(m)]
++                   + 2 * (*continuationHistory[1])[history_slot(pos.moved_piece(m))][to_sq(m)]
++                   + 2 * (*continuationHistory[3])[history_slot(pos.moved_piece(m))][to_sq(m)]
++                   +     (*continuationHistory[5])[history_slot(pos.moved_piece(m))][to_sq(m)];
  
        else // Type == EVASIONS
        {
diff --cc src/movepick.h
@@@ -79,8 -79,8 +79,8 @@@ template <typename T, int D, int Size
  struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
  
  /// In stats table, D=0 means that the template parameter is not used
 -enum StatsParams { NOT_USED = 0 };
 +enum StatsParams { NOT_USED = 0, PIECE_SLOTS = 8 };
+ enum StatsType { NoCaptures, Captures };
  
  /// ButterflyHistory records how often quiet moves have been successful or
  /// unsuccessful during the current search, and is used for reduction and move
diff --cc src/pawns.cpp
@@@ -92,26 -92,27 +92,28 @@@ namespace 
      {
          assert(pos.piece_on(s) == make_piece(Us, PAWN));
  
 -        Rank r = relative_rank(Us, s);
 +        Rank r = relative_rank(Us, s, pos.max_rank());
  
-         e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
          // Flag the pawn
          opposed    = theirPawns & forward_file_bb(Us, s);
+         blocked    = theirPawns & (s + Up);
          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 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)));
+         // the adjacent files and cannot safely advance.
 -        backward =  !(neighbours & forward_ranks_bb(Them, s + Up))
++        backward =   is_ok(s + Up)
++                  && !(neighbours & forward_ranks_bb(Them, s + Up))
+                   && (stoppers & (leverPush | blocked));
+         // Compute additional span if pawn is not backward nor blocked
+         if (!backward && !blocked)
+             e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
  
          // A pawn is passed if one of the three following conditions is true:
          // (a) there is no stoppers except some levers
          // Score this pawn
          if (support | phalanx)
          {
-             int v =  Connected[r] * (phalanx ? 3 : 2)  * (r == RANK_2 && pos.captures_to_hand() ? 3 : 1) / (opposed ? 2 : 1)
-                    + 17 * popcount(support);
 -            int v =  Connected[r] * (2 + bool(phalanx) - bool(opposed))
++            int v =  Connected[r] * (2 + bool(phalanx) - bool(opposed)) * (r == RANK_2 && pos.captures_to_hand() ? 3 : 1)
+                    + 21 * 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 * (1 + 2 * pos.must_capture()) + WeakUnopposed * !opposed;
 -            score -=   Isolated
++            score -=   Isolated * (1 + 2 * pos.must_capture())
+                      + WeakUnopposed * !opposed;
  
          else if (backward)
-             score -= Backward + WeakUnopposed * !opposed;
+             score -=   Backward
+                      + WeakUnopposed * !opposed;
  
          if (!support)
              score -=   Doubled * doubled
@@@ -242,21 -224,17 +247,17 @@@ Score Entry::do_king_safety(const Posit
    Square ksq = pos.square<KING>(Us);
    kingSquares[Us] = ksq;
    castlingRights[Us] = pos.castling_rights(Us);
+   auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
  
-   Score shelters[3] = { evaluate_shelter<Us>(pos, ksq),
-                         make_score(-VALUE_INFINITE, 0),
-                         make_score(-VALUE_INFINITE, 0) };
+   Score shelter = evaluate_shelter<Us>(pos, ksq);
  
    // If we can castle use the bonus after castling if it is bigger
    if (pos.can_castle(Us & KING_SIDE))
-       shelters[1] = evaluate_shelter<Us>(pos, make_square(pos.castling_kingside_file(), Us == WHITE ? RANK_1 : pos.max_rank()));
 -      shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
++      shelter = std::max(shelter, evaluate_shelter<Us>(pos, make_square(pos.castling_kingside_file(), pos.castling_rank(Us))), compare);
  
    if (pos.can_castle(Us & QUEEN_SIDE))
-       shelters[2] = evaluate_shelter<Us>(pos, make_square(pos.castling_queenside_file(), Us == WHITE ? RANK_1 : pos.max_rank()));
-   for (int i : {1, 2})
-      if (mg_value(shelters[i]) > mg_value(shelters[0]))
-          shelters[0] = shelters[i];
 -      shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
++      shelter = std::max(shelter, evaluate_shelter<Us>(pos, make_square(pos.castling_queenside_file(), pos.castling_rank(Us))), compare);
  
    // In endgame we like to bring our king near our closest pawn
    Bitboard pawns = pos.pieces(Us, PAWN);
@@@ -338,136 -264,57 +338,136 @@@ Position& Position::set(const Variant* 
    // 2. Active color
    ss >> token;
    sideToMove = (token == 'w' ? WHITE : BLACK);
 +  // Invert side to move for SFEN
 +  if (sfen)
 +      sideToMove = ~sideToMove;
    ss >> token;
  
 -  // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
 -  // Shredder-FEN that uses the letters of the columns on which the rooks began
 -  // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
 -  // if an inner rook is associated with the castling right, the castling tag is
 -  // replaced by the file letter of the involved rook, as for the Shredder-FEN.
 -  while ((ss >> token) && !isspace(token))
 +  // 3-4. Skip parsing castling and en passant flags if not present
 +  st->epSquare = SQ_NONE;
 +  if (!isdigit(ss.peek()) && !sfen)
    {
 -      Square rsq;
 -      Color c = islower(token) ? BLACK : WHITE;
 -      Piece rook = make_piece(c, ROOK);
 +      // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
 +      // Shredder-FEN that uses the letters of the columns on which the rooks began
 +      // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
 +      // if an inner rook is associated with the castling right, the castling tag is
 +      // replaced by the file letter of the involved rook, as for the Shredder-FEN.
 +      while ((ss >> token) && !isspace(token))
 +      {
 +          Square rsq;
 +          Color c = islower(token) ? BLACK : WHITE;
 +          Piece rook = make_piece(c, ROOK);
  
 -      token = char(toupper(token));
 +          token = char(toupper(token));
  
 -      if (token == 'K')
 -          for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
 +          if (token == 'K')
-               for (rsq = make_square(FILE_MAX, relative_rank(c, castling_rank(), max_rank())); piece_on(rsq) != rook; --rsq) {}
++              for (rsq = make_square(FILE_MAX, castling_rank(c)); piece_on(rsq) != rook; --rsq) {}
  
 -      else if (token == 'Q')
 -          for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
 +          else if (token == 'Q')
-               for (rsq = make_square(FILE_A, relative_rank(c, castling_rank(), max_rank())); piece_on(rsq) != rook; ++rsq) {}
++              for (rsq = make_square(FILE_A, castling_rank(c)); piece_on(rsq) != rook; ++rsq) {}
  
 -      else if (token >= 'A' && token <= 'H')
 -          rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
 +          else if (token >= 'A' && token <= 'A' + max_file())
-               rsq = make_square(File(token - 'A'), relative_rank(c, castling_rank(), max_rank()));
++              rsq = make_square(File(token - 'A'), castling_rank(c));
  
 -      else
 -          continue;
 +          else
 +              continue;
  
 -      set_castling_right(c, rsq);
 +          // Set gates (and skip castling rights)
 +          if (gating())
 +          {
 +              st->gatesBB[c] |= rsq;
 +              if (token == 'K' || token == 'Q')
-                   st->gatesBB[c] |= count<KING>(c) ? square<KING>(c) : make_square(FILE_E, relative_rank(c, castling_rank(), max_rank()));
++                  st->gatesBB[c] |= count<KING>(c) ? square<KING>(c) : make_square(FILE_E, castling_rank(c));
 +              // Do not set castling rights for gates unless there are no pieces in hand,
 +              // which means that the file is referring to a chess960 castling right.
 +              else if (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand())
 +                  continue;
 +          }
 +
 +          if (castling_enabled())
 +              set_castling_right(c, rsq);
 +      }
 +
 +      // Set castling rights for 960 gating variants
 +      if (gating() && castling_enabled())
 +          for (Color c : {WHITE, BLACK})
 +              if ((gates(c) & pieces(KING)) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand()))
 +              {
 +                  Bitboard castling_rooks = gates(c) & pieces(ROOK);
 +                  while (castling_rooks)
 +                      set_castling_right(c, pop_lsb(&castling_rooks));
 +              }
 +
 +      // counting limit
 +      if (counting_rule() && isdigit(ss.peek()))
 +          ss >> st->countingLimit;
 +
 +      // 4. En passant square. Ignore if no pawn capture is possible
 +      else if (   ((ss >> col) && (col >= 'a' && col <= 'a' + max_file()))
 +               && ((ss >> row) && (row >= '1' && row <= '1' + max_rank())))
 +      {
 +          st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
 +
 +          if (   !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
 +              || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
 +              st->epSquare = SQ_NONE;
 +      }
    }
  
 -  // 4. En passant square. Ignore if no pawn capture is possible
 -  if (   ((ss >> col) && (col >= 'a' && col <= 'h'))
 -      && ((ss >> row) && (row == '3' || row == '6')))
 -  {
 -      st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
 +  // Check counter for nCheck
 +  ss >> std::skipws >> token >> std::noskipws;
  
 -      if (   !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
 -          || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
 -          st->epSquare = SQ_NONE;
 +  if (check_counting())
 +  {
 +      if (ss.peek() == '+')
 +      {
 +          st->checksRemaining[WHITE] = CheckCount(std::max(token - '0', 0));
 +          ss >> token >> token;
 +          st->checksRemaining[BLACK] = CheckCount(std::max(token - '0', 0));
 +      }
 +      else
 +      {
 +          // If check count is not provided, assume that the next check wins
 +          st->checksRemaining[WHITE] = CheckCount(1);
 +          st->checksRemaining[BLACK] = CheckCount(1);
 +          ss.putback(token);
 +      }
    }
    else
 -      st->epSquare = SQ_NONE;
 +      ss.putback(token);
  
    // 5-6. Halfmove clock and fullmove number
 -  ss >> std::skipws >> st->rule50 >> gamePly;
 +  if (sfen)
 +  {
 +      // Pieces in hand for SFEN
 +      while ((ss >> token) && !isspace(token))
 +      {
 +          if (token == '-')
 +              continue;
 +          else if ((idx = piece_to_char().find(token)) != string::npos)
 +              add_to_hand(Piece(idx));
 +      }
 +      // Move count is in ply for SFEN
 +      ss >> std::skipws >> gamePly;
 +      gamePly = std::max(gamePly - 1, 0);
 +  }
 +  else
 +  {
 +      ss >> std::skipws >> st->rule50 >> gamePly;
  
 -  // Convert from fullmove starting from 1 to gamePly starting from 0,
 -  // handle also common incorrect FEN with fullmove = 0.
 -  gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
 +      // Convert from fullmove starting from 1 to gamePly starting from 0,
 +      // handle also common incorrect FEN with fullmove = 0.
 +      gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
 +  }
  
 -  chess960 = isChess960;
 +  // counting rules
 +  if (st->countingLimit && st->rule50)
 +  {
 +      st->countingPly = st->rule50;
 +      st->rule50 = 0;
 +  }
 +
 +  chess960 = isChess960 || v->chess960;
    thisThread = th;
    set_state(st);
  
  
  void Position::set_castling_right(Color c, Square rfrom) {
  
-   Square kfrom = count<KING>(c) ? square<KING>(c) : make_square(FILE_E, relative_rank(c, castling_rank(), max_rank()));
 -  Square kfrom = square<KING>(c);
++  Square kfrom = count<KING>(c) ? square<KING>(c) : make_square(FILE_E, castling_rank(c));
    CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
  
    st->castlingRights |= cr;
    castlingRightsMask[rfrom] |= cr;
    castlingRookSquare[cr] = rfrom;
  
-   Square kto = make_square(cr & KING_SIDE ? castling_kingside_file() : castling_queenside_file(),
-                            relative_rank(c, castling_rank(), max_rank()));
 -  Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
 -  Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
++  Square kto = make_square(cr & KING_SIDE ? castling_kingside_file() : castling_queenside_file(), castling_rank(c));
 +  Square rto = kto + (cr & KING_SIDE ? WEST : EAST);
  
    castlingPath[cr] =   (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
                      & ~(square_bb(kfrom) | rfrom);
@@@ -863,13 -557,9 +862,13 @@@ bool Position::legal(Move m) const 
    // enemy attacks, it is delayed at a later time: now!
    if (type_of(m) == CASTLING)
    {
 +      // Non-royal pieces can not be impeded from castling
 +      if (type_of(piece_on(from)) != KING)
 +          return true;
 +
        // After castling, the rook and king final positions are the same in
        // Chess960 as they would be in standard chess.
-       to = make_square(to > from ? castling_kingside_file() : castling_queenside_file(), relative_rank(us, castling_rank(), max_rank()));
 -      to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
++      to = make_square(to > from ? castling_kingside_file() : castling_queenside_file(), castling_rank(us));
        Direction step = to > from ? WEST : EAST;
  
        for (Square s = to; s != from; s += step)
@@@ -1049,11 -700,10 +1048,10 @@@ bool Position::gives_check(Move m) cons
    {
        Square kfrom = from;
        Square rfrom = to; // Castling is encoded as 'King captures the rook'
-       Square kto = make_square(rfrom > kfrom ? castling_kingside_file() : castling_queenside_file(),
-                               relative_rank(sideToMove, castling_rank(), max_rank()));
 -      Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
 -      Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
++      Square kto = make_square(rfrom > kfrom ? castling_kingside_file() : castling_queenside_file(), castling_rank(sideToMove));
 +      Square rto = kto + (rfrom > kfrom ? WEST : EAST);
  
 -      return   (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
 +      return   (PseudoAttacks[sideToMove][ROOK][rto] & square<KING>(~sideToMove))
              && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
    }
    default:
@@@ -1199,32 -818,7 +1197,32 @@@ void Position::do_move(Move m, StateInf
    }
  
    // Move the piece. The tricky Chess960 castling is handled earlier
 -  if (type_of(m) != CASTLING)
 +  if (type_of(m) == DROP)
 +  {
 +      drop_piece(make_piece(us, in_hand_piece_type(m)), pc, to);
 +      st->materialKey ^= Zobrist::psq[pc][pieceCount[pc]-1];
 +      if (type_of(pc) != PAWN)
 +          st->nonPawnMaterial[us] += PieceValue[MG][pc];
 +      // Set castling rights for dropped king or rook
-       if (castling_dropped_piece() && relative_rank(us, to, max_rank()) == castling_rank())
++      if (castling_dropped_piece() && rank_of(to) == castling_rank(us))
 +      {
 +          if (type_of(pc) == KING && file_of(to) == FILE_E)
 +          {
 +              Bitboard castling_rooks =  pieces(us, ROOK)
-                                        & rank_bb(relative_rank(us, castling_rank(), max_rank()))
++                                       & rank_bb(castling_rank(us))
 +                                       & (file_bb(FILE_A) | file_bb(max_file()));
 +              while (castling_rooks)
 +                  set_castling_right(us, pop_lsb(&castling_rooks));
 +          }
 +          else if (type_of(pc) == ROOK)
 +          {
 +              if (   (file_of(to) == FILE_A || file_of(to) == max_file())
-                   && piece_on(make_square(FILE_E, relative_rank(us, castling_rank(), max_rank()))) == make_piece(us, KING))
++                  && piece_on(make_square(FILE_E, castling_rank(us))) == make_piece(us, KING))
 +                  set_castling_right(us, to);
 +          }
 +      }
 +  }
 +  else if (type_of(m) != CASTLING)
        move_piece(pc, from, to);
  
    // If the moving piece is a pawn do some special extra work
@@@ -1515,16 -967,14 +1513,15 @@@ void Position::do_castling(Color us, Sq
  
    bool kingSide = to > from;
    rfrom = to; // Castling is encoded as "king captures friendly rook"
-   to = make_square(kingSide ? castling_kingside_file() : castling_queenside_file(),
-                    relative_rank(us, castling_rank(), max_rank()));
 -  rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
 -  to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
++  to = make_square(kingSide ? castling_kingside_file() : castling_queenside_file(), castling_rank(us));
 +  rto = to + (kingSide ? WEST : EAST);
  
    // Remove both pieces first since squares could overlap in Chess960
 -  remove_piece(make_piece(us, KING), Do ? from : to);
 +  Piece castling_piece = piece_on(Do ? from : to);
 +  remove_piece(castling_piece, Do ? from : to);
    remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
    board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
 -  put_piece(make_piece(us, KING), Do ? to : from);
 +  put_piece(castling_piece, Do ? to : from);
    put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
  }
  
diff --cc src/position.h
@@@ -87,72 -78,9 +87,72 @@@ public
    Position& operator=(const Position&) = delete;
  
    // FEN string input/output
 -  Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
 +  Position& set(const Variant* v, const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th, bool sfen = false);
    Position& set(const std::string& code, Color c, StateInfo* si);
 -  const std::string fen() const;
 +  const std::string fen(bool sfen = false) const;
 +
 +  // Variant rule properties
 +  const Variant* variant() const;
 +  Rank max_rank() const;
 +  File max_file() const;
 +  Bitboard board_bb() const;
 +  const std::set<PieceType>& piece_types() const;
 +  const std::string piece_to_char() const;
 +  Rank promotion_rank() const;
 +  const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types() const;
 +  bool sittuyin_promotion() const;
 +  int promotion_limit(PieceType pt) const;
 +  PieceType promoted_piece_type(PieceType pt) const;
 +  bool piece_promotion_on_capture() const;
 +  bool mandatory_pawn_promotion() const;
 +  bool mandatory_piece_promotion() const;
 +  bool piece_demotion() const;
 +  bool endgame_eval() const;
 +  bool double_step_enabled() const;
 +  Rank double_step_rank() const;
 +  bool first_rank_double_steps() const;
 +  bool castling_enabled() const;
 +  bool castling_dropped_piece() const;
 +  File castling_kingside_file() const;
 +  File castling_queenside_file() const;
-   Rank castling_rank() const;
++  Rank castling_rank(Color c) const;
 +  bool checking_permitted() const;
 +  bool must_capture() const;
 +  bool must_drop() const;
 +  bool piece_drops() const;
 +  bool drop_loop() const;
 +  bool captures_to_hand() const;
 +  bool first_rank_drops() const;
 +  bool drop_on_top() const;
 +  Bitboard drop_region(Color c) const;
 +  Bitboard drop_region(Color c, PieceType pt) const;
 +  bool sittuyin_rook_drop() const;
 +  bool drop_opposite_colored_bishop() const;
 +  bool drop_promoted() const;
 +  bool shogi_doubled_pawn() const;
 +  bool immobility_illegal() const;
 +  bool gating() const;
 +  bool seirawan_gating() const;
 +  bool cambodian_moves() const;
 +  // winning conditions
 +  int n_move_rule() const;
 +  int n_fold_rule() const;
 +  Value stalemate_value(int ply = 0) const;
 +  Value checkmate_value(int ply = 0) const;
 +  Value bare_king_value(int ply = 0) const;
 +  Value extinction_value(int ply = 0) const;
 +  bool bare_king_move() const;
 +  const std::set<PieceType>& extinction_piece_types() const;
 +  PieceType capture_the_flag_piece() const;
 +  Bitboard capture_the_flag(Color c) const;
 +  bool flag_move() const;
 +  bool check_counting() const;
 +  int connect_n() const;
 +  CheckCount checks_remaining(Color c) const;
 +  CountingRule counting_rule() const;
 +
 +  // Variant-specific properties
 +  int count_in_hand(Color c, PieceType pt) const;
  
    // Position representation
    Bitboard pieces() const;
@@@ -297,351 -203,6 +297,351 @@@ namespace PSQT 
  
  extern std::ostream& operator<<(std::ostream& os, const Position& pos);
  
 +inline const Variant* Position::variant() const {
 +  assert(var != nullptr);
 +  return var;
 +}
 +
 +inline Rank Position::max_rank() const {
 +  assert(var != nullptr);
 +  return var->maxRank;
 +}
 +
 +inline File Position::max_file() const {
 +  assert(var != nullptr);
 +  return var->maxFile;
 +}
 +
 +inline Bitboard Position::board_bb() const {
 +  assert(var != nullptr);
 +  return board_size_bb(var->maxFile, var->maxRank);
 +}
 +
 +inline const std::set<PieceType>& Position::piece_types() const {
 +  assert(var != nullptr);
 +  return var->pieceTypes;
 +}
 +
 +inline const std::string Position::piece_to_char() const {
 +  assert(var != nullptr);
 +  return var->pieceToChar;
 +}
 +
 +inline Rank Position::promotion_rank() const {
 +  assert(var != nullptr);
 +  return var->promotionRank;
 +}
 +
 +inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types() const {
 +  assert(var != nullptr);
 +  return var->promotionPieceTypes;
 +}
 +
 +inline bool Position::sittuyin_promotion() const {
 +  assert(var != nullptr);
 +  return var->sittuyinPromotion;
 +}
 +
 +inline int Position::promotion_limit(PieceType pt) const {
 +  assert(var != nullptr);
 +  return var->promotionLimit[pt];
 +}
 +
 +inline PieceType Position::promoted_piece_type(PieceType pt) const {
 +  assert(var != nullptr);
 +  return var->promotedPieceType[pt];
 +}
 +
 +inline bool Position::piece_promotion_on_capture() const {
 +  assert(var != nullptr);
 +  return var->piecePromotionOnCapture;
 +}
 +
 +inline bool Position::mandatory_pawn_promotion() const {
 +  assert(var != nullptr);
 +  return var->mandatoryPawnPromotion;
 +}
 +
 +inline bool Position::mandatory_piece_promotion() const {
 +  assert(var != nullptr);
 +  return var->mandatoryPiecePromotion;
 +}
 +
 +inline bool Position::piece_demotion() const {
 +  assert(var != nullptr);
 +  return var->pieceDemotion;
 +}
 +
 +inline bool Position::endgame_eval() const {
 +  assert(var != nullptr);
 +  return var->endgameEval;
 +}
 +
 +inline bool Position::double_step_enabled() const {
 +  assert(var != nullptr);
 +  return var->doubleStep;
 +}
 +
 +inline Rank Position::double_step_rank() const {
 +  assert(var != nullptr);
 +  return var->doubleStepRank;
 +}
 +
 +inline bool Position::first_rank_double_steps() const {
 +  assert(var != nullptr);
 +  return var->firstRankDoubleSteps;
 +}
 +
 +inline bool Position::castling_enabled() const {
 +  assert(var != nullptr);
 +  return var->castling;
 +}
 +
 +inline bool Position::castling_dropped_piece() const {
 +  assert(var != nullptr);
 +  return var->castlingDroppedPiece;
 +}
 +
 +inline File Position::castling_kingside_file() const {
 +  assert(var != nullptr);
 +  return var->castlingKingsideFile;
 +}
 +
 +inline File Position::castling_queenside_file() const {
 +  assert(var != nullptr);
 +  return var->castlingQueensideFile;
 +}
 +
- inline Rank Position::castling_rank() const {
++inline Rank Position::castling_rank(Color c) const {
 +  assert(var != nullptr);
-   return var->castlingRank;
++  return relative_rank(c, var->castlingRank, max_rank());
 +}
 +
 +inline bool Position::checking_permitted() const {
 +  assert(var != nullptr);
 +  return var->checking;
 +}
 +
 +inline bool Position::must_capture() const {
 +  assert(var != nullptr);
 +  return var->mustCapture;
 +}
 +
 +inline bool Position::must_drop() const {
 +  assert(var != nullptr);
 +  return var->mustDrop;
 +}
 +
 +inline bool Position::piece_drops() const {
 +  assert(var != nullptr);
 +  return var->pieceDrops;
 +}
 +
 +inline bool Position::drop_loop() const {
 +  assert(var != nullptr);
 +  return var->dropLoop;
 +}
 +
 +inline bool Position::captures_to_hand() const {
 +  assert(var != nullptr);
 +  return var->capturesToHand;
 +}
 +
 +inline bool Position::first_rank_drops() const {
 +  assert(var != nullptr);
 +  return var->firstRankDrops;
 +}
 +
 +inline bool Position::drop_on_top() const {
 +  assert(var != nullptr);
 +  return var->dropOnTop;
 +}
 +
 +inline Bitboard Position::drop_region(Color c) const {
 +  assert(var != nullptr);
 +  return c == WHITE ? var->whiteDropRegion : var->blackDropRegion;
 +}
 +
 +inline Bitboard Position::drop_region(Color c, PieceType pt) const {
 +  Bitboard b = drop_region(c) & board_bb();
 +
 +  // Connect4-style drops
 +  if (drop_on_top())
 +      b &= shift<NORTH>(pieces()) | Rank1BB;
 +  // Pawns on back ranks
 +  if (pt == PAWN)
 +  {
 +      b &= ~promotion_zone_bb(c, promotion_rank(), max_rank());
 +      if (!first_rank_drops())
 +          b &= ~rank_bb(relative_rank(c, RANK_1, max_rank()));
 +  }
 +  // Doubled shogi pawns
 +  if (pt == SHOGI_PAWN && !shogi_doubled_pawn())
 +      for (File f = FILE_A; f <= max_file(); ++f)
 +          if (file_bb(f) & pieces(c, pt))
 +              b &= ~file_bb(f);
 +  // Sittuyin rook drops
 +  if (pt == ROOK && sittuyin_rook_drop())
 +      b &= rank_bb(relative_rank(c, RANK_1, max_rank()));
 +
 +  return b;
 +}
 +
 +inline bool Position::sittuyin_rook_drop() const {
 +  assert(var != nullptr);
 +  return var->sittuyinRookDrop;
 +}
 +
 +inline bool Position::drop_opposite_colored_bishop() const {
 +  assert(var != nullptr);
 +  return var->dropOppositeColoredBishop;
 +}
 +
 +inline bool Position::drop_promoted() const {
 +  assert(var != nullptr);
 +  return var->dropPromoted;
 +}
 +
 +inline bool Position::shogi_doubled_pawn() const {
 +  assert(var != nullptr);
 +  return var->shogiDoubledPawn;
 +}
 +
 +inline bool Position::immobility_illegal() const {
 +  assert(var != nullptr);
 +  return var->immobilityIllegal;
 +}
 +
 +inline bool Position::gating() const {
 +  assert(var != nullptr);
 +  return var->gating;
 +}
 +
 +inline bool Position::seirawan_gating() const {
 +  assert(var != nullptr);
 +  return var->seirawanGating;
 +}
 +
 +inline bool Position::cambodian_moves() const {
 +  assert(var != nullptr);
 +  return var->cambodianMoves;
 +}
 +
 +inline int Position::n_move_rule() const {
 +  assert(var != nullptr);
 +  return var->nMoveRule;
 +}
 +
 +inline int Position::n_fold_rule() const {
 +  assert(var != nullptr);
 +  return var->nFoldRule;
 +}
 +
 +inline Value Position::stalemate_value(int ply) const {
 +  assert(var != nullptr);
 +  return convert_mate_value(var->stalemateValue, ply);
 +}
 +
 +inline Value Position::checkmate_value(int ply) const {
 +  assert(var != nullptr);
 +  // Check for illegal mate by shogi pawn drop
 +  if (    var->shogiPawnDropMateIllegal
 +      && !(checkers() & ~pieces(SHOGI_PAWN))
 +      && !st->capturedPiece
 +      &&  st->pliesFromNull > 0
 +      && (st->materialKey != st->previous->materialKey))
 +  {
 +      return mate_in(ply);
 +  }
 +  // Check for shatar mate rule
 +  if (var->shatarMateRule)
 +  {
 +      // Mate by knight is illegal
 +      if (!(checkers() & ~pieces(KNIGHT)))
 +          return mate_in(ply);
 +
 +      StateInfo* stp = st;
 +      while (stp->checkersBB)
 +      {
 +          // Return mate score if there is at least one shak in series of checks
 +          if (stp->shak)
 +              return convert_mate_value(var->checkmateValue, ply);
 +
 +          if (stp->pliesFromNull < 2)
 +              break;
 +
 +          stp = stp->previous->previous;
 +      }
 +      // Niol
 +      return VALUE_DRAW;
 +  }
 +  // Return mate value
 +  return convert_mate_value(var->checkmateValue, ply);
 +}
 +
 +inline Value Position::bare_king_value(int ply) const {
 +  assert(var != nullptr);
 +  return convert_mate_value(var->bareKingValue, ply);
 +}
 +
 +inline Value Position::extinction_value(int ply) const {
 +  assert(var != nullptr);
 +  return convert_mate_value(var->extinctionValue, ply);
 +}
 +
 +inline bool Position::bare_king_move() const {
 +  assert(var != nullptr);
 +  return var->bareKingMove;
 +}
 +
 +inline const std::set<PieceType>& Position::extinction_piece_types() const {
 +  assert(var != nullptr);
 +  return var->extinctionPieceTypes;
 +}
 +
 +inline PieceType Position::capture_the_flag_piece() const {
 +  assert(var != nullptr);
 +  return var->flagPiece;
 +}
 +
 +inline Bitboard Position::capture_the_flag(Color c) const {
 +  assert(var != nullptr);
 +  return c == WHITE ? var->whiteFlag : var->blackFlag;
 +}
 +
 +inline bool Position::flag_move() const {
 +  assert(var != nullptr);
 +  return var->flagMove;
 +}
 +
 +inline bool Position::check_counting() const {
 +  assert(var != nullptr);
 +  return var->checkCounting;
 +}
 +
 +inline int Position::connect_n() const {
 +  assert(var != nullptr);
 +  return var->connectN;
 +}
 +
 +inline CheckCount Position::checks_remaining(Color c) const {
 +  return st->checksRemaining[c];
 +}
 +
 +inline CountingRule Position::counting_rule() const {
 +  assert(var != nullptr);
 +  return var->countingRule;
 +}
 +
 +inline bool Position::is_immediate_game_end() const {
 +  Value result;
 +  return is_immediate_game_end(result);
 +}
 +
 +inline bool Position::is_game_end(Value& result, int ply) const {
 +  return is_immediate_game_end(result, ply) || is_optional_game_end(result, ply);
 +}
 +
  inline Color Position::side_to_move() const {
    return sideToMove;
  }
diff --cc src/psqt.cpp
Simple merge
diff --cc src/search.cpp
@@@ -169,8 -167,7 +167,8 @@@ namespace 
  
      for (const auto& m : MoveList<LEGAL>(pos))
      {
 +        assert(pos.pseudo_legal(m));
-         if (Root && depth <= ONE_PLY)
+         if (Root && depth <= 1)
              cnt = 1, nodes++;
          else
          {
@@@ -622,15 -618,12 +621,15 @@@ namespace 
  
      if (!rootNode)
      {
 +        Value variantResult;
 +        if (pos.is_game_end(variantResult, ss->ply))
 +            return variantResult;
 +
          // Step 2. Check for aborted search and immediate draw
          if (   Threads.stop.load(std::memory_order_relaxed)
 -            || pos.is_draw(ss->ply)
              || ss->ply >= MAX_PLY)
              return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos)
-                                                     : value_draw(depth, pos.this_thread());
+                                                     : value_draw(pos.this_thread());
  
          // Step 3. Mate distance pruning. Even if we mate at the next move our score
          // would be at best mate_in(ss->ply+1), but if alpha is already bigger because
  
      // Step 7. Razoring (~2 Elo)
      if (   !rootNode // The required rootNode PV handling is not available in qsearch
-         &&  depth < 2 * ONE_PLY
+         &&  depth < 2
 +        && !pos.must_capture()
 +        && !pos.capture_the_flag_piece()
 +        && !pos.check_counting()
          &&  eval <= alpha - RazorMargin)
          return qsearch<NT>(pos, ss, alpha, beta);
  
      improving =   ss->staticEval >= (ss-2)->staticEval
                 || (ss-2)->staticEval == VALUE_NONE;
  
 +    // Skip early pruning in case of mandatory capture
 +    if (pos.must_capture() && MoveList<CAPTURES>(pos).size())
 +        goto moves_loop;
 +
      // Step 8. Futility pruning: child node (~30 Elo)
      if (   !PvNode
-         &&  depth < 7 * ONE_PLY
+         &&  depth < 7
 -        &&  eval - futility_margin(depth, improving) >= beta
 +        && !(   pos.extinction_value() == -VALUE_MATE
 +             && pos.extinction_piece_types().find(ALL_PIECES) == pos.extinction_piece_types().end())
 +        && (pos.checking_permitted() || !pos.capture_the_flag_piece())
 +        &&  eval - futility_margin(depth, improving) * (1 + pos.check_counting()) >= beta
          &&  eval < VALUE_KNOWN_WIN) // Do not return unproven wins
          return eval;
  
          && (ss-1)->statScore < 22661
          &&  eval >= beta
          &&  eval >= ss->staticEval
-         &&  ss->staticEval >= beta - 33 * depth / ONE_PLY + 299 - improving * 30
+         &&  ss->staticEval >= beta - 33 * depth + 299 - improving * 30
          && !excludedMove
          &&  pos.non_pawn_material(us)
 +        && (pos.pieces(~us) ^ pos.pieces(~us, PAWN))
 +        && (pos.pieces() ^ pos.pieces(BREAKTHROUGH_PIECE) ^ pos.pieces(CLOBBER_PIECE))
          && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
      {
          assert(eval - beta >= 0);
  
          // Null move dynamic reduction based on depth and value
-         Depth R = ((835 - 150 * !pos.checking_permitted() + 70 * depth / ONE_PLY) / 256 + std::min(int(eval - beta) / 185, 3)) * ONE_PLY;
 -        Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3);
++        Depth R = (835 - 150 * !pos.checking_permitted() + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3);
  
          ss->currentMove = MOVE_NULL;
-         ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
+         ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
  
          pos.do_null_move(st);
  
      // If we have a good enough capture and a reduced search returns a value
      // much above beta, we can (almost) safely prune the previous move.
      if (   !PvNode
-         &&  depth >= 5 * ONE_PLY
+         &&  depth >= 5
 +        &&  (pos.pieces() ^ pos.pieces(CLOBBER_PIECE))
          &&  abs(beta) < VALUE_MATE_IN_MAX_PLY)
      {
 -        Value raisedBeta = std::min(beta + 191 - 46 * improving, VALUE_INFINITE);
 +        Value raisedBeta = std::min(beta + 191 * (1 + pos.check_counting() + (pos.extinction_value() != VALUE_NONE)) - 46 * improving, VALUE_INFINITE);
          MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory);
          int probCutCount = 0;
  
                  probCutCount++;
  
                  ss->currentMove = move;
-                 ss->continuationHistory = &thisThread->continuationHistory[history_slot(pos.moved_piece(move))][to_sq(move)];
-                 assert(depth >= 5 * ONE_PLY);
+                 ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+                                                                           [captureOrPromotion]
 -                                                                          [pos.moved_piece(move)]
++                                                                          [history_slot(pos.moved_piece(move))]
+                                                                           [to_sq(move)];
  
                  pos.do_move(move, st);
  
      }
  
      // Step 11. Internal iterative deepening (~2 Elo)
-     if (depth >= (7 - 2 * pos.captures_to_hand()) * ONE_PLY && !ttMove)
 -    if (depth >= 7 && !ttMove)
++    if (depth >= (7 - 2 * pos.captures_to_hand()) && !ttMove)
      {
-         search<NT>(pos, ss, alpha, beta, depth - (7 - 2 * pos.captures_to_hand()) * ONE_PLY, cutNode);
 -        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;
@@@ -956,8 -943,8 +963,8 @@@ moves_loop: // When in check, search st
        ss->moveCount = ++moveCount;
  
        if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
-           sync_cout << "info depth " << depth / ONE_PLY
+           sync_cout << "info depth " << depth
 -                    << " currmove " << UCI::move(move, pos.is_chess960())
 +                    << " currmove " << UCI::move(pos, move)
                      << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
        if (PvNode)
            (ss+1)->pv = nullptr;
  
        // Shuffle extension
        else if (   PvNode
 +               && pos.n_move_rule()
                 && pos.rule50_count() > 18
-                && depth < 3 * ONE_PLY
+                && depth < 3
                 && ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4)  // To avoid too many extensions
-           extension = ONE_PLY;
+           extension = 1;
  
        // Passed pawn extension
        else if (   move == ss->killers[0]
                 && pos.advanced_pawn_push(move)
                 && pos.pawn_passed(us, to_sq(move)))
-           extension = ONE_PLY;
+           extension = 1;
+       // Castling extension
+       if (type_of(move) == CASTLING)
+           extension = 1;
  
 +      // Losing chess capture extension
 +      else if (    pos.must_capture()
 +               &&  pos.capture(move)
 +               &&  MoveList<CAPTURES>(pos).size() == 1)
-           extension = ONE_PLY;
++          extension = 1;
 +
        // Calculate new depth for this move
-       newDepth = depth - ONE_PLY + extension;
+       newDepth = depth - 1 + extension;
  
        // Step 14. Pruning at shallow depth (~170 Elo)
        if (  !rootNode
            && bestValue > VALUE_MATED_IN_MAX_PLY)
        {
            // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold
-           moveCountPruning = moveCount >= futility_move_count(improving, depth / ONE_PLY)
 -          moveCountPruning = moveCount >= futility_move_count(improving, depth);
++          moveCountPruning = moveCount >= futility_move_count(improving, depth)
 +                            || (pos.must_capture() && (moveCountPruning || (pos.capture(move) && pos.legal(move))));
  
            if (   !captureOrPromotion
                && !givesCheck
                    continue;
            }
            else if (  !(givesCheck && extension)
 -                   && !pos.see_ge(move, Value(-199) * depth)) // (~20 Elo)
 +                   && !pos.must_capture()
-                    && !pos.see_ge(move, Value(-199 - 120 * pos.captures_to_hand()) * (depth / ONE_PLY))) // (~20 Elo)
++                   && !pos.see_ge(move, Value(-199 - 120 * pos.captures_to_hand()) * depth)) // (~20 Elo)
                    continue;
        }
  
  
        // Update the current move (this must be done after singular extension search)
        ss->currentMove = move;
-       ss->continuationHistory = &thisThread->continuationHistory[history_slot(movedPiece)][to_sq(move)];
+       ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+                                                                 [captureOrPromotion]
 -                                                                [movedPiece]
++                                                                [history_slot(movedPiece)]
+                                                                 [to_sq(move)];
  
        // Step 15. Make the move
        pos.do_move(move, st, givesCheck);
  
            // Decrease reduction if opponent's move count is high (~10 Elo)
            if ((ss-1)->moveCount > 15)
-               r -= ONE_PLY;
+               r--;
  
            // Decrease reduction if ttMove has been singularly extended
-           r -= singularLMR * ONE_PLY;
+           if (singularLMR)
+               r -= 2;
  
 -          if (!captureOrPromotion)
 +          if (!captureOrPromotion && !(pos.must_capture() && MoveList<CAPTURES>(pos).size()))
            {
                // Increase reduction if ttMove is a capture (~0 Elo)
                if (ttCapture)
                // hence break make_move(). (~5 Elo)
                else if (    type_of(move) == NORMAL
                         && !pos.see_ge(reverse_move(move)))
-                   r -= 2 * ONE_PLY;
+                   r -= 2;
  
                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)]
                               - 4729;
  
                // Reset statScore to zero if negative and most stats shows >= 0
        }
  
        ss->currentMove = move;
-       ss->continuationHistory = &thisThread->continuationHistory[history_slot(pos.moved_piece(move))][to_sq(move)];
+       ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+                                                                 [captureOrPromotion]
 -                                                                [pos.moved_piece(move)]
++                                                                [history_slot(pos.moved_piece(move))]
+                                                                 [to_sq(move)];
  
        // Make and search the move
        pos.do_move(move, st, givesCheck);
Simple merge
diff --cc src/thread.cpp
@@@ -201,9 -208,9 +208,9 @@@ void ThreadPool::start_thinking(Positio
    for (Thread* th : *this)
    {
        th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0;
-       th->rootDepth = th->completedDepth = DEPTH_ZERO;
+       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/tt.cpp
@@@ -35,11 -35,9 +35,9 @@@ TranspositionTable TT; // Our global tr
  
  void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
  
-   assert(d / ONE_PLY * ONE_PLY == d);
    // Preserve any existing move for the same position
    if (m || (k >> 48) != key16)
 -      move16 = (uint16_t)m;
 +      move32 = (uint32_t)m;
  
    // Overwrite less valuable entries
    if (  (k >> 48) != key16
diff --cc src/tt.h
+++ b/src/tt.h
  
  struct TTEntry {
  
 -  Move  move()  const { return (Move )move16; }
 +  Move  move()  const { return (Move )move32; }
    Value value() const { return (Value)value16; }
    Value eval()  const { return (Value)eval16; }
-   Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_OFFSET; }
+   Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; }
    bool is_pv() const { return (bool)(genBound8 & 0x4); }
    Bound bound() const { return (Bound)(genBound8 & 0x3); }
    void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
diff --cc src/types.h
@@@ -374,35 -203,19 +375,31 @@@ enum RiderType 
  
  extern Value PieceValue[PHASE_NB][PIECE_NB];
  
- enum Depth : int {
+ typedef int Depth;
  
-   ONE_PLY = 1,
+ enum : int {
  
-   DEPTH_ZERO          =  0 * ONE_PLY,
-   DEPTH_QS_CHECKS     =  0 * ONE_PLY,
-   DEPTH_QS_NO_CHECKS  = -1 * ONE_PLY,
-   DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
+   DEPTH_QS_CHECKS     =  0,
+   DEPTH_QS_NO_CHECKS  = -1,
+   DEPTH_QS_RECAPTURES = -5,
  
-   DEPTH_NONE   = -6 * ONE_PLY,
+   DEPTH_NONE   = -6,
    DEPTH_OFFSET = DEPTH_NONE,
-   DEPTH_MAX    = MAX_PLY * ONE_PLY
  };
  
- static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
  enum Square : int {
 +#ifdef LARGEBOARDS
 +  SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_I1, SQ_J1, SQ_K1, SQ_L1,
 +  SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_I2, SQ_J2, SQ_K2, SQ_L2,
 +  SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, SQ_I3, SQ_J3, SQ_K3, SQ_L3,
 +  SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4, SQ_I4, SQ_J4, SQ_K4, SQ_L4,
 +  SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5, SQ_I5, SQ_J5, SQ_K5, SQ_L5,
 +  SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6, SQ_I6, SQ_J6, SQ_K6, SQ_L6,
 +  SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7, SQ_I7, SQ_J7, SQ_K7, SQ_L7,
 +  SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_I8, SQ_J8, SQ_K8, SQ_L8,
 +  SQ_A9, SQ_B9, SQ_C9, SQ_D9, SQ_E9, SQ_F9, SQ_G9, SQ_H9, SQ_I9, SQ_J9, SQ_K9, SQ_L9,
 +  SQ_A10, SQ_B10, SQ_C10, SQ_D10, SQ_E10, SQ_F10, SQ_G10, SQ_H10, SQ_I10, SQ_J10, SQ_K10, SQ_L10,
 +#else
    SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
    SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
    SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
@@@ -577,25 -351,17 +572,21 @@@ constexpr Color operator~(Color c) 
  }
  
  constexpr Square operator~(Square s) {
 +#ifdef LARGEBOARDS
 +  return Square(s - FILE_NB * (s / FILE_NB * 2 - RANK_MAX)); // Vertical flip SQ_A1 -> SQ_A10
 +#else
    return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
 +#endif
  }
  
- constexpr File operator~(File f) {
-   return File(FILE_MAX - f); // Horizontal flip FILE_A -> FILE_H
- }
- constexpr Rank operator~(Rank r) {
-   return Rank(RANK_MAX - r); // Vertical flip Rank_1 -> Rank_8
- }
  constexpr Piece operator~(Piece pc) {
 -  return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
 +  return Piece(pc ^ PIECE_TYPE_NB); // Swap color of piece BLACK KNIGHT -> WHITE KNIGHT
  }
  
+ inline File map_to_queenside(File f) {
+   return std::min(f, File(FILE_H - f)); // Map files ABCDEFGH to files ABCDDCBA
+ }
  constexpr CastlingRights operator&(Color c, CastlingRights cr) {
    return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr);
  }
diff --cc src/uci.cpp
@@@ -199,10 -191,8 +199,9 @@@ void UCI::loop(int argc, char* argv[]) 
    Position pos;
    string token, cmd;
    StateListPtr states(new std::deque<StateInfo>(1));
-   auto uiThread = std::make_shared<Thread>(0);
  
 -  pos.set(StartFEN, false, &states->back(), Threads.main());
 +  assert(variants.find(Options["UCI_Variant"])->second != nullptr);
-   pos.set(variants.find(Options["UCI_Variant"])->second, variants.find(Options["UCI_Variant"])->second->startFen, false, &states->back(), uiThread.get());
++  pos.set(variants.find(Options["UCI_Variant"])->second, variants.find(Options["UCI_Variant"])->second->startFen, false, &states->back(), Threads.main());
  
    for (int i = 1; i < argc; ++i)
        cmd += std::string(argv[i]) + " ";
        else if (token == "setoption")  setoption(is);
        else if (token == "go")         go(pos, is, states);
        else if (token == "position")   position(pos, is, states);
 -      else if (token == "ucinewgame") Search::clear();
 +      else if (token == "ucinewgame" || token == "usinewgame") Search::clear();
        else if (token == "isready")    sync_cout << "readyok" << sync_endl;
  
-       // Additional custom non-UCI commands, mainly for debugging
+       // Additional custom non-UCI commands, mainly for debugging.
+       // Do not use these commands during a search!
        else if (token == "flip")  pos.flip();
        else if (token == "bench") bench(pos, is, states);
        else if (token == "d")     sync_cout << pos << sync_endl;