#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;
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);
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
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));
}
}
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));
}
}
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;
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
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;
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
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;
}
}
// 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;
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
{
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);
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
{
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;
}
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);
- }
}
}
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
// Used by NNUE
StateInfo* state() const;
- const EvalList* eval_list() const;
private:
// Initialization helpers (used while setting up a position)
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];
Thread* thisThread;
StateInfo* st;
bool chess960;
-
- // List of pieces used in NNUE evaluation function
- EvalList evalList;
};
namespace PSQT {
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
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 },
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).
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)
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;
}
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;
}