]> git.sesse.net Git - stockfish/blobdiff - src/position.cpp
Move to_char() and to_string() to notation
[stockfish] / src / position.cpp
index 3f01977081b9301f4b3e29124a0b53d64b7ef8eb..db4c857b0d25401cf97e3f70b56c8aeb10a46ee1 100644 (file)
 
 #include "bitcount.h"
 #include "movegen.h"
-#include "notation.h"
 #include "position.h"
 #include "psqtab.h"
 #include "rkiss.h"
 #include "thread.h"
 #include "tt.h"
+#include "notation.h"
 
 using std::string;
 
@@ -226,7 +226,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
       incremented after Black's move.
 */
 
-  char col, row, token;
+  unsigned char col, row, token;
   size_t idx;
   Square sq = SQ_A8;
   std::istringstream ss(fenStr);
@@ -274,7 +274,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
           for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; ++rsq) {}
 
       else if (token >= 'A' && token <= 'H')
-          rsq = File(token - 'A') | relative_rank(c, RANK_1);
+          rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
 
       else
           continue;
@@ -286,7 +286,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
   if (   ((ss >> col) && (col >= 'a' && col <= 'h'))
       && ((ss >> row) && (row == '3' || row == '6')))
   {
-      st->epSquare = File(col - 'a') | Rank(row - '1');
+      st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
 
       if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)))
           st->epSquare = SQ_NONE;
@@ -297,7 +297,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
 
   // Convert from fullmove starting from 1 to ply starting from 0,
   // handle also common incorrect FEN with fullmove = 0.
-  gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK);
+  gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
 
   chess960 = isChess960;
   thisThread = th;
@@ -388,21 +388,21 @@ const string Position::fen() const {
   int emptyCnt;
   std::ostringstream ss;
 
-  for (Rank rank = RANK_8; rank >= RANK_1; --rank)
+  for (Rank r = RANK_8; r >= RANK_1; --r)
   {
-      for (File file = FILE_A; file <= FILE_H; ++file)
+      for (File f = FILE_A; f <= FILE_H; ++f)
       {
-          for (emptyCnt = 0; file <= FILE_H && empty(file | rank); ++file)
+          for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f)
               ++emptyCnt;
 
           if (emptyCnt)
               ss << emptyCnt;
 
-          if (file <= FILE_H)
-              ss << PieceToChar[piece_on(file | rank)];
+          if (f <= FILE_H)
+              ss << PieceToChar[piece_on(make_square(f, r))];
       }
 
-      if (rank > RANK_1)
+      if (r > RANK_1)
           ss << '/';
   }
 
@@ -424,49 +424,51 @@ const string Position::fen() const {
       ss << '-';
 
   ss << (ep_square() == SQ_NONE ? " - " : " " + to_string(ep_square()) + " ")
-     << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2;
+     << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
 
   return ss.str();
 }
 
 
-/// Position::pretty() returns an ASCII representation of the position to be
-/// printed to the standard output together with the move's san notation.
+/// Position::pretty() returns an ASCII representation of the position
 
-const string Position::pretty(Move move) const {
+const string Position::pretty() const {
 
-  const string dottedLine =            "\n+---+---+---+---+---+---+---+---+";
-  const string twoRows =  dottedLine + "\n|   | . |   | . |   | . |   | . |"
-                        + dottedLine + "\n| . |   | . |   | . |   | . |   |";
+  std::ostringstream ss;
 
-  string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
+  ss << "\n +---+---+---+---+---+---+---+---+\n";
 
-  for (Bitboard b = pieces(); b; )
+  for (Rank r = RANK_8; r >= RANK_1; --r)
   {
-      Square s = pop_lsb(&b);
-      brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)];
-  }
-
-  std::ostringstream ss;
+      for (File f = FILE_A; f <= FILE_H; ++f)
+          ss << " | " << PieceToChar[piece_on(make_square(f, r))];
 
-  if (move)
-      ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
-         << move_to_san(*const_cast<Position*>(this), move);
+      ss << " |\n +---+---+---+---+---+---+---+---+\n";
+  }
 
-  ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
+  ss << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
      << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
 
   for (Bitboard b = checkers(); b; )
       ss << to_string(pop_lsb(&b)) << " ";
 
