Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Sun, 26 Jan 2020 16:12:57 +0000 (17:12 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sun, 26 Jan 2020 16:12:57 +0000 (17:12 +0100)
1  2 
src/position.cpp

@@@ -42,48 -42,16 +42,10 @@@ namespace Zobrist 
    Key enpassant[FILE_NB];
    Key castling[CASTLING_RIGHT_NB];
    Key side, noPawns;
 +  Key inHand[PIECE_NB][SQUARE_NB];
 +  Key checks[COLOR_NB][CHECKS_NB];
  }
  
--namespace {
--
- // min_attacker() is a helper function used by see_ge() to locate the least
- // valuable attacker for the side to move, remove the attacker we just found
- // from the bitboards and scan for new X-ray attacks behind it.
- template<PieceType Pt>
- PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
-                        Bitboard& occupied, Bitboard& attackers) {
-   Bitboard b = stmAttackers & byTypeBB[Pt];
-   if (!b)
-       return min_attacker<PieceType(Pt + 1)>(byTypeBB, to, stmAttackers, occupied, attackers);
-   occupied ^= lsb(b); // Remove the attacker from occupied
-   // Add any X-ray attack behind the just removed piece. For instance with
-   // rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8.
-   // Note that new added attackers can be of any color.
-   if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
-       attackers |= attacks_bb<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
-   if (Pt == ROOK || Pt == QUEEN)
-       attackers |= attacks_bb<ROOK>(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]);
-   // X-ray may add already processed pieces because byTypeBB[] is constant: in
-   // the rook example, now attackers contains _again_ rook in a7, so remove it.
-   attackers &= occupied;
-   return Pt;
- }
- template<>
- PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
-   return KING; // No need to update bitboards: it is the last cycle
- }
- } // namespace
 -const string PieceToChar(" PNBRQK  pnbrqk");
 -
 -constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
 -                             B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
 -} // namespace
 -
  
  /// operator<<(Position) returns an ASCII representation of the position
  
@@@ -1679,259 -1014,112 +1641,286 @@@ bool Position::see_ge(Move m, Value thr
    assert(is_ok(m));
  
    // Only deal with normal moves, assume others pass a simple see
 -  if (type_of(m) != NORMAL)
 +  if (type_of(m) != NORMAL && type_of(m) != DROP && type_of(m) != PIECE_PROMOTION)
        return VALUE_ZERO >= threshold;
  
-   Bitboard stmAttackers;
    Square from = from_sq(m), to = to_sq(m);
-   PieceType nextVictim = type_of(m) == DROP ? dropped_piece_type(m) : type_of(piece_on(from));
-   Color us = type_of(m) == DROP ? sideToMove : color_of(piece_on(from));
-   Color stm = ~us; // First consider opponent's move
-   Value balance;   // Values of the pieces taken by us minus opponent's ones
 +  // nCheck
 +  if (check_counting() && color_of(moved_piece(m)) == sideToMove && gives_check(m))
 +      return true;
 +
 +  // Extinction
 +  if (   extinction_value() != VALUE_NONE
 +      && piece_on(to)
 +      && (   (   extinction_piece_types().find(type_of(piece_on(to))) != extinction_piece_types().end()
 +              && pieceCount[piece_on(to)] == 1)
 +          || (   extinction_piece_types().find(ALL_PIECES) != extinction_piece_types().end()
 +              && count<ALL_PIECES>(~sideToMove) == 1)))
 +      return extinction_value() < VALUE_ZERO;
  
-   // The opponent may be able to recapture so this is the best result
-   // we can hope for.
-   balance = PieceValue[MG][piece_on(to)] - threshold;
-   if (balance < VALUE_ZERO)
+   int swap = PieceValue[MG][piece_on(to)] - threshold;
+   if (swap < 0)
        return false;
  
-   // Now assume the worst possible result: that the opponent can
-   // capture our piece for free.
-   balance -= PieceValue[MG][nextVictim];
-   // If it is enough (like in PxQ) then return immediately. Note that
-   // in case nextVictim == KING we always return here, this is ok
-   // if the given move is legal.
-   if (balance >= VALUE_ZERO)
 -  swap = PieceValue[MG][piece_on(from)] - swap;
++  swap = PieceValue[MG][moved_piece(m)] - swap;
+   if (swap <= 0)
        return true;
  
-   // Find all attackers to the destination square, with the moving piece
-   // removed, but possibly an X-ray attacker added behind it.
-   Bitboard occupied = type_of(m) == DROP ? pieces() ^ to : pieces() ^ from ^ to;
-   Bitboard attackers = attackers_to(to, occupied) & occupied;
 -  Bitboard occ = pieces() ^ from ^ to;
 -  Color stm = color_of(piece_on(from));
++  Bitboard occ = (type_of(m) != DROP ? pieces() ^ from : pieces()) ^ to;
++  Color stm = color_of(moved_piece(m));
+   Bitboard attackers = attackers_to(to, occ);
+   Bitboard stmAttackers, bb;
+   int res = 1;
  
 +  // Flying general rule
 +  if (var->flyingGeneral)
 +  {
-       if (attackers & pieces(us, KING))
-           attackers |= attacks_bb(us, ROOK, to, occupied & ~pieces(ROOK)) & pieces(~us, KING);
-       if (attackers & pieces(~us, KING))
-           attackers |= attacks_bb(~us, ROOK, to, occupied & ~pieces(ROOK)) & pieces(us, KING);
++      if (attackers & pieces(stm, KING))
++          attackers |= attacks_bb(stm, ROOK, to, occ & ~pieces(ROOK)) & pieces(~stm, KING);
++      if (attackers & pieces(~stm, KING))
++          attackers |= attacks_bb(~stm, ROOK, to, occ & ~pieces(ROOK)) & pieces(stm, KING);
 +  }
 +
    while (true)
    {
-       stmAttackers = attackers & pieces(stm);
+       stm = ~stm;
+       attackers &= occ;
+       // If stm has no more attackers then give up: stm loses
+       if (!(stmAttackers = attackers & pieces(stm)))
+           break;
  
        // Don't allow pinned pieces to attack (except the king) as long as
-       // any pinners are on their original square.
-       if (st->pinners[~stm] & occupied)
+       // there are pinners on their original square.
+       if (st->pinners[~stm] & occ)
            stmAttackers &= ~st->blockersForKing[stm];
  
        if (!stmAttackers)
            break;
  
+       res ^= 1;
        // Locate and remove the next least valuable attacker, and add to
-       // the bitboard 'attackers' the possibly X-ray attackers behind it.
-       nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
+       // the bitboard 'attackers' any X-ray attackers behind it.
+       if ((bb = stmAttackers & pieces(PAWN)))
+       {
+           if ((swap = PawnValueMg - swap) < res)
+               break;
+           occ ^= lsb(bb);
+           attackers |= attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN);
+       }
+       else if ((bb = stmAttackers & pieces(KNIGHT)))
+       {
+           if ((swap = KnightValueMg - swap) < res)
+               break;
  
-       stm = ~stm; // Switch side to move
+           occ ^= lsb(bb);
+       }
  
-       // Negamax the balance with alpha = balance, beta = balance+1 and
-       // add nextVictim's value.
-       //
-       //      (balance, balance+1) -> (-balance-1, -balance)
-       //
-       assert(balance < VALUE_ZERO);
+       else if ((bb = stmAttackers & pieces(BISHOP)))
+       {
+           if ((swap = BishopValueMg - swap) < res)
+               break;
  
-       balance = -balance - 1 - PieceValue[MG][nextVictim];
+           occ ^= lsb(bb);
+           attackers |= attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN);
+       }
  
