Remove EvalList
authorsyzygy1 <3028851+syzygy1@users.noreply.github.com>
Mon, 24 Aug 2020 00:29:38 +0000 (02:29 +0200)
committerJoost VandeVondele <Joost.VandeVondele@gmail.com>
Wed, 26 Aug 2020 05:11:26 +0000 (07:11 +0200)
This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code.

The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective.

If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object.

If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE).

An STC test has confirmed a small speedup:

https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 87704 W: 9763 L: 9500 D: 68441
Ptnml(0-2): 426, 6950, 28845, 7197, 434

closes https://github.com/official-stockfish/Stockfish/pull/3068

No functional change

src/nnue/evaluate_nnue.cpp
src/nnue/features/feature_set.h
src/nnue/features/half_kp.cpp
src/nnue/features/half_kp.h
src/nnue/nnue_common.h
src/position.cpp
src/position.h
src/types.h

index dfbb1ac..e661908 100644 (file)
 
 #include "evaluate_nnue.h"
 
-ExtPieceSquare kpp_board_index[PIECE_NB] = {
- // convention: W - us, B - them
- // viewed from other side, W and B are reversed
-    { PS_NONE,     PS_NONE     },
-    { PS_W_PAWN,   PS_B_PAWN   },
-    { PS_W_KNIGHT, PS_B_KNIGHT },
-    { PS_W_BISHOP, PS_B_BISHOP },
-    { PS_W_ROOK,   PS_B_ROOK   },
-    { PS_W_QUEEN,  PS_B_QUEEN  },
-    { PS_W_KING,   PS_B_KING   },
-    { PS_NONE,     PS_NONE     },
-    { PS_NONE,     PS_NONE     },
-    { PS_B_PAWN,   PS_W_PAWN   },
-    { PS_B_KNIGHT, PS_W_KNIGHT },
-    { PS_B_BISHOP, PS_W_BISHOP },
-    { PS_B_ROOK,   PS_W_ROOK   },
-    { PS_B_QUEEN,  PS_W_QUEEN  },
-    { PS_B_KING,   PS_W_KING   },
-    { PS_NONE,     PS_NONE     }
-};
-
-
 namespace Eval::NNUE {
 
+  uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = {
+   // convention: W - us, B - them
+   // viewed from other side, W and B are reversed
+      { PS_NONE,     PS_NONE     },
+      { PS_W_PAWN,   PS_B_PAWN   },
+      { PS_W_KNIGHT, PS_B_KNIGHT },
+      { PS_W_BISHOP, PS_B_BISHOP },
+      { PS_W_ROOK,   PS_B_ROOK   },
+      { PS_W_QUEEN,  PS_B_QUEEN  },
+      { PS_W_KING,   PS_B_KING   },
+      { PS_NONE,     PS_NONE     },
+      { PS_NONE,     PS_NONE     },
+      { PS_B_PAWN,   PS_W_PAWN   },
+      { PS_B_KNIGHT, PS_W_KNIGHT },
+      { PS_B_BISHOP, PS_W_BISHOP },
+      { PS_B_ROOK,   PS_W_ROOK   },
+      { PS_B_QUEEN,  PS_W_QUEEN  },
+      { PS_B_KING,   PS_W_KING   },
+      { PS_NONE,     PS_NONE     }
+  };
+
   // Input feature converter
   AlignedPtr<FeatureTransformer> feature_transformer;
 
index 79ca83a..558a6b2 100644 (file)
@@ -68,8 +68,7 @@ namespace Eval::NNUE::Features {
         reset[perspective] = false;
         switch (trigger) {
           case TriggerEvent::kFriendKingMoved:
-            reset[perspective] =
-                dp.pieceId[0] == PIECE_ID_KING + perspective;
+            reset[perspective] = dp.piece[0] == make_piece(perspective, KING);
             break;
           default:
             assert(false);
index 628add6..88e384a 100644 (file)
 
 namespace Eval::NNUE::Features {
 
-  // Find the index of the feature quantity from the king position and PieceSquare
-  template <Side AssociatedKing>
-  inline IndexType HalfKP<AssociatedKing>::MakeIndex(Square sq_k, PieceSquare p) {
-    return static_cast<IndexType>(PS_END) * static_cast<IndexType>(sq_k) + p;
+  // Orient a square according to perspective (rotates by 180 for black)
+  inline Square orient(Color perspective, Square s) {
+    return Square(int(s) ^ (bool(perspective) * 63));
   }
 
-  // Get pieces information
+  // Find the index of the feature quantity from the king position and PieceSquare
   template <Side AssociatedKing>
-  inline void HalfKP<AssociatedKing>::GetPieces(
-      const Position& pos, Color perspective,
-      PieceSquare** pieces, Square* sq_target_k) {
+  inline IndexType HalfKP<AssociatedKing>::MakeIndex(
+      Color perspective, Square s, Piece pc, Square ksq) {
 
-    *pieces = (perspective == BLACK) ?
-        pos.eval_list()->piece_list_fb() :
-        pos.eval_list()->piece_list_fw();
-    const PieceId target = (AssociatedKing == Side::kFriend) ?
-        static_cast<PieceId>(PIECE_ID_KING + perspective) :
-        static_cast<PieceId>(PIECE_ID_KING + ~perspective);
-    *sq_target_k = static_cast<Square>(((*pieces)[target] - PS_W_KING) % SQUARE_NB);
+    return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective] + PS_END * ksq);
   }
 
   // Get a list of indices for active features
@@ -49,16 +41,11 @@ namespace Eval::NNUE::Features {
   void HalfKP<AssociatedKing>::AppendActiveIndices(
       const Position& pos, Color perspective, IndexList* active) {
 
-    // Do nothing if array size is small to avoid compiler warning
-    if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return;
-
-    PieceSquare* pieces;
-    Square sq_target_k;
-    GetPieces(pos, perspective, &pieces, &sq_target_k);
-    for (PieceId i = PIECE_ID_ZERO; i < PIECE_ID_KING; ++i) {
-      if (pieces[i] != PS_NONE) {
-        active->push_back(MakeIndex(sq_target_k, pieces[i]));
-      }
+    Square ksq = orient(perspective, pos.square<KING>(perspective));
+    Bitboard bb = pos.pieces() & ~pos.pieces(KING);
+    while (bb) {
+      Square s = pop_lsb(&bb);
+      active->push_back(MakeIndex(perspective, s, pos.piece_on(s), ksq));
     }
   }
 
@@ -68,22 +55,15 @@ namespace Eval::NNUE::Features {
       const Position& pos, Color perspective,
       IndexList* removed, IndexList* added) {
 
-    PieceSquare* pieces;
-    Square sq_target_k;
-    GetPieces(pos, perspective, &pieces, &sq_target_k);
+    Square ksq = orient(perspective, pos.square<KING>(perspective));
     const auto& dp = pos.state()->dirtyPiece;
     for (int i = 0; i < dp.dirty_num; ++i) {
-      if (dp.pieceId[i] >= PIECE_ID_KING) continue;
-      const auto old_p = static_cast<PieceSquare>(
-          dp.old_piece[i].from[perspective]);
-      if (old_p != PS_NONE) {
-        removed->push_back(MakeIndex(sq_target_k, old_p));
-      }
-      const auto new_p = static_cast<PieceSquare>(
-          dp.new_piece[i].from[perspective]);
-      if (new_p != PS_NONE) {
-        added->push_back(MakeIndex(sq_target_k, new_p));
-      }
+      Piece pc = dp.piece[i];
+      if (type_of(pc) == KING) continue;
+      if (dp.from[i] != SQ_NONE)
+        removed->push_back(MakeIndex(perspective, dp.from[i], pc, ksq));
+      if (dp.to[i] != SQ_NONE)
+        added->push_back(MakeIndex(perspective, dp.to[i], pc, ksq));
     }
   }
 
index 99842ee..ee6a8df 100644 (file)
@@ -41,7 +41,7 @@ namespace Eval::NNUE::Features {
     static constexpr IndexType kDimensions =
         static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END);
     // Maximum number of simultaneously active features
-    static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING;
+    static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count
     // Trigger for full calculation instead of difference calculation
     static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
 
@@ -53,13 +53,9 @@ namespace Eval::NNUE::Features {
     static void AppendChangedIndices(const Position& pos, Color perspective,
                                      IndexList* removed, IndexList* added);
 
-    // Index of a feature for a given king position and another piece on some square
-    static IndexType MakeIndex(Square sq_k, PieceSquare p);
-
    private:
-    // Get pieces information
-    static void GetPieces(const Position& pos, Color perspective,
-                          PieceSquare** pieces, Square* sq_target_k);
+    // Index of a feature for a given king position and another piece on some square
+    static IndexType MakeIndex(Color perspective, Square s, Piece pc, Square sq_k);
   };
 
 }  // namespace Eval::NNUE::Features
index a9d8e5a..7bc905d 100644 (file)
@@ -94,6 +94,27 @@ namespace Eval::NNUE {
 
   constexpr std::size_t kMaxSimdWidth = 32;
 
+  // unique number for each piece type on each square
+  enum {
+    PS_NONE     =  0,
+    PS_W_PAWN   =  1,
+    PS_B_PAWN   =  1 * SQUARE_NB + 1,
+    PS_W_KNIGHT =  2 * SQUARE_NB + 1,
+    PS_B_KNIGHT =  3 * SQUARE_NB + 1,
+    PS_W_BISHOP =  4 * SQUARE_NB + 1,
+    PS_B_BISHOP =  5 * SQUARE_NB + 1,
+    PS_W_ROOK   =  6 * SQUARE_NB + 1,
+    PS_B_ROOK   =  7 * SQUARE_NB + 1,
+    PS_W_QUEEN  =  8 * SQUARE_NB + 1,
+    PS_B_QUEEN  =  9 * SQUARE_NB + 1,
+    PS_W_KING   = 10 * SQUARE_NB + 1,
+    PS_END      = PS_W_KING, // pieces without kings (pawns included)
+    PS_B_KING   = 11 * SQUARE_NB + 1,
+    PS_END2     = 12 * SQUARE_NB + 1
+  };
+
+  extern uint32_t kpp_board_index[PIECE_NB][COLOR_NB];
+
   // Type of input feature after conversion
   using TransformedFeatureType = std::uint8_t;
   using IndexType = std::uint32_t;
index 0289854..fe89b75 100644 (file)
@@ -198,9 +198,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
   std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
   st = si;
 
-  // Each piece on board gets a unique ID used to track the piece later
-  PieceId piece_id, next_piece_id = PIECE_ID_ZERO;
-
   ss >> std::noskipws;
 
   // 1. Piece placement
@@ -212,21 +209,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
       else if (token == '/')
           sq += 2 * SOUTH;
 
-      else if ((idx = PieceToChar.find(token)) != string::npos)
-      {
-          auto pc = Piece(idx);
-          put_piece(pc, sq);
-
-          if (Eval::useNNUE)
-          {
-              // Kings get a fixed ID, other pieces get ID in order of placement
-              piece_id =
-                (idx == W_KING) ? PIECE_ID_WKING :
-                (idx == B_KING) ? PIECE_ID_BKING :
-                next_piece_id++;
-              evalList.put_piece(piece_id, sq, pc);
-          }
-
+      else if ((idx = PieceToChar.find(token)) != string::npos) {
+          put_piece(Piece(idx), sq);
           ++sq;
       }
   }
@@ -721,8 +705,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
   // Used by NNUE
   st->accumulator.computed_accumulation = false;
   st->accumulator.computed_score = false;
-  PieceId dp0 = PIECE_ID_NONE;
-  PieceId dp1 = PIECE_ID_NONE;
   auto& dp = st->dirtyPiece;
   dp.dirty_num = 1;
 
@@ -775,12 +757,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
 
       if (Eval::useNNUE)
       {
-          dp.dirty_num = 2; // 2 pieces moved
-          dp1 = piece_id_on(capsq);
-          dp.pieceId[1] = dp1;
-          dp.old_piece[1] = evalList.piece_with_id(dp1);
-          evalList.put_piece(dp1, capsq, NO_PIECE);
-          dp.new_piece[1] = evalList.piece_with_id(dp1);
+          dp.dirty_num = 2;  // 1 piece moved, 1 piece captured
+          dp.piece[1] = captured;
+          dp.from[1] = capsq;
+          dp.to[1] = SQ_NONE;
       }
 
       // Update board and piece lists
@@ -821,11 +801,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
   {
       if (Eval::useNNUE)
       {
-          dp0 = piece_id_on(from);
-          dp.pieceId[0] = dp0;
-          dp.old_piece[0] = evalList.piece_with_id(dp0);
-          evalList.put_piece(dp0, to, pc);
-          dp.new_piece[0] = evalList.piece_with_id(dp0);
+          dp.piece[0] = pc;
+          dp.from[0] = from;
+          dp.to[0] = to;
       }
 
       move_piece(from, to);
@@ -854,9 +832,12 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
 
           if (Eval::useNNUE)
           {
-              dp0 = piece_id_on(to);
-              evalList.put_piece(dp0, to, promotion);
-              dp.new_piece[0] = evalList.piece_with_id(dp0);
+              // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
+              dp.to[0] = SQ_NONE;
+              dp.piece[dp.dirty_num] = promotion;
+              dp.from[dp.dirty_num] = SQ_NONE;
+              dp.to[dp.dirty_num] = to;
+              dp.dirty_num++;
           }
 
           // Update hash keys
@@ -950,12 +931,6 @@ void Position::undo_move(Move m) {
   {
       move_piece(to, from); // Put the piece back at the source square
 
-      if (Eval::useNNUE)
-      {
-          PieceId dp0 = st->dirtyPiece.pieceId[0];
-          evalList.put_piece(dp0, from, pc);
-      }
-
       if (st->capturedPiece)
       {
           Square capsq = to;
@@ -972,14 +947,6 @@ void Position::undo_move(Move m) {
           }
 
           put_piece(st->capturedPiece, capsq); // Restore the captured piece
-
-          if (Eval::useNNUE)
-          {
-              PieceId dp1 = st->dirtyPiece.pieceId[1];
-              assert(evalList.piece_with_id(dp1).from[WHITE] == PS_NONE);
-              assert(evalList.piece_with_id(dp1).from[BLACK] == PS_NONE);
-              evalList.put_piece(dp1, capsq, st->capturedPiece);
-          }
       }
   }
 
@@ -1001,32 +968,16 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
   rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
   to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
 
-  if (Eval::useNNUE)
+  if (Do && Eval::useNNUE)
   {
-      PieceId dp0, dp1;
       auto& dp = st->dirtyPiece;
-      dp.dirty_num = 2; // 2 pieces moved
-
-      if (Do)
-      {
-          dp0 = piece_id_on(from);
-          dp1 = piece_id_on(rfrom);
-          dp.pieceId[0] = dp0;
-          dp.old_piece[0] = evalList.piece_with_id(dp0);
-          evalList.put_piece(dp0, to, make_piece(us, KING));
-          dp.new_piece[0] = evalList.piece_with_id(dp0);
-          dp.pieceId[1] = dp1;
-          dp.old_piece[1] = evalList.piece_with_id(dp1);
-          evalList.put_piece(dp1, rto, make_piece(us, ROOK));
-          dp.new_piece[1] = evalList.piece_with_id(dp1);
-      }
-      else
-      {
-          dp0 = piece_id_on(to);
-          dp1 = piece_id_on(rto);
-          evalList.put_piece(dp0, from, make_piece(us, KING));
-          evalList.put_piece(dp1, rfrom, make_piece(us, ROOK));
-      }
+      dp.piece[0] = make_piece(us, KING);
+      dp.from[0] = from;
+      dp.to[0] = to;
+      dp.piece[1] = make_piece(us, ROOK);
+      dp.from[1] = rfrom;
+      dp.to[1] = rto;
+      dp.dirty_num = 2;
   }
 
   // Remove both pieces first since squares could overlap in Chess960
index 5ce1727..d6f5c9f 100644 (file)
@@ -171,7 +171,6 @@ public:
 
   // Used by NNUE
   StateInfo* state() const;
-  const EvalList* eval_list() const;
 
 private:
   // Initialization helpers (used while setting up a position)
@@ -186,9 +185,6 @@ private:
   template<bool Do>
   void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
 
-  // ID of a piece on a given square
-  PieceId piece_id_on(Square sq) const;
-
   // Data members
   Piece board[SQUARE_NB];
   Bitboard byTypeBB[PIECE_TYPE_NB];
@@ -205,9 +201,6 @@ private:
   Thread* thisThread;
   StateInfo* st;
   bool chess960;
-
-  // List of pieces used in NNUE evaluation function
-  EvalList evalList;
 };
 
 namespace PSQT {
@@ -451,20 +444,4 @@ inline StateInfo* Position::state() const {
   return st;
 }
 
-inline const EvalList* Position::eval_list() const {
-
-  return &evalList;
-}
-
-inline PieceId Position::piece_id_on(Square sq) const
-{
-
-  assert(piece_on(sq) != NO_PIECE);
-
-  PieceId pid = evalList.piece_id_list[sq];
-  assert(is_ok(pid));
-
-  return pid;
-}
-
 #endif // #ifndef POSITION_H_INCLUDED
index 1cd711b..5873c69 100644 (file)
@@ -201,22 +201,6 @@ enum Piece {
   PIECE_NB = 16
 };
 
-// An ID used to track the pieces. Max. 32 pieces on board.
-enum PieceId {
-  PIECE_ID_ZERO   = 0,
-  PIECE_ID_KING   = 30,
-  PIECE_ID_WKING  = 30,
-  PIECE_ID_BKING  = 31,
-  PIECE_ID_NONE   = 32
-};
-
-inline PieceId operator++(PieceId& d, int) {
-
-  PieceId x = d;
-  d = PieceId(int(d) + 1);
-  return x;
-}
-
 constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
   { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO },
@@ -271,93 +255,20 @@ enum Rank : int {
   RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
 };
 
-// unique number for each piece type on each square
-enum PieceSquare : uint32_t {
-  PS_NONE     =  0,
-  PS_W_PAWN   =  1,
-  PS_B_PAWN   =  1 * SQUARE_NB + 1,
-  PS_W_KNIGHT =  2 * SQUARE_NB + 1,
-  PS_B_KNIGHT =  3 * SQUARE_NB + 1,
-  PS_W_BISHOP =  4 * SQUARE_NB + 1,
-  PS_B_BISHOP =  5 * SQUARE_NB + 1,
-  PS_W_ROOK   =  6 * SQUARE_NB + 1,
-  PS_B_ROOK   =  7 * SQUARE_NB + 1,
-  PS_W_QUEEN  =  8 * SQUARE_NB + 1,
-  PS_B_QUEEN  =  9 * SQUARE_NB + 1,
-  PS_W_KING   = 10 * SQUARE_NB + 1,
-  PS_END      = PS_W_KING, // pieces without kings (pawns included)
-  PS_B_KING   = 11 * SQUARE_NB + 1,
-  PS_END2     = 12 * SQUARE_NB + 1
-};
-
-struct ExtPieceSquare {
-  PieceSquare from[COLOR_NB];
-};
-
-// Array for finding the PieceSquare corresponding to the piece on the board
-extern ExtPieceSquare kpp_board_index[PIECE_NB];
-
-constexpr bool is_ok(PieceId pid);
-constexpr Square rotate180(Square sq);
-
-// Structure holding which tracked piece (PieceId) is where (PieceSquare)
-class EvalList {
-
-public:
-  // Max. number of pieces without kings is 30 but must be a multiple of 4 in AVX2
-  static const int MAX_LENGTH = 32;
-
-  // Array that holds the piece id for the pieces on the board
-  PieceId piece_id_list[SQUARE_NB];
-
-  // List of pieces, separate from White and Black POV
-  PieceSquare* piece_list_fw() const { return const_cast<PieceSquare*>(pieceListFw); }
-  PieceSquare* piece_list_fb() const { return const_cast<PieceSquare*>(pieceListFb); }
-
-  // Place the piece pc with piece_id on the square sq on the board
-  void put_piece(PieceId piece_id, Square sq, Piece pc)
-  {
-      assert(is_ok(piece_id));
-      if (pc != NO_PIECE)
-      {
-          pieceListFw[piece_id] = PieceSquare(kpp_board_index[pc].from[WHITE] + sq);
-          pieceListFb[piece_id] = PieceSquare(kpp_board_index[pc].from[BLACK] + rotate180(sq));
-          piece_id_list[sq] = piece_id;
-      }
-      else
-      {
-          pieceListFw[piece_id] = PS_NONE;
-          pieceListFb[piece_id] = PS_NONE;
-          piece_id_list[sq] = piece_id;
-      }
-  }
-
-  // Convert the specified piece_id piece to ExtPieceSquare type and return it
-  ExtPieceSquare piece_with_id(PieceId piece_id) const
-  {
-      ExtPieceSquare eps;
-      eps.from[WHITE] = pieceListFw[piece_id];
-      eps.from[BLACK] = pieceListFb[piece_id];
-      return eps;
-  }
-
-private:
-  PieceSquare pieceListFw[MAX_LENGTH];
-  PieceSquare pieceListFb[MAX_LENGTH];
-};
-
-// For differential evaluation of pieces that changed since last turn
+// Keep track of what a move changes on the board (used by NNUE)
 struct DirtyPiece {
 
   // Number of changed pieces
   int dirty_num;
 
-  // The ids of changed pieces, max. 2 pieces can change in one move
-  PieceId pieceId[2];
+  // Max 3 pieces can change in one move. A promotion with capture moves
+  // both the pawn and the captured piece to SQ_NONE and the piece promoted
+  // to from SQ_NONE to the capture square.
+  Piece piece[3];
 
-  // What changed from the piece with that piece number
-  ExtPieceSquare old_piece[2];
-  ExtPieceSquare new_piece[2];
+  // From and to squares, which may be SQ_NONE
+  Square from[3];
+  Square to[3];
 };
 
 /// Score enum stores a middlegame and an endgame value in a single integer (enum).
@@ -407,8 +318,6 @@ ENABLE_FULL_OPERATORS_ON(Value)
 ENABLE_FULL_OPERATORS_ON(Direction)
 
 ENABLE_INCR_OPERATORS_ON(Piece)
-ENABLE_INCR_OPERATORS_ON(PieceSquare)
-ENABLE_INCR_OPERATORS_ON(PieceId)
 ENABLE_INCR_OPERATORS_ON(PieceType)
 ENABLE_INCR_OPERATORS_ON(Square)
 ENABLE_INCR_OPERATORS_ON(File)
@@ -497,10 +406,6 @@ inline Color color_of(Piece pc) {
   return Color(pc >> 3);
 }
 
-constexpr bool is_ok(PieceId pid) {
-  return pid < PIECE_ID_NONE;
-}
-
 constexpr bool is_ok(Square s) {
   return s >= SQ_A1 && s <= SQ_H8;
 }
@@ -537,11 +442,6 @@ constexpr Square to_sq(Move m) {
   return Square(m & 0x3F);
 }
 
-// Return relative square when turning the board 180 degrees
-constexpr Square rotate180(Square sq) {
-  return (Square)(sq ^ 0x3F);
-}
-
 constexpr int from_to(Move m) {
  return m & 0xFFF;
 }