-  ss << "\nLegal moves: ";
-  for (MoveList<LEGAL> it(*this); *it; ++it)
-      ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
-
   return ss.str();
 }
 
 
+/// Position::game_phase() calculates the game phase interpolating total non-pawn
+/// material between endgame and midgame limits.
+
+Phase Position::game_phase() const {
+
+  Value npm = st->npMaterial[WHITE] + st->npMaterial[BLACK];
+
+  npm = std::max(EndgameLimit, std::min(npm, MidgameLimit));
+
+  return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
+}
+
+
 /// Position::check_blockers() returns a bitboard of all the pieces with color
 /// 'c' that are blocking check on the king with color 'kingColor'. A piece
 /// blocks a check if removing that piece from the board would result in a
@@ -526,19 +528,18 @@ bool Position::legal(Move m, Bitboard pinned) const {
   // the move is made.
   if (type_of(m) == ENPASSANT)
   {
-      Color them = ~us;
-      Square to = to_sq(m);
-      Square capsq = to + pawn_push(them);
       Square ksq = king_square(us);
-      Bitboard b = (pieces() ^ from ^ capsq) | to;
+      Square to = to_sq(m);
+      Square capsq = to - pawn_push(us);
+      Bitboard occ = (pieces() ^ from ^ capsq) | to;
 
       assert(to == ep_square());
       assert(moved_piece(m) == make_piece(us, PAWN));
-      assert(piece_on(capsq) == make_piece(them, PAWN));
+      assert(piece_on(capsq) == make_piece(~us, PAWN));
       assert(piece_on(to) == NO_PIECE);
 
-      return   !(attacks_bb<  ROOK>(ksq, b) & pieces(them, QUEEN, ROOK))
-            && !(attacks_bb<BISHOP>(ksq, b) & pieces(them, QUEEN, BISHOP));
+      return   !(attacks_bb<  ROOK>(ksq, occ) & pieces(~us, QUEEN, ROOK))
+            && !(attacks_bb<BISHOP>(ksq, occ) & pieces(~us, QUEEN, BISHOP));
   }
 
   // If the moving piece is a king, check whether the destination
@@ -665,7 +666,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
   // the captured pawn.
   case ENPASSANT:
   {
-      Square capsq = file_of(to) | rank_of(from);
+      Square capsq = make_square(file_of(to), rank_of(from));
       Bitboard b = (pieces() ^ from ^ capsq) | to;
 
       return  (attacks_bb<  ROOK>(ci.ksq, b) & pieces(sideToMove, QUEEN, ROOK))
@@ -739,14 +740,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   {
       assert(pc == make_piece(us, KING));
 
-      bool kingSide = to > from;
-      Square rfrom = to; // Castling is encoded as "king captures friendly rook"
-      Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
-      to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
-      captured = NO_PIECE_TYPE;
-
-      do_castling(from, to, rfrom, rto);
+      Square rfrom, rto;
+      do_castling<true>(from, to, rfrom, rto);
 
+      captured = NO_PIECE_TYPE;
       st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom];
       k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
   }
@@ -828,7 +825,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
           k ^= Zobrist::enpassant[file_of(st->epSquare)];
       }
 