-       // If balance is still non-negative after giving away nextVictim then we
-       // win. The only thing to be careful about it is that we should revert
-       // stm if we captured with the king when the opponent still has attackers.
-       if (balance >= VALUE_ZERO)
+       else if ((bb = stmAttackers & pieces(ROOK)))
        {
-           if (nextVictim == KING && (attackers & pieces(stm)))
-               stm = ~stm;
-           break;
+           if ((swap = RookValueMg - swap) < res)
+               break;
+           occ ^= lsb(bb);
+           attackers |= attacks_bb<ROOK>(to, occ) & pieces(ROOK, QUEEN);
+       }
+       else if ((bb = stmAttackers & pieces(QUEEN)))
+       {
+           if ((swap = QueenValueMg - swap) < res)
+               break;
+           occ ^= lsb(bb);
+           attackers |=  (attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN))
+                       | (attacks_bb<ROOK  >(to, occ) & pieces(ROOK  , QUEEN));
+       }
++      // fairy pieces
++      // pick next piece without considering value
++      else if ((bb = stmAttackers & ~pieces(KING)))
++      {
++          if ((swap = PieceValue[MG][piece_on(lsb(bb))] - swap) < res)
++              break;
++
++          occ ^= lsb(bb);
 +      }
-       assert(nextVictim != KING);
++
+       else // KING
+            // If we "capture" with the king but opponent still has attackers,
+            // reverse the result.
+           return (attackers & ~pieces(stm)) ? res ^ 1 : res;
    }
-   return us != stm; // We break the above loop when stm loses
- }
  
