Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 19 Sep 2020 12:45:56 +0000 (14:45 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 19 Sep 2020 12:45:56 +0000 (14:45 +0200)
No functional change.

17 files changed:
1  2 
src/bitboard.h
src/endgame.cpp
src/evaluate.cpp
src/material.cpp
src/material.h
src/misc.cpp
src/pawns.cpp
src/position.cpp
src/position.h
src/psqt.cpp
src/search.cpp
src/thread.cpp
src/timeman.cpp
src/tt.cpp
src/tt.h
src/types.h
src/ucioption.cpp

diff --cc src/bitboard.h
@@@ -370,33 -276,20 +373,36 @@@ template<> inline int distance<File>(Sq
  template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
  template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
  
 -inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
 -inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
 +inline int edge_distance(File f, File maxFile = FILE_H) { return std::min(f, File(maxFile - f)); }
 +inline int edge_distance(Rank r, Rank maxRank = RANK_8) { return std::min(r, Rank(maxRank - r)); }
  
- /// Return the target square bitboard if we do not step off the board, empty otherwise
+ /// safe_destination() returns the bitboard of target square for the given step
+ /// from the given square. If the step is off the board, returns empty bitboard.
  
  inline Bitboard safe_destination(Square s, int step)
  {
      Square to = Square(s + step);
 -    return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
 +    return is_ok(to) && distance(s, to) <= 3 ? square_bb(to) : Bitboard(0);
 +}
 +
 +template<RiderType R>
 +inline Bitboard rider_attacks_bb(Square s, Bitboard occupied) {
 +
 +  assert(R == RIDER_BISHOP || R == RIDER_ROOK_H || R == RIDER_ROOK_V || R == RIDER_CANNON_H || R == RIDER_CANNON_V
 +         || R == RIDER_HORSE || R == RIDER_ELEPHANT || R == RIDER_JANGGI_ELEPHANT);
 +  const Magic& m =  R == RIDER_ROOK_H ? RookMagicsH[s]
 +                  : R == RIDER_ROOK_V ? RookMagicsV[s]
 +                  : R == RIDER_CANNON_H ? CannonMagicsH[s]
 +                  : R == RIDER_CANNON_V ? CannonMagicsV[s]
 +                  : R == RIDER_HORSE ? HorseMagics[s]
 +                  : R == RIDER_ELEPHANT ? ElephantMagics[s]
 +                  : R == RIDER_JANGGI_ELEPHANT ? JanggiElephantMagics[s]
 +                  : BishopMagics[s];
 +  return m.attacks[m.index(occupied)];
  }
  
  /// attacks_bb(Square) returns the pseudo attacks of the give piece type
  /// assuming an empty board.
  
@@@ -405,9 -298,10 +411,10 @@@ inline Bitboard attacks_bb(Square s) 
  
    assert((Pt != PAWN) && (is_ok(s)));
  
 -  return PseudoAttacks[Pt][s];
 +  return PseudoAttacks[WHITE][Pt][s];
  }
  
  /// attacks_bb(Square, Bitboard) returns the attacks by the given piece
  /// assuming the board is occupied according to the passed Bitboard.
  /// Sliding piece attacks do not continue passed an occupied square.
diff --cc src/endgame.cpp
Simple merge
@@@ -131,13 -126,11 +131,15 @@@ namespace 
      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(2, 4);
 +  constexpr Score EndgameKingProximity = S(0, 10);
 +
    // Assorted bonuses and penalties
-   constexpr Score BishopPawns         = S(  3,  7);
+   constexpr Score BishopKingProtector = S(  6,  9);
    constexpr Score BishopOnKingRing    = S( 24,  0);
+   constexpr Score BishopOutpost       = S( 30, 23);
+   constexpr Score BishopPawns         = S(  3,  7);
    constexpr Score BishopXRayPawns     = S(  4,  5);
    constexpr Score CorneredBishop      = S( 50, 50);
    constexpr Score FlankAttacks        = S(  8,  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;
      constexpr Direction Down = -pawn_push(Us);
      return score;
    }
  
 +  // Evaluation::hand() scores pieces of a given color and type in hand
 +  template<Tracing T> template<Color Us>
 +  Score Evaluation<T>::hand(PieceType pt) {
 +
 +    constexpr Color Them = ~Us;
 +
 +    Score score = SCORE_ZERO;
 +
 +    if (pos.count_in_hand(Us, pt))
 +    {
 +        Bitboard b = pos.drop_region(Us, pt) & ~pos.pieces() & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]);
 +        if ((b & kingRing[Them]) && pt != SHOGI_PAWN)
 +        {
 +            kingAttackersCountInHand[Us] += pos.count_in_hand(Us, pt);
 +            kingAttackersWeightInHand[Us] += KingAttackWeights[std::min(int(pt), QUEEN + 1)] * pos.count_in_hand(Us, pt);
 +            kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
 +        }
 +        Bitboard theirHalf = pos.board_bb() & ~forward_ranks_bb(Them, relative_rank(Them, Rank((pos.max_rank() - 1) / 2), pos.max_rank()));
 +        mobility[Us] += DropMobility * popcount(b & theirHalf & ~attackedBy[Them][ALL_PIECES]);
 +        if (pos.promoted_piece_type(pt) != NO_PIECE_TYPE && pos.drop_promoted())
 +            score += make_score(std::max(PieceValue[MG][pos.promoted_piece_type(pt)] - PieceValue[MG][pt], VALUE_ZERO),
 +                                std::max(PieceValue[EG][pos.promoted_piece_type(pt)] - PieceValue[EG][pt], VALUE_ZERO)) / 4 * pos.count_in_hand(Us, pt);
 +        if (pos.enclosing_drop())
 +            mobility[Us] += make_score(500, 500) * popcount(b);
 +
 +        // Reduce score if there is a deficit of gates
 +        if (pos.seirawan_gating() && !pos.piece_drops() && pos.count_in_hand(Us, ALL_PIECES) > popcount(pos.gates(Us)))
 +            score -= make_score(200, 900) / pos.count_in_hand(Us, ALL_PIECES) * (pos.count_in_hand(Us, ALL_PIECES) - popcount(pos.gates(Us)));
 +
 +        if (pt == SHOGI_PAWN && !pos.shogi_doubled_pawn())
 +            score -= make_score(50, 20) * std::max(pos.count_with_hand(Us, SHOGI_PAWN) - pos.max_file() - 1, 0);
 +    }
 +
 +    return score;
 +  }
  
    // Evaluation::king() assigns bonuses and penalties to a king of a given color
    template<Tracing T> template<Color Us>
    Score Evaluation<T>::king() const {
  
    }
  
  
 +  // Evaluation::variant() computes variant-specific evaluation bonuses for a given side.
 +
 +  template<Tracing T> template<Color Us>
 +  Score Evaluation<T>::variant() const {
 +
 +    constexpr Color Them = ~Us;
 +    constexpr Direction Down = pawn_push(Them);
 +
 +    Score score = SCORE_ZERO;
 +
 +    // Capture the flag
 +    if (pos.capture_the_flag(Us))
 +    {
 +        PieceType ptCtf = pos.capture_the_flag_piece();
 +        Bitboard ctfPieces = pos.pieces(Us, ptCtf);
 +        Bitboard ctfTargets = pos.capture_the_flag(Us) & pos.board_bb();
 +        Bitboard onHold = 0;
 +        Bitboard onHold2 = 0;
 +        Bitboard processed = 0;
 +        Bitboard blocked = pos.pieces(Us, PAWN) | attackedBy[Them][ALL_PIECES];
 +        Bitboard doubleBlocked =  attackedBy2[Them]
 +                                | (pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | attackedBy[Them][ALL_PIECES]))
 +                                | (pos.pieces(Them) & pe->pawn_attacks(Them))
 +                                | (pawn_attacks_bb<Them>(pos.pieces(Them, PAWN) & pe->pawn_attacks(Them)));
 +        Bitboard inaccessible = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces(Them, PAWN));
 +        // Traverse all paths of the CTF pieces to the CTF targets.
 +        // Put squares that are attacked or occupied on hold for one iteration.
 +        for (int dist = 0; (ctfPieces || onHold || onHold2) && (ctfTargets & ~processed); dist++)
 +        {
 +            int wins = popcount(ctfTargets & ctfPieces);
 +            if (wins)
 +                score += make_score(4000, 4000) * wins / (wins + dist * dist);
 +            Bitboard current = ctfPieces & ~ctfTargets;
 +            processed |= ctfPieces;
 +            ctfPieces = onHold & ~processed;
 +            onHold = onHold2 & ~processed;
 +            onHold2 = 0;
 +            while (current)
 +            {
 +                Square s = pop_lsb(&current);
 +                Bitboard attacks = (  (PseudoAttacks[Us][ptCtf][s] & pos.pieces())
 +                                    | (PseudoMoves[Us][ptCtf][s] & ~pos.pieces())) & ~processed & pos.board_bb();
 +                ctfPieces |= attacks & ~blocked;
 +                onHold |= attacks & ~doubleBlocked;
 +                onHold2 |= attacks & ~inaccessible;
 +            }
 +        }
 +    }
 +
 +    // nCheck
 +    if (pos.check_counting())
 +    {
 +        int remainingChecks = pos.checks_remaining(Us);
 +        assert(remainingChecks > 0);
 +        score += make_score(3600, 1000) / (remainingChecks * remainingChecks);
 +    }
 +
 +    // Extinction
 +    if (pos.extinction_value() != VALUE_NONE)
 +    {
 +        for (PieceType pt : pos.extinction_piece_types())
 +            if (pt != ALL_PIECES)
 +            {
 +                int denom = std::max(pos.count(Us, pt) - pos.extinction_piece_count(), 1);
 +                if (pos.count(Them, pt) >= pos.extinction_opponent_piece_count() || pos.two_boards())
 +                    score += make_score(1000000 / (500 + PieceValue[MG][pt]),
 +                                        1000000 / (500 + PieceValue[EG][pt])) / (denom * denom)
 +                            * (pos.extinction_value() / VALUE_MATE);
 +            }
 +            else if (pos.extinction_value() == VALUE_MATE)
 +                score += make_score(pos.non_pawn_material(Us), pos.non_pawn_material(Us)) / pos.count<ALL_PIECES>(Us);
 +    }
 +
 +    // Connect-n
 +    if (pos.connect_n() > 0)
 +    {
 +        for (Direction d : {NORTH, NORTH_EAST, EAST, SOUTH_EAST})
 +        {
 +            // Find sufficiently large gaps
 +            Bitboard b = pos.board_bb() & ~pos.pieces(Them);
 +            for (int i = 1; i < pos.connect_n(); i++)
 +                b &= shift(d, b);
 +            // Count number of pieces per gap
 +            while (b)
 +            {
 +                Square s = pop_lsb(&b);
 +                int c = 0;
 +                for (int j = 0; j < pos.connect_n(); j++)
 +                    if (pos.pieces(Us) & (s - j * d))
 +                        c++;
 +                score += make_score(200, 200)  * c / (pos.connect_n() - c) / (pos.connect_n() - c);
 +            }
 +        }
 +    }
 +
 +    // Potential piece flips
 +    if (pos.flip_enclosed_pieces())
 +    {
 +        // Stable pieces
 +        if (pos.flip_enclosed_pieces() == REVERSI)
 +        {
 +            Bitboard edges = (FileABB | file_bb(pos.max_file()) | Rank1BB | rank_bb(pos.max_rank())) & pos.board_bb();
 +            Bitboard edgePieces = pos.pieces(Us) & edges;
 +            while (edgePieces)
 +            {
 +                Bitboard connectedEdge = attacks_bb(Us, ROOK, pop_lsb(&edgePieces), ~(pos.pieces(Us) & edges)) & edges;
 +                if (!more_than_one(connectedEdge & ~pos.pieces(Us)))
 +                    score += make_score(300, 300);
 +                else if (!(connectedEdge & ~pos.pieces()))
 +                    score += make_score(200, 200);
 +            }
 +        }
 +
 +        // Unstable
 +        Bitboard unstable = 0;
 +        Bitboard drops = pos.drop_region(Them, IMMOBILE_PIECE);
 +        while (drops)
 +        {
 +            Square s = pop_lsb(&drops);
 +            if (pos.flip_enclosed_pieces() == REVERSI)
 +            {
 +                Bitboard b = attacks_bb(Them, QUEEN, s, ~pos.pieces(Us)) & ~PseudoAttacks[Them][KING][s] & pos.pieces(Them);
 +                while(b)
 +                    unstable |= between_bb(s, pop_lsb(&b));
 +            }
 +            else
 +                unstable |= PseudoAttacks[Them][KING][s] & pos.pieces(Us);
 +        }
 +        score -= make_score(200, 200) * popcount(unstable);
 +    }
 +
 +    if (T)
 +        Trace::add(VARIANT, Us, score);
 +
 +    return score;
 +  }
 +
 +
    // Evaluation::winnable() adjusts the mg and eg score components based on the