-      if (type_of(m) == PROMOTION)
+      else if (type_of(m) == PROMOTION)
       {
           PieceType promotion = promotion_type(m);
 
@@ -868,7 +865,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   // Update the key with the final value
   st->key = k;
 
-  // Update checkers bitboard: piece must be already moved
+  // Update checkers bitboard: piece must be already moved due to attacks_from()
   st->checkersBB = 0;
 
   if (moveIsCheck)
@@ -882,7 +879,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
               st->checkersBB |= to;
 
           // Discovered checks
-          if (ci.dcCandidates && (ci.dcCandidates & from))
+          if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from))
           {
               if (pt != ROOK)
                   st->checkersBB |= attacks_from<ROOK>(king_square(them)) & pieces(us, QUEEN, ROOK);
@@ -909,56 +906,49 @@ void Position::undo_move(Move m) {
   sideToMove = ~sideToMove;
 
   Color us = sideToMove;
-  Color them = ~us;
   Square from = from_sq(m);
   Square to = to_sq(m);
   PieceType pt = type_of(piece_on(to));
-  PieceType captured = st->capturedType;
 
   assert(empty(from) || type_of(m) == CASTLING);
-  assert(captured != KING);
+  assert(st->capturedType != KING);
 
   if (type_of(m) == PROMOTION)
   {
-      PieceType promotion = promotion_type(m);
-
-      assert(promotion == pt);
+      assert(pt == promotion_type(m));
       assert(relative_rank(us, to) == RANK_8);
-      assert(promotion >= KNIGHT && promotion <= QUEEN);
+      assert(promotion_type(m) >= KNIGHT && promotion_type(m) <= QUEEN);
 
-      remove_piece(to, us, promotion);
+      remove_piece(to, us, promotion_type(m));
       put_piece(to, us, PAWN);
       pt = PAWN;
   }
 
   if (type_of(m) == CASTLING)
   {
-      bool kingSide = to > from;
-      Square rfrom = to; // Castling is encoded as "king captures friendly rook"
-      Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
-      to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
-      captured = NO_PIECE_TYPE;
-      pt = KING;
-      do_castling(to, from, rto, rfrom);
+      Square rfrom, rto;
+      do_castling<false>(from, to, rfrom, rto);
   }
   else
-      move_piece(to, from, us, pt); // Put the piece back at the source square
-
-  if (captured)
   {
-      Square capsq = to;
+      move_piece(to, from, us, pt); // Put the piece back at the source square
 
-      if (type_of(m) == ENPASSANT)
+      if (st->capturedType)
       {
-          capsq -= pawn_push(us);
+          Square capsq = to;
 
-          assert(pt == PAWN);
-          assert(to == st->previous->epSquare);
-          assert(relative_rank(us, to) == RANK_6);
-          assert(piece_on(capsq) == NO_PIECE);
-      }
+          if (type_of(m) == ENPASSANT)
+          {
+              capsq -= pawn_push(us);
+
+              assert(pt == PAWN);
+              assert(to == st->previous->epSquare);
+              assert(relative_rank(us, to) == RANK_6);
+              assert(piece_on(capsq) == NO_PIECE);
+          }
 
-      put_piece(capsq, them, captured); // Restore the captured piece
+          put_piece(capsq, ~us, st->capturedType); // Restore the captured piece
+      }
   }
 
   // Finally point our state pointer back to the previous state
@@ -971,15 +961,20 @@ void Position::undo_move(Move m) {
 
 /// Position::do_castling() is a helper used to do/undo a castling move. This
 /// is a bit tricky, especially in Chess960.
+template<bool Do>
+void Position::do_castling(Square from, Square& to, Square& rfrom, Square& rto) {
 
-void Position::do_castling(Square kfrom, Square kto, Square rfrom, Square rto) {
+  bool kingSide = to > from;
+  rfrom = to; // Castling is encoded as "king captures friendly rook"
+  rto = relative_square(sideToMove, kingSide ? SQ_F1 : SQ_D1);
+  to  = relative_square(sideToMove, kingSide ? SQ_G1 : SQ_C1);
 
   // Remove both pieces first since squares could overlap in Chess960
-  remove_piece(kfrom, sideToMove, KING);
-  remove_piece(rfrom, sideToMove, ROOK);
-  board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us
-  put_piece(kto, sideToMove, KING);
-  put_piece(rto, sideToMove, ROOK);
+  remove_piece(Do ?  from :  to, sideToMove, KING);
+  remove_piece(Do ? rfrom : rto, sideToMove, ROOK);
+  board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
+  put_piece(Do ?  to :  from, sideToMove, KING);
+  put_piece(Do ? rto : rfrom, sideToMove, ROOK);
 }
 
 
@@ -1122,10 +1117,6 @@ Value Position::see(Move m) const {
 
 bool Position::is_draw() const {
 
-  if (   !pieces(PAWN)
-      && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg))
-      return true;
-
   if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
       return true;
 
@@ -1154,9 +1145,9 @@ void Position::flip() {
   string f, token;
   std::stringstream ss(fen());
 
-  for (Rank rank = RANK_8; rank >= RANK_1; --rank) // Piece placement
+  for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement
   {
-      std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
+      std::getline(ss, token, r > RANK_1 ? '/' : ' ');
       f.insert(0, token + (f.empty() ? " " : "/"));
   }
 
@@ -1183,44 +1174,30 @@ void Position::flip() {
 /// Position::pos_is_ok() performs some consistency checks for the position object.
 /// This is meant to be helpful when debugging.
 
-bool Position::pos_is_ok(int* failedStep) const {
+bool Position::pos_is_ok(int* step) const {
 
-  int dummy, *step = failedStep ? failedStep : &dummy;
-
-  // What features of the position should be verified?
+  // Which parts of the position should be verified?
   const bool all = false;
 
   const bool testBitboards       = all || false;
+  const bool testState           = all || false;
   const bool testKingCount       = all || false;
   const bool testKingCapture     = all || false;
-  const bool testState           = all || false;
-  const bool testCheckerCount    = all || false;
   const bool testPieceCounts     = all || false;
   const bool testPieceList       = all || false;
   const bool testCastlingSquares = all || false;
 
-  if (*step = 1, sideToMove != WHITE && sideToMove != BLACK)
-      return false;
-
-  if ((*step)++, piece_on(king_square(WHITE)) != W_KING)
-      return false;
-
-  if ((*step)++, piece_on(king_square(BLACK)) != B_KING)
-      return false;
+  if (step)
+      *step = 1;
 
-  if ((*step)++, ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)
+  if (   (sideToMove != WHITE && sideToMove != BLACK)
+      || piece_on(king_square(WHITE)) != W_KING
+      || piece_on(king_square(BLACK)) != B_KING
+      || (   ep_square() != SQ_NONE
+          && relative_rank(sideToMove, ep_square()) != RANK_6))
       return false;
 
-  if ((*step)++, testKingCount)
-      if (   std::count(board, board + SQUARE_NB, W_KING) != 1
-          || std::count(board, board + SQUARE_NB, B_KING) != 1)
-          return false;
-
-  if ((*step)++, testKingCapture)
-      if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove))
-          return false;
-
-  if ((*step)++, testBitboards)
+  if (step && ++*step, testBitboards)
   {
       // The intersection of the white and black pieces must be empty
       if (pieces(WHITE) & pieces(BLACK))
@@ -1238,7 +1215,7 @@ bool Position::pos_is_ok(int* failedStep) const {
                   return false;
   }
 
-  if ((*step)++, testState)
+  if (step && ++*step, testState)
   {
       StateInfo si;
       set_state(&si);
@@ -1247,20 +1224,27 @@ bool Position::pos_is_ok(int* failedStep) const {
           || st->materialKey != si.materialKey
           || st->npMaterial[WHITE] != si.npMaterial[WHITE]
           || st->npMaterial[BLACK] != si.npMaterial[BLACK]
-          || st->psq != si.psq)
+          || st->psq != si.psq
+          || st->checkersBB != si.checkersBB)
           return false;
   }
 
-  if ((*step)++, testCheckerCount && popcount<Full>(st->checkersBB) > 2)
-      return false;
+  if (step && ++*step, testKingCount)
+      if (   std::count(board, board + SQUARE_NB, W_KING) != 1
+          || std::count(board, board + SQUARE_NB, B_KING) != 1)
+          return false;
+
+  if (step && ++*step, testKingCapture)
+      if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove))
+          return false;
 
-  if ((*step)++, testPieceCounts)
+  if (step && ++*step, testPieceCounts)
       for (Color c = WHITE; c <= BLACK; ++c)
           for (PieceType pt = PAWN; pt <= KING; ++pt)
               if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
                   return false;
 
-  if ((*step)++, testPieceList)
+  if (step && ++*step, testPieceList)
       for (Color c = WHITE; c <= BLACK; ++c)
           for (PieceType pt = PAWN; pt <= KING; ++pt)
               for (int i = 0; i < pieceCount[c][pt];  ++i)
@@ -1268,7 +1252,7 @@ bool Position::pos_is_ok(int* failedStep) const {
                       || index[pieceList[c][pt][i]] != i)
                       return false;
 
-  if ((*step)++, testCastlingSquares)
+  if (step && ++*step, testCastlingSquares)
       for (Color c = WHITE; c <= BLACK; ++c)
           for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
           {
@@ -1281,6 +1265,5 @@ bool Position::pos_is_ok(int* failedStep) const {
                   return false;
           }
 
-  *step = 0;
   return true;
 }