+   return res;
+ }
  
 -/// Position::is_draw() tests whether the position is drawn by 50-move rule
 -/// or by repetition. It does not detect stalemates.
 +/// Position::is_optinal_game_end() tests whether the position may end the game by
 +/// 50-move rule, by repetition, or a variant rule that allows a player to claim a game result.
 +
 +bool Position::is_optional_game_end(Value& result, int ply) const {
 +
 +  // n-move rule
 +  if (n_move_rule() && st->rule50 > (2 * n_move_rule() - 1) && (!checkers() || MoveList<LEGAL>(*this).size()))
 +  {
 +      result = VALUE_DRAW;
 +      return true;
 +  }
 +
 +  // n-fold repetition
 +  if (n_fold_rule())
 +  {
 +      int end = captures_to_hand() ? st->pliesFromNull : std::min(st->rule50, st->pliesFromNull);
 +
 +      if (end >= 4)
 +      {
 +          StateInfo* stp = st->previous->previous;
 +          int cnt = 0;
 +          bool perpetualThem = st->checkersBB && stp->checkersBB;
 +          bool perpetualUs = st->previous->checkersBB && stp->previous->checkersBB;
 +
 +          for (int i = 4; i <= end; i += 2)
 +          {
 +              stp = stp->previous->previous;
 +              perpetualThem &= bool(stp->checkersBB);
 +
 +              // Return a draw score if a position repeats once earlier but strictly
 +              // after the root, or repeats twice before or at the root.
 +              if (   stp->key == st->key
 +                  && ++cnt + 1 == (ply > i ? 2 : n_fold_rule()))
 +              {
 +                  result = convert_mate_value(  var->perpetualCheckIllegal && perpetualThem ? VALUE_MATE
 +                                              : var->perpetualCheckIllegal && perpetualUs ? -VALUE_MATE
 +                                              : var->nFoldValueAbsolute && sideToMove == BLACK ? -var->nFoldValue
 +                                              : var->nFoldValue, ply);
 +                  return true;
 +              }
  
 -bool Position::is_draw(int ply) const {
 +              if (i + 1 <= end)
 +                  perpetualUs &= bool(stp->previous->checkersBB);
 +          }
 +      }
 +  }
  
 -  if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
 +  // counting rules
 +  if (   counting_rule()
 +      && st->countingLimit
 +      && st->countingPly >= st->countingLimit
 +      && (!checkers() || MoveList<LEGAL>(*this).size()))
 +  {
 +      result = VALUE_DRAW;
        return true;
 +  }
  
 -  // Return a draw score if a position repeats once earlier but strictly
 -  // after the root, or repeats twice before or at the root.
 -  if (st->repetition && st->repetition < ply)
 +  // sittuyin stalemate due to optional promotion (3.9 c.7)
 +  if (   sittuyin_promotion()
 +      && count<ALL_PIECES>(sideToMove) == 2
 +      && count<PAWN>(sideToMove) == 1
 +      && !checkers())
 +  {
 +      bool promotionsOnly = true;
 +      for (const auto& m : MoveList<LEGAL>(*this))
 +          if (type_of(m) != PROMOTION)
 +          {
 +              promotionsOnly = false;
 +              break;
 +          }
 +      if (promotionsOnly)
 +      {
 +          result = VALUE_DRAW;
 +          return true;
 +      }
 +  }
 +
 +  return false;
 +}
 +
 +/// Position::is_immediate_game_end() tests whether the position ends the game
 +/// immediately by a variant rule, i.e., there are no more legal moves.
 +/// It does not not detect stalemates.
 +
 +bool Position::is_immediate_game_end(Value& result, int ply) const {
 +
 +  // bare king rule
 +  if (    bare_king_value() != VALUE_NONE
 +      && !bare_king_move()
 +      && !(count<ALL_PIECES>(sideToMove) - count<KING>(sideToMove)))
 +  {
 +      result = bare_king_value(ply);
 +      return true;
 +  }
 +  if (    bare_king_value() != VALUE_NONE
 +      &&  bare_king_move()
 +      && !(count<ALL_PIECES>(~sideToMove) - count<KING>(~sideToMove)))
 +  {
 +      result = -bare_king_value(ply);
 +      return true;
 +  }
 +  // extinction
 +  if (extinction_value() != VALUE_NONE)
 +  {
 +      for (PieceType pt : extinction_piece_types())
 +          if (!count(WHITE, pt) || !count(BLACK, pt))
 +          {
 +              result = !count(sideToMove, pt) ? extinction_value(ply) : -extinction_value(ply);
 +              return true;
 +          }
 +  }
 +  // capture the flag
 +  if (   capture_the_flag_piece()
 +      && !flag_move()
 +      && (capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece())))
 +  {
 +      result = mated_in(ply);
 +      return true;
 +  }
 +  if (   capture_the_flag_piece()
 +      && flag_move()
 +      && (capture_the_flag(sideToMove) & pieces(sideToMove, capture_the_flag_piece())))
 +  {
 +      result =  (capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece()))
 +              && sideToMove == WHITE ? VALUE_DRAW : mate_in(ply);
 +      return true;
 +  }
 +  // nCheck
 +  if (check_counting() && checks_remaining(~sideToMove) == 0)
 +  {
 +      result = mated_in(ply);
 +      return true;
 +  }
 +  // Connect-n
 +  if (connect_n() > 0)
 +  {
 +      Bitboard b;
 +      for (Direction d : {NORTH, NORTH_EAST, EAST, SOUTH_EAST})
 +      {
 +          b = pieces(~sideToMove);
 +          for (int i = 1; i < connect_n() && b; i++)
 +              b &= shift(d, b);
 +          if (b)
 +          {
 +              result = mated_in(ply);
 +              return true;
 +          }
 +      }
 +  }
 +  // Tsume mode: Assume that side with king wins when not in check
 +  if (!count<KING>(~sideToMove) && count<KING>(sideToMove) && !checkers() && Options["TsumeMode"])
 +  {
 +      result = mate_in(ply);
        return true;
 +  }
  
    return false;
  }