-   // known attacking/defending status of the players.
-   // A single value is derived from the mg and eg values and returned.
+   // known attacking/defending status of the players. A single value is derived
+   // by interpolation from the mg and eg values and returned.
  
    template<Tracing T>
    Value Evaluation<T>::winnable(Score score) const {
      initialize<WHITE>();
      initialize<BLACK>();
  
-     // Pieces should be evaluated first (populate attack tables).
+     // Pieces evaluated first (also populates attackedBy, attackedBy2).
 -    // Note that the order of evaluation of the terms is left unspecified.
 -    score +=  pieces<WHITE, KNIGHT>() - pieces<BLACK, KNIGHT>()
 -            + pieces<WHITE, BISHOP>() - pieces<BLACK, BISHOP>()
 -            + pieces<WHITE, ROOK  >() - pieces<BLACK, ROOK  >()
 -            + pieces<WHITE, QUEEN >() - pieces<BLACK, QUEEN >();
 +    // For unused piece types, we still need to set attack bitboard to zero.
 +    for (PieceType pt = KNIGHT; pt < KING; ++pt)
 +        if (pt != SHOGI_PAWN)
 +            score += pieces<WHITE>(pt) - pieces<BLACK>(pt);
 +
 +    // Evaluate pieces in hand once attack tables are complete
 +    if (pos.piece_drops() || pos.seirawan_gating())
 +        for (PieceType pt = PAWN; pt < KING; ++pt)
 +            score += hand<WHITE>(pt) - hand<BLACK>(pt);
  
 -    score += mobility[WHITE] - mobility[BLACK];
 +    score += (mobility[WHITE] - mobility[BLACK]) * (1 + pos.captures_to_hand() + pos.must_capture() + pos.check_counting());
  
      // More complex interactions that require fully populated attack bitboards
      score +=  king<   WHITE>() - king<   BLACK>()
@@@ -88,10 -79,12 +88,12 @@@ namespace 
            && pos.count<PAWN>(~us) >= 1;
    }
  
    /// imbalance() calculates the imbalance by comparing the piece count of each
    /// piece type for both colors.
    template<Color Us>
 -  int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
 +  int imbalance(const Position& pos, const int pieceCount[][PIECE_TYPE_NB]) {
  
      constexpr Color Them = ~Us;
  
      // Second-degree polynomial material imbalance, by Tord Romstad
      for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
      {
 -        if (!pieceCount[Us][pt1])
 +        if (!pieceCount[Us][pt1] || (pos.extinction_value() == VALUE_MATE && pt1 != KNIGHT))
              continue;
  
-         int v = 0;
+         int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1];
  
-         for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
+         for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2)
 -            v +=  QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
 +            v +=  QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] * (pos.must_capture() && pt1 == KNIGHT && pt2 == PAWN ? 2 : pos.check_counting() && pt1 <= BISHOP ? 0 : 1)
                  + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
  
          bonus += pieceCount[Us][pt1] * v;
diff --cc src/material.h
Simple merge
diff --cc src/misc.cpp
Simple merge
diff --cc src/pawns.cpp
Simple merge
@@@ -119,26 -97,15 +119,25 @@@ std::ostream& operator<<(std::ostream& 
  // https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
  
  // First and second hash functions for indexing the cuckoo tables
 +#ifdef LARGEBOARDS
 +inline int H1(Key h) { return h & 0x7fff; }
 +inline int H2(Key h) { return (h >> 16) & 0x7fff; }
 +#else
  inline int H1(Key h) { return h & 0x1fff; }
  inline int H2(Key h) { return (h >> 16) & 0x1fff; }
 +#endif
  
  // Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves
 +#ifdef LARGEBOARDS
 +Key cuckoo[65536];
 +Move cuckooMove[65536];
 +#else
  Key cuckoo[8192];
  Move cuckooMove[8192];
 +#endif
  
  
- /// Position::init() initializes at startup the various arrays used to compute
- /// hash keys.
+ /// Position::init() initializes at startup the various arrays used to compute hash keys
  
  void Position::init() {
  
diff --cc src/position.h
Simple merge
diff --cc src/psqt.cpp
@@@ -104,117 -99,25 +104,117 @@@ constexpr Score PBonus[RANK_NB][FILE_NB
  
  #undef S
  
 -Score psq[PIECE_NB][SQUARE_NB];
 -
 +Score psq[PIECE_NB][SQUARE_NB + 1];
  
- // init() initializes piece-square tables: the white halves of the tables are
- // copied from Bonus[] adding the piece value, then the black halves of the
- // tables are initialized by flipping and changing the sign of the white scores.
+ // PSQT::init() initializes piece-square tables: the white halves of the tables are
+ // copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
+ // the tables are initialized by flipping and changing the sign of the white scores.
 -void init() {
 +void init(const Variant* v) {
 +
 +  PieceType strongestPiece = NO_PIECE_TYPE;
 +  for (PieceType pt : v->pieceTypes)
 +      if (PieceValue[MG][pt] > PieceValue[MG][strongestPiece])
 +          strongestPiece = pt;
 +
 +  Value maxPromotion = VALUE_ZERO;
 +  for (PieceType pt : v->promotionPieceTypes)
 +      maxPromotion = std::max(maxPromotion, PieceValue[EG][pt]);
  
 -  for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
 +  for (PieceType pt = PAWN; pt <= KING; ++pt)
    {
 +      Piece pc = make_piece(WHITE, pt);
 +
        Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
  
 -      for (Square s = SQ_A1; s <= SQ_H8; ++s)
 +      // Consider promotion types in pawn score
 +      if (pt == PAWN)
 +          score -= make_score(0, (QueenValueEg - maxPromotion) / 100);
 +
 +      // Scale slider piece values with board size
 +      const PieceInfo* pi = pieceMap.find(pt)->second;
 +      bool isSlider = pi->sliderQuiet.size() || pi->sliderCapture.size() || pi->hopperQuiet.size() || pi->hopperCapture.size();
 +      bool isPawn = !isSlider && pi->stepsQuiet.size() && !std::any_of(pi->stepsQuiet.begin(), pi->stepsQuiet.end(), [](Direction d) { return d < SOUTH / 2; });
 +      bool isSlowLeaper = !isSlider && !std::any_of(pi->stepsQuiet.begin(), pi->stepsQuiet.end(), [](Direction d) { return dist(d) > 1; });
 +
 +      if (isSlider)
 +      {
 +          constexpr int lc = 5;
 +          constexpr int rm = 5;
 +          constexpr int r0 = rm + RANK_8;
 +          int r1 = rm + (v->maxRank + v->maxFile) / 2;
 +          int leaper = pi->stepsQuiet.size() + pi->stepsCapture.size();
 +          int slider = pi->sliderQuiet.size() + pi->sliderCapture.size() + pi->hopperQuiet.size() + pi->hopperCapture.size();
 +          score = make_score(mg_value(score) * (lc * leaper + r1 * slider) / (lc * leaper + r0 * slider),
 +                             eg_value(score) * (lc * leaper + r1 * slider) / (lc * leaper + r0 * slider));
 +      }
 +
 +      // Increase leapers' value in makpong
 +      if (v->makpongRule)
 +      {
 +          if (std::any_of(pi->stepsCapture.begin(), pi->stepsCapture.end(), [](Direction d) { return dist(d) > 1; })
 +                  && !pi->lameLeaper)
 +              score = make_score(mg_value(score) * 4200 / (3500 + mg_value(score)),
 +                                 eg_value(score) * 4700 / (3500 + mg_value(score)));
 +      }
 +
 +      // For drop variants, halve the piece values
 +      if (v->capturesToHand)
 +          score = make_score(mg_value(score) * 3500 / (7000 + mg_value(score)),
 +                             eg_value(score) * 3500 / (7000 + eg_value(score)));
 +      else if (!v->checking)
 +          score = make_score(mg_value(score) * 2000 / (3500 + mg_value(score)),
 +                             eg_value(score) * 2200 / (3500 + eg_value(score)));
 +      else if (v->twoBoards)
 +          score = make_score(mg_value(score) * 7000 / (7000 + mg_value(score)),
 +                             eg_value(score) * 7000 / (7000 + eg_value(score)));
 +      else if (v->checkCounting)
 +          score = make_score(mg_value(score) * (40000 + mg_value(score)) / 41000,
 +                             eg_value(score) * (30000 + eg_value(score)) / 31000);
 +      else if (pt == strongestPiece)
 +              score += make_score(std::max(QueenValueMg - PieceValue[MG][pt], VALUE_ZERO) / 20,
 +                                  std::max(QueenValueEg - PieceValue[EG][pt], VALUE_ZERO) / 20);
 +
 +      // For antichess variants, use negative piece values
 +      if (   v->extinctionValue == VALUE_MATE
 +          && v->extinctionPieceTypes.find(ALL_PIECES) != v->extinctionPieceTypes.end())
 +          score = -make_score(mg_value(score) / 8, eg_value(score) / 8 / (1 + !pi->sliderCapture.size()));
 +
 +      // Determine pawn rank
 +      std::istringstream ss(v->startFen);
 +      unsigned char token;
 +      Rank rc = v->maxRank;
 +      Rank pawnRank = RANK_2;
 +      while ((ss >> token) && !isspace(token))
 +      {
 +          if (token == '/')
 +              --rc;
 +          else if (token == v->pieceToChar[PAWN] || token == v->pieceToChar[SHOGI_PAWN])
 +              pawnRank = rc;
 +      }
 +
 +      for (Square s = SQ_A1; s <= SQ_MAX; ++s)
        {
 -          File f = File(edge_distance(file_of(s)));
 -          psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
 -                                                     : Bonus[pc][rank_of(s)][f]);
 -          psq[~pc][flip_rank(s)] = -psq[pc][s];
 +          File f = std::max(File(edge_distance(file_of(s), v->maxFile)), FILE_A);
 +          Rank r = rank_of(s);
-           psq[ pc][ s] = score + (  pt == PAWN  ? PBonus[std::min(r, RANK_8)][std::min(file_of(s), FILE_H)]
-                                   : pt == KING  ? KingBonus[Utility::clamp(Rank(r - pawnRank + 1), RANK_1, RANK_8)][std::min(f, FILE_D)] * (1 + v->capturesToHand)
-                                   : pt <= QUEEN ? Bonus[pc][std::min(r, RANK_8)][std::min(f, FILE_D)]
-                                   : pt == HORSE ? Bonus[KNIGHT][std::min(r, RANK_8)][std::min(f, FILE_D)]
-                                   : isSlider    ? make_score(5, 5) * (2 * f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile - 1)
-                                   : isPawn      ? make_score(5, 5) * (2 * f - v->maxFile)
-                                                 : make_score(10, 10) * (1 + isSlowLeaper) * (f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile / 2));
++          psq[ pc][s] = score + (  pt == PAWN  ? PBonus[std::min(r, RANK_8)][std::min(file_of(s), FILE_H)]
++                                 : pt == KING  ? KingBonus[Utility::clamp(Rank(r - pawnRank + 1), RANK_1, RANK_8)][std::min(f, FILE_D)] * (1 + v->capturesToHand)
++                                 : pt <= QUEEN ? Bonus[pc][std::min(r, RANK_8)][std::min(f, FILE_D)]
++                                 : pt == HORSE ? Bonus[KNIGHT][std::min(r, RANK_8)][std::min(f, FILE_D)]
++                                 : isSlider    ? make_score(5, 5) * (2 * f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile - 1)
++                                 : isPawn      ? make_score(5, 5) * (2 * f - v->maxFile)
++                                               : make_score(10, 10) * (1 + isSlowLeaper) * (f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile / 2));
 +          if (pt == SOLDIER && r < v->soldierPromotionRank)
 +              psq[pc][s] -= score * (v->soldierPromotionRank - r) / (4 + f);
 +          if (v->enclosingDrop == REVERSI)
 +          {
 +              if (f == FILE_A && (r == RANK_1 || r == v->maxRank))
 +                  psq[pc][s] += make_score(1000, 1000);
 +          }
 +          psq[~pc][rank_of(s) <= v->maxRank ? flip_rank(s, v->maxRank) : s] = -psq[pc][s];
        }
 +      // pieces in pocket
 +      psq[ pc][SQ_NONE] = score + make_score(35, 10) * (1 + !isSlider);
 +      psq[~pc][SQ_NONE] = -psq[pc][SQ_NONE];
    }
  }
  
diff --cc src/search.cpp
@@@ -555,35 -525,7 +555,35 @@@ void Thread::search() 
            double totalTime = rootMoves.size() == 1 ? 0 :
                               Time.optimum() * fallingEval * reduction * bestMoveInstability;
  
 +          if (completedDepth >= 8 && rootPos.two_boards() && Options["Protocol"] == "xboard")
 +          {
 +              if (Limits.time[us])
 +                  Partner.ptell<FAIRY>("time " + std::to_string((Limits.time[us] - Time.elapsed()) / 10));
 +              if (Limits.time[~us])
 +                  Partner.ptell<FAIRY>("otim " + std::to_string(Limits.time[~us] / 10));
 +              if (!Partner.weDead && bestValue <= VALUE_MATED_IN_MAX_PLY)
 +              {
 +                  Partner.ptell("dead");
 +                  Partner.weDead = true;
 +              }
 +              else if (Partner.weDead && bestValue > VALUE_MATED_IN_MAX_PLY)
 +              {
 +                  Partner.ptell("x");
 +                  Partner.weDead = false;
 +              }
 +              else if (!Partner.weWin && bestValue >= VALUE_MATE_IN_MAX_PLY && Limits.time[~us] < Partner.time * 10)
 +              {
 +                  Partner.ptell("sit");
 +                  Partner.weWin = true;
 +              }
 +              else if (Partner.weWin && (bestValue < VALUE_MATE_IN_MAX_PLY || Limits.time[~us] > Partner.time * 10))
 +              {
 +                  Partner.ptell("x");
 +                  Partner.weWin = false;
 +              }
 +          }
 +
-           // Stop the search if we have exceeded the totalTime, at least 1ms search.
+           // Stop the search if we have exceeded the totalTime, at least 1ms search
            if (Time.elapsed() > totalTime)
            {
                // If we are allowed to ponder do not stop the search now but
@@@ -680,15 -622,12 +680,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 && !ss->inCheck) ? evaluate(pos)
-                                                     : value_draw(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
@@@ -1662,10 -1573,10 +1664,10 @@@ moves_loop: // When in check, search st
         }
      }
  
-     // All legal moves have been searched. A special case: If we're in check
+     // All legal moves have been searched. A special case: if we're in check
      // and no legal moves were found, it is checkmate.
      if (ss->inCheck && bestValue == -VALUE_INFINITE)
 -        return mated_in(ss->ply); // Plies to mate from the root
 +        return pos.checkmate_value(ss->ply); // Plies to mate from the root
  
      tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
                bestValue >= beta ? BOUND_LOWER :
diff --cc src/thread.cpp
Simple merge
diff --cc src/timeman.cpp
  
  TimeManagement Time; // Our global time management object
  
- /// init() is called at the beginning of the search and calculates the bounds
- /// of time allowed for the current game ply.  We currently support:
- //      1) x basetime (+z increment)
- //      2) x moves in y seconds (+z increment)
+ /// TimeManagement::init() is called at the beginning of the search and calculates
+ /// the bounds of time allowed for the current game ply. We currently support:
+ //      1) x basetime (+ z increment)
+ //      2) x moves in y seconds (+ z increment)
  
 -void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
 +void TimeManagement::init(const Position& pos, Search::LimitsType& limits, Color us, int ply) {
  
    TimePoint moveOverhead    = TimePoint(Options["Move Overhead"]);
    TimePoint slowMover       = TimePoint(Options["Slow Mover"]);
diff --cc src/tt.cpp
Simple merge
diff --cc src/tt.h
Simple merge
diff --cc src/types.h
@@@ -649,16 -349,16 +649,16 @@@ constexpr Color operator~(Color c) 
    return Color(c ^ BLACK); // Toggle color
  }
  
- constexpr Square flip_rank(Square s, Rank maxRank = RANK_8) {
 -constexpr Square flip_rank(Square s) { // Swap A1 <-> A8
 -  return Square(s ^ SQ_A8);
++constexpr Square flip_rank(Square s, Rank maxRank = RANK_8) { // Swap A1 <-> A8
 +  return Square(s + NORTH * (maxRank - 2 * (s / NORTH)));
  }
  
- constexpr Square flip_file(Square s, File maxFile = FILE_H) {
 -constexpr Square flip_file(Square s) { // Swap A1 <-> H1
 -  return Square(s ^ SQ_H1);
++constexpr Square flip_file(Square s, File maxFile = FILE_H) { // Swap A1 <-> H1
 +  return Square(s + maxFile - 2 * (s % NORTH));
  }
  
  constexpr Piece operator~(Piece pc) {
-   return Piece(pc ^ PIECE_TYPE_NB); // Swap color of piece BLACK KNIGHT -> WHITE KNIGHT
 -  return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT
++  return Piece(pc ^ PIECE_TYPE_NB);  // Swap color of piece B_KNIGHT <-> W_KNIGHT
  }
  
  constexpr CastlingRights operator&(Color c, CastlingRights cr) {
Simple merge