From: Fabian Fichter Date: Sat, 26 Jun 2021 18:26:01 +0000 (+0200) Subject: Merge official-stockfish/master X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=c1dd28c5a2c92db0e94b95e15e773ac7f9829bcb;p=fairystockfish.git Merge official-stockfish/master --- c1dd28c5a2c92db0e94b95e15e773ac7f9829bcb diff --cc src/nnue/features/half_kp.cpp index 8fe1ec6,aa1dece..9481136 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@@ -23,28 -24,24 +24,29 @@@ namespace Stockfish::Eval::NNUE::Features { + // Map square to numbering on 8x8 board + constexpr Square to_chess_square(Square s) { + return Square(s - rank_of(s) * (FILE_MAX - FILE_H)); + } + // Orient a square according to perspective (rotates by 180 for black) - inline Square orient(Color perspective, Square s) { - inline Square HalfKP::orient(Color perspective, Square s) { - return Square(int(s) ^ (bool(perspective) * 63)); ++ inline Square HalfKPChess::orient(Color perspective, Square s) { + return Square(int(to_chess_square(s)) ^ (bool(perspective) * 63)); } // Index of a feature for a given king position and another piece on some square - inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { - inline IndexType HalfKP::make_index(Color perspective, Square s, Piece pc, Square ksq) { ++ inline IndexType HalfKPChess::make_index(Color perspective, Square s, Piece pc, Square ksq) { return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); } // Get a list of indices for active features - template - void HalfKPChess::append_active_indices( - const Position& pos, Color perspective, IndexList* active) { - - Square ksq = orient(perspective, pos.square(perspective)); - Bitboard bb = pos.pieces() & ~pos.pieces(KING); - void HalfKP::append_active_indices( ++ void HalfKPChess::append_active_indices( + const Position& pos, + Color perspective, + ValueListInserter active + ) { - Square ksq = orient(perspective, pos.square(perspective)); - Bitboard bb = pos.pieces() & ~pos.pieces(KING); ++ Square ksq = orient(perspective, pos.square(perspective, pos.nnue_king())); ++ Bitboard bb = pos.pieces() & ~pos.pieces(pos.nnue_king()); while (bb) { Square s = pop_lsb(bb); @@@ -55,38 -52,35 +57,36 @@@ // append_changed_indices() : get a list of indices for recently changed features - // IMPORTANT: The `pos` in this function is pretty much useless as it - // is not always the position the features are updated to. The feature - // transformer code right now can update multiple accumulators per move, - // but since Stockfish only keeps the full state of the current leaf - // search position it is not possible to always pass here the position for - // which the accumulator is being updated. Therefore the only thing that - // can be reliably extracted from `pos` is the king square for the king - // of the `perspective` color (note: not even the other king's square will - // match reality in all cases, this is also the reason why `dp` is passed - // as a parameter and not extracted from pos.state()). This is of particular - // problem for future nets with other feature sets, where updating the active - // feature might require more information from the intermediate positions. In - // this case the only easy solution is to remove the multiple updates from - // the feature transformer update code and only update the accumulator for - // the current leaf position (the position after the move). - - template - void HalfKPChess::append_changed_indices( - const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added) { - - Square ksq = orient(perspective, pos.square(perspective)); - void HalfKP::append_changed_indices( ++ void HalfKPChess::append_changed_indices( + Square ksq, + StateInfo* st, + Color perspective, + ValueListInserter removed, - ValueListInserter added ++ ValueListInserter added, ++ const Position& pos + ) { + const auto& dp = st->dirtyPiece; + Square oriented_ksq = orient(perspective, ksq); for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; -- if (type_of(pc) == KING) continue; ++ if (type_of(pc) == pos.nnue_king()) continue; if (dp.from[i] != SQ_NONE) - removed->push_back(make_index(perspective, dp.from[i], pc, ksq)); + removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); if (dp.to[i] != SQ_NONE) - added->push_back(make_index(perspective, dp.to[i], pc, ksq)); + added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq)); } } - template class HalfKPChess; - int HalfKP::update_cost(StateInfo* st) { ++ int HalfKPChess::update_cost(StateInfo* st) { + return st->dirtyPiece.dirty_num; + } + - int HalfKP::refresh_cost(const Position& pos) { ++ int HalfKPChess::refresh_cost(const Position& pos) { + return pos.count() - 2; + } + - bool HalfKP::requires_refresh(StateInfo* st, Color perspective) { - return st->dirtyPiece.piece[0] == make_piece(perspective, KING); ++ bool HalfKPChess::requires_refresh(StateInfo* st, Color perspective, const Position& pos) { ++ return st->dirtyPiece.piece[0] == make_piece(perspective, pos.nnue_king()); + } } // namespace Stockfish::Eval::NNUE::Features diff --cc src/nnue/features/half_kp.h index 995023f,a09c221..7bccd1e --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@@ -28,30 -34,75 +34,117 @@@ namespace Stockfish::Eval::NNUE::Featur // Feature HalfKP: Combination of the position of own king // and the position of pieces other than kings - template - class HalfKP { + class HalfKPChess { + // 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_NB = 10 * SQUARE_NB + 1 ++ PS_B_PAWN = 1 * SQUARE_NB_CHESS + 1, ++ PS_W_KNIGHT = 2 * SQUARE_NB_CHESS + 1, ++ PS_B_KNIGHT = 3 * SQUARE_NB_CHESS + 1, ++ PS_W_BISHOP = 4 * SQUARE_NB_CHESS + 1, ++ PS_B_BISHOP = 5 * SQUARE_NB_CHESS + 1, ++ PS_W_ROOK = 6 * SQUARE_NB_CHESS + 1, ++ PS_B_ROOK = 7 * SQUARE_NB_CHESS + 1, ++ PS_W_QUEEN = 8 * SQUARE_NB_CHESS + 1, ++ PS_B_QUEEN = 9 * SQUARE_NB_CHESS + 1, ++ PS_NB = 10 * SQUARE_NB_CHESS + 1, + }; + - static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { ++ static constexpr uint32_t PieceSquareIndex[COLOR_NB][PIECE_NB] = { + // convention: W - us, B - them + // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } ++ { ++ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_QUEEN, PS_W_BISHOP, ++ PS_W_BISHOP, PS_W_BISHOP, PS_W_QUEEN, PS_W_QUEEN, PS_NONE, PS_NONE, PS_W_QUEEN, PS_W_KNIGHT, ++ PS_W_BISHOP, PS_W_KNIGHT, PS_W_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_W_BISHOP, PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ ++ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_QUEEN, PS_B_BISHOP, ++ PS_B_BISHOP, PS_B_BISHOP, PS_B_QUEEN, PS_B_QUEEN, PS_NONE, PS_NONE, PS_B_QUEEN, PS_B_KNIGHT, ++ PS_B_BISHOP, PS_B_KNIGHT, PS_B_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_B_BISHOP, PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ }, ++ ++ { ++ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_QUEEN, PS_B_BISHOP, ++ PS_B_BISHOP, PS_B_BISHOP, PS_B_QUEEN, PS_B_QUEEN, PS_NONE, PS_NONE, PS_B_QUEEN, PS_B_KNIGHT, ++ PS_B_BISHOP, PS_B_KNIGHT, PS_B_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_B_BISHOP, PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ ++ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_QUEEN, PS_W_BISHOP, ++ PS_W_BISHOP, PS_W_BISHOP, PS_W_QUEEN, PS_W_QUEEN, PS_NONE, PS_NONE, PS_W_QUEEN, PS_W_KNIGHT, ++ PS_W_BISHOP, PS_W_KNIGHT, PS_W_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_W_BISHOP, PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ } + }; ++ // Check that the fragile array definition is correct ++ static_assert(PieceSquareIndex[WHITE][make_piece(WHITE, PAWN)] == PS_W_PAWN); ++ static_assert(PieceSquareIndex[WHITE][make_piece(WHITE, KING)] == PS_NONE); ++ static_assert(PieceSquareIndex[WHITE][make_piece(BLACK, PAWN)] == PS_B_PAWN); ++ static_assert(PieceSquareIndex[WHITE][make_piece(BLACK, KING)] == PS_NONE); ++ + + // Orient a square according to perspective (rotates by 180 for black) + static Square orient(Color perspective, Square s); + + // Index of a feature for a given king position and another piece on some square + static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); + public: // Feature name static constexpr const char* Name = "HalfKP(Friend)"; + // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = - 0x5D69D5B9u ^ (AssociatedKing == Side::Friend); + static constexpr std::uint32_t HashValue = 0x5D69D5B8u; + // Number of feature dimensions static constexpr IndexType Dimensions = - static_cast(SQUARE_NB) * static_cast(PS_NB); + static_cast(SQUARE_NB_CHESS) * static_cast(PS_NB); - // Maximum number of simultaneously active features - static constexpr IndexType MaxActiveDimensions = 30; // Kings don't count - // Trigger for full calculation instead of difference calculation - static constexpr TriggerEvent RefreshTrigger = TriggerEvent::FriendKingMoved; + + // Maximum number of simultaneously active features. 30 because kins are not included. + static constexpr IndexType MaxActiveDimensions = 30; // Get a list of indices for active features - static void append_active_indices(const Position& pos, Color perspective, - IndexList* active); + static void append_active_indices( + const Position& pos, + Color perspective, + ValueListInserter active); // Get a list of indices for recently changed features - static void append_changed_indices(const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added); + static void append_changed_indices( + Square ksq, + StateInfo* st, + Color perspective, + ValueListInserter removed, - ValueListInserter added); ++ ValueListInserter added, ++ const Position& pos); + + // Returns the cost of updating one perspective, the most costly one. + // Assumes no refresh needed. + static int update_cost(StateInfo* st); + static int refresh_cost(const Position& pos); + + // Returns whether the change stored in this StateInfo means that + // a full accumulator refresh is required. - static bool requires_refresh(StateInfo* st, Color perspective); ++ static bool requires_refresh(StateInfo* st, Color perspective, const Position& pos); }; } // namespace Stockfish::Eval::NNUE::Features diff --cc src/nnue/features/half_kp_shogi.cpp index fbadfe5,0000000..0d7e2fa mode 100644,000000..100644 --- a/src/nnue/features/half_kp_shogi.cpp +++ b/src/nnue/features/half_kp_shogi.cpp @@@ -1,98 -1,0 +1,116 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//Definition of input features HalfKP of NNUE evaluation function + +#include "half_kp_shogi.h" - #include "index_list.h" ++ ++#include "../../position.h" + +namespace Stockfish::Eval::NNUE::Features { + + constexpr Square rotate(Square s) { + return Square(SQUARE_NB_SHOGI - 1 - int(s)); + } + + constexpr Square to_shogi_square(Square s) { + return Square((8 - s % 12) * 9 + 8 - s / 12); + } + + // Orient a square according to perspective (rotates by 180 for black) - inline Square orient(Color perspective, Square s) { ++ inline Square HalfKPShogi::orient(Color perspective, Square s) { + return perspective == WHITE ? to_shogi_square(s) : rotate(to_shogi_square(s)); + } + + // Index of a feature for a given king position and another piece on some square - inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { ++ inline IndexType HalfKPShogi::make_index(Color perspective, Square s, Piece pc, Square ksq) { + return IndexType(orient(perspective, s) + PieceSquareIndexShogi[perspective][pc] + SHOGI_PS_END * ksq); + } + + // Index of a feature for a given king position and hand piece - inline IndexType make_index(Color perspective, Color c, int hand_index, PieceType pt, Square ksq) { ++ inline IndexType HalfKPShogi::make_index(Color perspective, Color c, int hand_index, PieceType pt, Square ksq) { + Color color = (c == perspective) ? WHITE : BLACK; + return IndexType(hand_index + PieceSquareIndexShogiHand[color][pt] + SHOGI_PS_END * ksq); + } + + // Get a list of indices for active features - template - void HalfKPShogi::append_active_indices( - const Position& pos, Color perspective, IndexList* active) { - ++ void HalfKPShogi::append_active_indices( ++ const Position& pos, ++ Color perspective, ++ ValueListInserter active ++ ) { + Square ksq = orient(perspective, pos.square(perspective)); + Bitboard bb = pos.pieces() & ~pos.pieces(KING); + while (bb) { + Square s = pop_lsb(bb); - active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); ++ active.push_back(make_index(perspective, s, pos.piece_on(s), ksq)); + } + + // Indices for pieces in hand + for (Color c : {WHITE, BLACK}) + for (PieceType pt : pos.piece_types()) + for (int i = 0; i < pos.count_in_hand(c, pt); i++) - active->push_back(make_index(perspective, c, i, pt, ksq)); ++ active.push_back(make_index(perspective, c, i, pt, ksq)); + } + - // Get a list of indices for recently changed features - template - void HalfKPShogi::append_changed_indices( - const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added) { - - Square ksq = orient(perspective, pos.square(perspective)); ++ // append_changed_indices() : get a list of indices for recently changed features ++ ++ void HalfKPShogi::append_changed_indices( ++ Square ksq, ++ StateInfo* st, ++ Color perspective, ++ ValueListInserter removed, ++ ValueListInserter added, ++ const Position& pos ++ ) { ++ const auto& dp = st->dirtyPiece; ++ Square oriented_ksq = orient(perspective, ksq); + for (int i = 0; i < dp.dirty_num; ++i) { + Piece pc = dp.piece[i]; - if (type_of(pc) == KING) continue; ++ if (type_of(pc) == pos.nnue_king()) continue; + if (dp.from[i] != SQ_NONE) - removed->push_back(make_index(perspective, dp.from[i], pc, ksq)); ++ removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); + else if (dp.dirty_num == 1) + { + Piece handPc = dp.handPiece[i]; - removed->push_back(make_index(perspective, color_of(handPc), pos.count_in_hand(color_of(handPc), type_of(handPc)), type_of(handPc), ksq)); ++ removed.push_back(make_index(perspective, color_of(handPc), dp.handCount[i], type_of(handPc), oriented_ksq)); + } + if (dp.to[i] != SQ_NONE) - added->push_back(make_index(perspective, dp.to[i], pc, ksq)); ++ added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq)); + else if (i == 1) + { + Piece handPc = dp.handPiece[i]; - added->push_back(make_index(perspective, color_of(handPc), pos.count_in_hand(color_of(handPc), type_of(handPc)) - 1, type_of(handPc), ksq)); ++ added.push_back(make_index(perspective, color_of(handPc), dp.handCount[i] - 1, type_of(handPc), oriented_ksq)); + } + } + } + - template class HalfKPShogi; ++ int HalfKPShogi::update_cost(StateInfo* st) { ++ return st->dirtyPiece.dirty_num; ++ } ++ ++ int HalfKPShogi::refresh_cost(const Position& pos) { ++ return pos.count() - 2; ++ } ++ ++ bool HalfKPShogi::requires_refresh(StateInfo* st, Color perspective, const Position& pos) { ++ return st->dirtyPiece.piece[0] == make_piece(perspective, pos.nnue_king()); ++ } ++ + +} // namespace Stockfish::Eval::NNUE::Features diff --cc src/nnue/features/half_kp_shogi.h index 343d2d1,0000000..f6033b0 mode 100644,000000..100644 --- a/src/nnue/features/half_kp_shogi.h +++ b/src/nnue/features/half_kp_shogi.h @@@ -1,59 -1,0 +1,209 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//Definition of input features HalfKP of NNUE evaluation function + +#ifndef NNUE_FEATURES_HALF_KP_SHOGI_H_INCLUDED +#define NNUE_FEATURES_HALF_KP_SHOGI_H_INCLUDED + ++#include "../nnue_common.h" ++ +#include "../../evaluate.h" - #include "features_common.h" ++#include "../../misc.h" ++ ++namespace Stockfish { ++ struct StateInfo; ++} + +namespace Stockfish::Eval::NNUE::Features { + + // Feature HalfKP: Combination of the position of own king + // and the position of pieces other than kings - template + class HalfKPShogi { + ++ enum { ++ PS_NONE = 0, ++ SHOGI_HAND_W_PAWN = 1, ++ SHOGI_HAND_B_PAWN = 20, ++ SHOGI_HAND_W_LANCE = 39, ++ SHOGI_HAND_B_LANCE = 44, ++ SHOGI_HAND_W_KNIGHT = 49, ++ SHOGI_HAND_B_KNIGHT = 54, ++ SHOGI_HAND_W_SILVER = 59, ++ SHOGI_HAND_B_SILVER = 64, ++ SHOGI_HAND_W_GOLD = 69, ++ SHOGI_HAND_B_GOLD = 74, ++ SHOGI_HAND_W_BISHOP = 79, ++ SHOGI_HAND_B_BISHOP = 82, ++ SHOGI_HAND_W_ROOK = 85, ++ SHOGI_HAND_B_ROOK = 88, ++ SHOGI_HAND_END = 90, ++ ++ SHOGI_PS_W_PAWN = SHOGI_HAND_END, ++ SHOGI_PS_B_PAWN = 1 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_LANCE = 2 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_LANCE = 3 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_KNIGHT = 4 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_KNIGHT = 5 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_SILVER = 6 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_SILVER = 7 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_GOLD = 8 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_GOLD = 9 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_BISHOP = 10 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_BISHOP = 11 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_HORSE = 12 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_HORSE = 13 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_ROOK = 14 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_ROOK = 15 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_DRAGON = 16 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_B_DRAGON = 17 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_W_KING = 18 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_END = SHOGI_PS_W_KING, // pieces without kings (pawns included) ++ SHOGI_PS_B_KING = 19 * SQUARE_NB_SHOGI + SHOGI_HAND_END, ++ SHOGI_PS_END2 = 20 * SQUARE_NB_SHOGI + SHOGI_HAND_END ++ }; ++ ++ static constexpr uint32_t PieceSquareIndexShogi[COLOR_NB][PIECE_NB] = { ++ // convention: W - us, B - them ++ // viewed from other side, W and B are reversed ++ { ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_W_BISHOP, SHOGI_PS_W_ROOK, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, SHOGI_PS_W_SILVER, PS_NONE, SHOGI_PS_W_DRAGON, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_W_PAWN, SHOGI_PS_W_LANCE, SHOGI_PS_W_KNIGHT, SHOGI_PS_W_GOLD, SHOGI_PS_W_HORSE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_W_KING, ++ ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_B_BISHOP, SHOGI_PS_B_ROOK, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, SHOGI_PS_B_SILVER, PS_NONE, SHOGI_PS_B_DRAGON, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_B_PAWN, SHOGI_PS_B_LANCE, SHOGI_PS_B_KNIGHT, SHOGI_PS_B_GOLD, SHOGI_PS_B_HORSE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_B_KING ++ }, ++ ++ { ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_B_BISHOP, SHOGI_PS_B_ROOK, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, SHOGI_PS_B_SILVER, PS_NONE, SHOGI_PS_B_DRAGON, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_B_PAWN, SHOGI_PS_B_LANCE, SHOGI_PS_B_KNIGHT, SHOGI_PS_B_GOLD, SHOGI_PS_B_HORSE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_B_KING, ++ ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_W_BISHOP, SHOGI_PS_W_ROOK, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, SHOGI_PS_W_SILVER, PS_NONE, SHOGI_PS_W_DRAGON, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_W_PAWN, SHOGI_PS_W_LANCE, SHOGI_PS_W_KNIGHT, SHOGI_PS_W_GOLD, SHOGI_PS_W_HORSE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, SHOGI_PS_W_KING ++ } ++ }; ++ static_assert(PieceSquareIndexShogi[WHITE][make_piece(WHITE, SHOGI_PAWN)] == SHOGI_PS_W_PAWN); ++ static_assert(PieceSquareIndexShogi[WHITE][make_piece(WHITE, KING)] == SHOGI_PS_W_KING); ++ static_assert(PieceSquareIndexShogi[WHITE][make_piece(BLACK, SHOGI_PAWN)] == SHOGI_PS_B_PAWN); ++ static_assert(PieceSquareIndexShogi[WHITE][make_piece(BLACK, KING)] == SHOGI_PS_B_KING); ++ ++ static constexpr uint32_t PieceSquareIndexShogiHand[COLOR_NB][PIECE_TYPE_NB] = { ++ // convention: W - us, B - them ++ // viewed from other side, W and B are reversed ++ { ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_HAND_W_BISHOP, SHOGI_HAND_W_ROOK, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, SHOGI_HAND_W_SILVER, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_HAND_W_PAWN, SHOGI_HAND_W_LANCE, SHOGI_HAND_W_KNIGHT, SHOGI_HAND_W_GOLD, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE ++ }, ++ ++ { ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_HAND_B_BISHOP, SHOGI_HAND_B_ROOK, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, SHOGI_HAND_B_SILVER, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, SHOGI_HAND_B_PAWN, SHOGI_HAND_B_LANCE, SHOGI_HAND_B_KNIGHT, SHOGI_HAND_B_GOLD, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE ++ } ++ }; ++ static_assert(PieceSquareIndexShogiHand[WHITE][SHOGI_PAWN] == SHOGI_HAND_W_PAWN); ++ static_assert(PieceSquareIndexShogiHand[WHITE][GOLD] == SHOGI_HAND_W_GOLD); ++ static_assert(PieceSquareIndexShogiHand[BLACK][SHOGI_PAWN] == SHOGI_HAND_B_PAWN); ++ static_assert(PieceSquareIndexShogiHand[BLACK][GOLD] == SHOGI_HAND_B_GOLD); ++ ++ // Orient a square according to perspective (rotates by 180 for black) ++ static Square orient(Color perspective, Square s); ++ ++ // Index of a feature for a given king position and another piece on some square ++ static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); ++ ++ // Index of a feature for a given king position and a piece in hand ++ static IndexType make_index(Color perspective, Color c, int hand_index, PieceType pt, Square ksq); ++ + public: + // Feature name + static constexpr const char* Name = "HalfKP(Friend)"; ++ + // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = - 0x5D69D5B9u ^ (AssociatedKing == Side::Friend); ++ static constexpr std::uint32_t HashValue = 0x5D69D5B8u; ++ + // Number of feature dimensions + static constexpr IndexType Dimensions = + static_cast(SQUARE_NB_SHOGI) * static_cast(SHOGI_PS_END); - // Maximum number of simultaneously active features - static constexpr IndexType MaxActiveDimensions = 38; // Kings don't count - // Trigger for full calculation instead of difference calculation - static constexpr TriggerEvent RefreshTrigger = TriggerEvent::FriendKingMoved; ++ ++ // Maximum number of simultaneously active features. 38 because kins are not included. ++ static constexpr IndexType MaxActiveDimensions = 38; + + // Get a list of indices for active features - static void append_active_indices(const Position& pos, Color perspective, - IndexList* active); ++ static void append_active_indices( ++ const Position& pos, ++ Color perspective, ++ ValueListInserter active); + + // Get a list of indices for recently changed features - static void append_changed_indices(const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added); ++ static void append_changed_indices( ++ Square ksq, ++ StateInfo* st, ++ Color perspective, ++ ValueListInserter removed, ++ ValueListInserter added, ++ const Position& pos); ++ ++ // Returns the cost of updating one perspective, the most costly one. ++ // Assumes no refresh needed. ++ static int update_cost(StateInfo* st); ++ static int refresh_cost(const Position& pos); ++ ++ // Returns whether the change stored in this StateInfo means that ++ // a full accumulator refresh is required. ++ static bool requires_refresh(StateInfo* st, Color perspective, const Position& pos); + }; + +} // namespace Stockfish::Eval::NNUE::Features + +#endif // #ifndef NNUE_FEATURES_HALF_KP_SHOGI_H_INCLUDED diff --cc src/nnue/features/half_kp_variants.cpp index a149823,0000000..d93107f mode 100644,000000..100644 --- a/src/nnue/features/half_kp_variants.cpp +++ b/src/nnue/features/half_kp_variants.cpp @@@ -1,96 -1,0 +1,112 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//Definition of input features HalfKP of NNUE evaluation function + +#include "half_kp_variants.h" - #include "index_list.h" + +#ifdef LARGEBOARDS +#include "half_kp_shogi.h" +#endif + ++#include "../../position.h" ++ +namespace Stockfish::Eval::NNUE::Features { + + // Map square to numbering on 8x8 board + constexpr Square to_chess_square(Square s) { + return Square(s - rank_of(s) * (FILE_MAX - FILE_H)); + } + + // Orient a square according to perspective (rotates by 180 for black) - inline Square orient(const Position& pos, Color perspective, Square s) { ++ inline Square HalfKPVariants::orient(Color perspective, Square s, const Position& pos) { + return to_chess_square( perspective == WHITE || (pos.capture_the_flag(BLACK) & Rank8BB) ? s + : flip_rank(flip_file(s, pos.max_file()), pos.max_rank())); + } + + // Index of a feature for a given king position and another piece on some square - inline IndexType make_index(const Position& pos, Color perspective, Square s, Piece pc, Square ksq) { - return IndexType(orient(pos, perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); ++ inline IndexType HalfKPVariants::make_index(Color perspective, Square s, Piece pc, Square ksq, const Position& pos) { ++ return IndexType(orient(perspective, s, pos) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); + } + + // Get a list of indices for active features - template - void HalfKPVariants::append_active_indices( - const Position& pos, Color perspective, IndexList* active) { - ++ void HalfKPVariants::append_active_indices( ++ const Position& pos, ++ Color perspective, ++ ValueListInserter active ++ ) { + // Re-route to shogi features +#ifdef LARGEBOARDS + if (currentNnueFeatures == NNUE_SHOGI) + { - assert(HalfKPShogi::Dimensions <= Dimensions); - return HalfKPShogi::append_active_indices(pos, perspective, active); ++ assert(HalfKPShogi::Dimensions <= Dimensions); ++ return HalfKPShogi::append_active_indices(pos, perspective, active); + } +#endif + - Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king())); ++ Square oriented_ksq = orient(perspective, pos.square(perspective, pos.nnue_king()), pos); + Bitboard bb = pos.pieces() & ~pos.pieces(pos.nnue_king()); + while (bb) { + Square s = pop_lsb(bb); - active->push_back(make_index(pos, perspective, s, pos.piece_on(s), ksq)); ++ active.push_back(make_index(perspective, s, pos.piece_on(s), oriented_ksq, pos)); + } + } + - // Get a list of indices for recently changed features - template - void HalfKPVariants::append_changed_indices( - const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added) { ++ // append_changed_indices() : get a list of indices for recently changed features + ++ void HalfKPVariants::append_changed_indices( ++ Square ksq, ++ StateInfo* st, ++ Color perspective, ++ ValueListInserter removed, ++ ValueListInserter added, ++ const Position& pos ++ ) { + // Re-route to shogi features +#ifdef LARGEBOARDS + if (currentNnueFeatures == NNUE_SHOGI) + { - assert(HalfKPShogi::Dimensions <= Dimensions); - return HalfKPShogi::append_changed_indices(pos, dp, perspective, removed, added); ++ assert(HalfKPShogi::Dimensions <= Dimensions); ++ return HalfKPShogi::append_changed_indices(ksq, st, perspective, removed, added, pos); + } +#endif - - Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king())); ++ const auto& dp = st->dirtyPiece; ++ Square oriented_ksq = orient(perspective, ksq, pos); + for (int i = 0; i < dp.dirty_num; ++i) { + Piece pc = dp.piece[i]; + if (type_of(pc) == pos.nnue_king()) continue; + if (dp.from[i] != SQ_NONE) - removed->push_back(make_index(pos, perspective, dp.from[i], pc, ksq)); ++ removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq, pos)); + if (dp.to[i] != SQ_NONE) - added->push_back(make_index(pos, perspective, dp.to[i], pc, ksq)); ++ added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq, pos)); + } + } + - template class HalfKPVariants; ++ int HalfKPVariants::update_cost(StateInfo* st) { ++ return st->dirtyPiece.dirty_num; ++ } ++ ++ int HalfKPVariants::refresh_cost(const Position& pos) { ++ return pos.count() - 2; ++ } ++ ++ bool HalfKPVariants::requires_refresh(StateInfo* st, Color perspective, const Position& pos) { ++ return st->dirtyPiece.piece[0] == make_piece(perspective, pos.nnue_king()); ++ } + +} // namespace Stockfish::Eval::NNUE::Features diff --cc src/nnue/features/half_kp_variants.h index f5720d4,0000000..f095737 mode 100644,000000..100644 --- a/src/nnue/features/half_kp_variants.h +++ b/src/nnue/features/half_kp_variants.h @@@ -1,63 -1,0 +1,164 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//Definition of input features HalfKP of NNUE evaluation function + +#ifndef NNUE_FEATURES_HALF_KP_VARIANTS_H_INCLUDED +#define NNUE_FEATURES_HALF_KP_VARIANTS_H_INCLUDED + ++#include "../nnue_common.h" ++ +#include "../../evaluate.h" - #include "features_common.h" ++#include "../../misc.h" ++ ++#include "half_kp_shogi.h" ++#include "half_kp.h" ++ ++namespace Stockfish { ++ struct StateInfo; ++} + +namespace Stockfish::Eval::NNUE::Features { + + // Feature HalfKP: Combination of the position of own king + // and the position of pieces other than kings - template + class HalfKPVariants { + ++ // unique number for each piece type on each square ++ enum { ++ PS_NONE = 0, ++ PS_W_PAWN = 1, ++ PS_B_PAWN = 1 * SQUARE_NB_CHESS + 1, ++ PS_W_KNIGHT = 2 * SQUARE_NB_CHESS + 1, ++ PS_B_KNIGHT = 3 * SQUARE_NB_CHESS + 1, ++ PS_W_BISHOP = 4 * SQUARE_NB_CHESS + 1, ++ PS_B_BISHOP = 5 * SQUARE_NB_CHESS + 1, ++ PS_W_ROOK = 6 * SQUARE_NB_CHESS + 1, ++ PS_B_ROOK = 7 * SQUARE_NB_CHESS + 1, ++ PS_W_QUEEN = 8 * SQUARE_NB_CHESS + 1, ++ PS_B_QUEEN = 9 * SQUARE_NB_CHESS + 1, ++ PS_NB = 10 * SQUARE_NB_CHESS + 1, ++ }; ++ ++ static constexpr uint32_t PieceSquareIndex[COLOR_NB][PIECE_NB] = { ++ // convention: W - us, B - them ++ // viewed from other side, W and B are reversed ++ { ++ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_QUEEN, PS_W_BISHOP, ++ PS_W_BISHOP, PS_W_BISHOP, PS_W_QUEEN, PS_W_QUEEN, PS_NONE, PS_NONE, PS_W_QUEEN, PS_W_KNIGHT, ++ PS_W_BISHOP, PS_W_KNIGHT, PS_W_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_W_BISHOP, PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ ++ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_QUEEN, PS_B_BISHOP, ++ PS_B_BISHOP, PS_B_BISHOP, PS_B_QUEEN, PS_B_QUEEN, PS_NONE, PS_NONE, PS_B_QUEEN, PS_B_KNIGHT, ++ PS_B_BISHOP, PS_B_KNIGHT, PS_B_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_B_BISHOP, PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ }, ++ ++ { ++ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_QUEEN, PS_B_BISHOP, ++ PS_B_BISHOP, PS_B_BISHOP, PS_B_QUEEN, PS_B_QUEEN, PS_NONE, PS_NONE, PS_B_QUEEN, PS_B_KNIGHT, ++ PS_B_BISHOP, PS_B_KNIGHT, PS_B_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_B_BISHOP, PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ ++ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_QUEEN, PS_W_BISHOP, ++ PS_W_BISHOP, PS_W_BISHOP, PS_W_QUEEN, PS_W_QUEEN, PS_NONE, PS_NONE, PS_W_QUEEN, PS_W_KNIGHT, ++ PS_W_BISHOP, PS_W_KNIGHT, PS_W_ROOK, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_W_BISHOP, PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, ++ } ++ }; ++ // Check that the fragile array definition is correct ++ static_assert(PieceSquareIndex[WHITE][make_piece(WHITE, PAWN)] == PS_W_PAWN); ++ static_assert(PieceSquareIndex[WHITE][make_piece(WHITE, KING)] == PS_NONE); ++ static_assert(PieceSquareIndex[WHITE][make_piece(BLACK, PAWN)] == PS_B_PAWN); ++ static_assert(PieceSquareIndex[WHITE][make_piece(BLACK, KING)] == PS_NONE); ++ ++ // Orient a square according to perspective (rotates by 180 for black) ++ static Square orient(Color perspective, Square s, const Position& pos); ++ ++ // Index of a feature for a given king position and another piece on some square ++ static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq, const Position& pos); ++ + public: + // Feature name + static constexpr const char* Name = "HalfKP(Friend)"; ++ + // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = - 0x5D69D5B9u ^ (AssociatedKing == Side::Friend); ++ static constexpr std::uint32_t HashValue = 0x5D69D5B8u; ++ + // Number of feature dimensions + static constexpr IndexType Dimensions = +#ifdef LARGEBOARDS - static_cast(SQUARE_NB_SHOGI) * static_cast(SHOGI_PS_END); ++ HalfKPShogi::Dimensions; +#else - static_cast(SQUARE_NB_CHESS) * static_cast(PS_NB); ++ HalfKPChess::Dimensions; +#endif - // Maximum number of simultaneously active features - static constexpr IndexType MaxActiveDimensions = 64; // Kings don't count - // Trigger for full calculation instead of difference calculation - static constexpr TriggerEvent RefreshTrigger = TriggerEvent::FriendKingMoved; ++ ++ static IndexType get_dimensions() { ++ return currentNnueFeatures == NNUE_SHOGI ? HalfKPShogi::Dimensions ++ : currentNnueFeatures == NNUE_CHESS ? HalfKPChess::Dimensions ++ : SQUARE_NB_CHESS * PS_NB; ++ } ++ ++ // Maximum number of simultaneously active features. 30 because kins are not included. ++ static constexpr IndexType MaxActiveDimensions = 64; + + // Get a list of indices for active features - static void append_active_indices(const Position& pos, Color perspective, - IndexList* active); ++ static void append_active_indices( ++ const Position& pos, ++ Color perspective, ++ ValueListInserter active); + + // Get a list of indices for recently changed features - static void append_changed_indices(const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added); ++ static void append_changed_indices( ++ Square ksq, ++ StateInfo* st, ++ Color perspective, ++ ValueListInserter removed, ++ ValueListInserter added, ++ const Position& pos); ++ ++ // Returns the cost of updating one perspective, the most costly one. ++ // Assumes no refresh needed. ++ static int update_cost(StateInfo* st); ++ static int refresh_cost(const Position& pos); ++ ++ // Returns whether the change stored in this StateInfo means that ++ // a full accumulator refresh is required. ++ static bool requires_refresh(StateInfo* st, Color perspective, const Position& pos); + }; + +} // namespace Stockfish::Eval::NNUE::Features + +#endif // #ifndef NNUE_FEATURES_HALF_KP_VARIANTS_H_INCLUDED diff --cc src/nnue/nnue_architecture.h index e3a7323,55a01fb..3dd6fe2 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@@ -21,13 -21,34 +21,36 @@@ #ifndef NNUE_ARCHITECTURE_H_INCLUDED #define NNUE_ARCHITECTURE_H_INCLUDED - // Defines the network structure - // #include "architectures/halfkp_256x2-32-32.h" - // #include "architectures/halfkp_shogi_256x2-32-32.h" - #include "architectures/halfkp_variants_256x2-32-32.h" + #include "nnue_common.h" + -#include "features/half_kp.h" ++//#include "features/half_kp.h" ++#include "features/half_kp_variants.h" + + #include "layers/input_slice.h" + #include "layers/affine_transform.h" + #include "layers/clipped_relu.h" namespace Stockfish::Eval::NNUE { + // Input features used in evaluation function - using FeatureSet = Features::HalfKP; ++ //using FeatureSet = Features::HalfKPChess; ++ using FeatureSet = Features::HalfKPVariants; + + // Number of input feature dimensions after conversion + constexpr IndexType TransformedFeatureDimensions = 256; + + namespace Layers { + + // Define network structure + using InputLayer = InputSlice; + using HiddenLayer1 = ClippedReLU>; + using HiddenLayer2 = ClippedReLU>; + using OutputLayer = AffineTransform; + + } // namespace Layers + + using Network = Layers::OutputLayer; + static_assert(TransformedFeatureDimensions % MaxSimdWidth == 0, ""); static_assert(Network::OutputDimensions == 1, ""); static_assert(std::is_same::value, ""); diff --cc src/nnue/nnue_feature_transformer.h index 4da475e,f441274..bb4c63e --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@@ -110,12 -111,9 +111,10 @@@ namespace Stockfish::Eval::NNUE // Read network parameters bool read_parameters(std::istream& stream) { + for (std::size_t i = 0; i < HalfDimensions; ++i) biases[i] = read_little_endian(stream); - for (std::size_t i = 0; i < HalfDimensions * ( currentNnueFeatures == NNUE_SHOGI ? SQUARE_NB_SHOGI * SHOGI_PS_END - : currentNnueFeatures == NNUE_CHESS ? SQUARE_NB_CHESS * PS_NB - : SQUARE_NB_CHESS * PS_NB); ++i) - for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) ++ for (std::size_t i = 0; i < HalfDimensions * FeatureSet::get_dimensions(); ++i) weights[i] = read_little_endian(stream); return !stream.fail(); } @@@ -247,18 -251,13 +252,13 @@@ // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; - int gain = pos.count() - 2; - - while (st->accumulator.state[c] == EMPTY) + int gain = FeatureSet::refresh_cost(pos); + while (st->accumulator.state[perspective] == EMPTY) { - auto& dp = st->dirtyPiece; - // The first condition tests whether an incremental update is - // possible at all: if this side's king has moved, it is not possible. - static_assert(std::is_same_v>, - "Current code assumes that only FriendlyKingMoved refresh trigger is being used."); - if ( dp.piece[0] == make_piece(c, pos.nnue_king()) - || (gain -= dp.dirty_num + 1) < 0) + // This governs when a full feature refresh is needed and how many + // updates are better than just one full refresh. - if ( FeatureSet::requires_refresh(st, perspective) ++ if ( FeatureSet::requires_refresh(st, perspective, pos) + || (gain -= FeatureSet::update_cost(st) + 1) < 0) break; next = st; st = st->previous; @@@ -272,23 -271,21 +272,21 @@@ // Update incrementally in two steps. First, we update the "next" // accumulator. Then, we update the current accumulator (pos.state()). - // Gather all features to be updated. This code assumes HalfKP features - // only and doesn't support refresh triggers. - static_assert(std::is_same_v>, - RawFeatures>); - Features::IndexList removed[2], added[2]; - Features::HalfKP::append_changed_indices(pos, - next->dirtyPiece, c, &removed[0], &added[0]); + // Gather all features to be updated. - const Square ksq = pos.square(perspective); ++ const Square ksq = pos.square(perspective, pos.nnue_king()); + IndexList removed[2], added[2]; + FeatureSet::append_changed_indices( - ksq, next, perspective, removed[0], added[0]); ++ ksq, next, perspective, removed[0], added[0], pos); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) - Features::HalfKP::append_changed_indices(pos, - st2->dirtyPiece, c, &removed[1], &added[1]); + FeatureSet::append_changed_indices( - ksq, st2, perspective, removed[1], added[1]); ++ ksq, st2, perspective, removed[1], added[1], pos); // Mark the accumulators as computed. - next->accumulator.state[c] = COMPUTED; - pos.state()->accumulator.state[c] = COMPUTED; + next->accumulator.state[perspective] = COMPUTED; + pos.state()->accumulator.state[perspective] = COMPUTED; - // Now update the accumulators listed in info[], where the last element is a sentinel. - StateInfo *info[3] = + // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. + StateInfo *states_to_update[3] = { next, next == pos.state() ? nullptr : pos.state(), nullptr }; #ifdef VECTOR for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) diff --cc src/position.cpp index 1956d7d,2b3be3f..a37108b --- a/src/position.cpp +++ b/src/position.cpp @@@ -1417,20 -767,6 +1417,23 @@@ void Position::do_move(Move m, StateInf if (type_of(m) == EN_PASSANT) board[capsq] = NO_PIECE; + if (captures_to_hand()) + { + Piece pieceToHand = !capturedPromoted || drop_loop() ? ~captured + : unpromotedCaptured ? ~unpromotedCaptured + : make_piece(~color_of(captured), PAWN); + add_to_hand(pieceToHand); + k ^= Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)] - 1] + ^ Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]]; + + if (Eval::useNNUE) ++ { + dp.handPiece[1] = pieceToHand; ++ dp.handCount[1] = pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]; ++ } + } + else + dp.handPiece[1] = NO_PIECE; // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[captured][capsq]; @@@ -1468,85 -795,8 +1471,86 @@@ k ^= Zobrist::castling[st->castlingRights]; } + // Flip enclosed pieces + st->flippedPieces = 0; + if (flip_enclosed_pieces() && !is_pass(m)) + { + // Find end of rows to be flipped + if (flip_enclosed_pieces() == REVERSI) + { + Bitboard b = attacks_bb(us, QUEEN, to, board_bb() & ~pieces(~us)) & ~PseudoAttacks[us][KING][to] & pieces(us); + while(b) + st->flippedPieces |= between_bb(to, pop_lsb(b)); + } + else + { + assert(flip_enclosed_pieces() == ATAXX); + st->flippedPieces = PseudoAttacks[us][KING][to] & pieces(~us); + } + + // Flip pieces + Bitboard to_flip = st->flippedPieces; + while(to_flip) + { + Square s = pop_lsb(to_flip); + Piece flipped = piece_on(s); + Piece resulting = ~flipped; + + // remove opponent's piece + remove_piece(s); + k ^= Zobrist::psq[flipped][s]; + st->materialKey ^= Zobrist::psq[flipped][pieceCount[flipped]]; + st->nonPawnMaterial[them] -= PieceValue[MG][flipped]; + + // add our piece + put_piece(resulting, s); + k ^= Zobrist::psq[resulting][s]; + st->materialKey ^= Zobrist::psq[resulting][pieceCount[resulting]-1]; + st->nonPawnMaterial[us] += PieceValue[MG][resulting]; + } + } + // Move the piece. The tricky Chess960 castling is handled earlier - if (type_of(m) != CASTLING) + if (type_of(m) == DROP) + { + if (Eval::useNNUE) + { + // Add drop piece + dp.piece[0] = pc; + dp.handPiece[0] = make_piece(us, in_hand_piece_type(m)); ++ dp.handCount[0] = pieceCountInHand[us][in_hand_piece_type(m)]; + dp.from[0] = SQ_NONE; + dp.to[0] = to; + } + + drop_piece(make_piece(us, in_hand_piece_type(m)), pc, to); + st->materialKey ^= Zobrist::psq[pc][pieceCount[pc]-1]; + if (type_of(pc) != PAWN) + st->nonPawnMaterial[us] += PieceValue[MG][pc]; + // Set castling rights for dropped king or rook + if (castling_dropped_piece() && rank_of(to) == castling_rank(us)) + { + if (type_of(pc) == castling_king_piece() && file_of(to) == castling_king_file()) + { + st->castlingKingSquare[us] = to; + Bitboard castling_rooks = pieces(us, castling_rook_piece()) + & rank_bb(castling_rank(us)) + & (file_bb(FILE_A) | file_bb(max_file())); + while (castling_rooks) + set_castling_right(us, pop_lsb(castling_rooks)); + } + else if (type_of(pc) == castling_rook_piece()) + { + if ( (file_of(to) == FILE_A || file_of(to) == max_file()) + && piece_on(make_square(castling_king_file(), castling_rank(us))) == make_piece(us, castling_king_piece())) + { + st->castlingKingSquare[us] = make_square(castling_king_file(), castling_rank(us)); + set_castling_right(us, to); + } + } + } + } + else if (type_of(m) != CASTLING) { if (Eval::useNNUE) { @@@ -1664,113 -859,11 +1668,117 @@@ // Set capture piece st->capturedPiece = captured; + // Add gating piece + if (is_gating(m)) + { + Square gate = gating_square(m); + Piece gating_piece = make_piece(us, gating_type(m)); + + put_piece(gating_piece, gate); + remove_from_hand(gating_piece); + + if (Eval::useNNUE) + { + // Add gating piece + dp.piece[dp.dirty_num] = gating_piece; + dp.handPiece[dp.dirty_num] = gating_piece; ++ dp.handCount[dp.dirty_num] = pieceCountInHand[us][gating_type(m)]; + dp.from[dp.dirty_num] = SQ_NONE; + dp.to[dp.dirty_num] = gate; + dp.dirty_num++; + } + + st->gatesBB[us] ^= gate; + k ^= Zobrist::psq[gating_piece][gate]; + st->materialKey ^= Zobrist::psq[gating_piece][pieceCount[gating_piece]]; + st->nonPawnMaterial[us] += PieceValue[MG][gating_piece]; + } + + // Remove gates + if (gating()) + { + if (is_ok(from) && (gates(us) & from)) + st->gatesBB[us] ^= from; + if (type_of(m) == CASTLING && (gates(us) & to_sq(m))) + st->gatesBB[us] ^= to_sq(m); + if (gates(them) & to) + st->gatesBB[them] ^= to; + if (seirawan_gating() && count_in_hand(us, ALL_PIECES) == 0 && !captures_to_hand()) + st->gatesBB[us] = 0; + } + + // Remove the blast pieces + if (captured && blast_on_capture()) + { + std::memset(st->unpromotedBycatch, 0, sizeof(st->unpromotedBycatch)); + st->demotedBycatch = st->promotedBycatch = 0; + Bitboard blast = (attacks_bb(to) & (pieces() ^ pieces(PAWN))) | to; + while (blast) + { + Square bsq = pop_lsb(blast); + Piece bpc = piece_on(bsq); + Color bc = color_of(bpc); + if (type_of(bpc) != PAWN) + st->nonPawnMaterial[bc] -= PieceValue[MG][bpc]; + + if (Eval::useNNUE) + { + dp.piece[dp.dirty_num] = bpc; + dp.handPiece[dp.dirty_num] = NO_PIECE; + dp.from[dp.dirty_num] = bsq; + dp.to[dp.dirty_num] = SQ_NONE; + dp.dirty_num++; + } + + // Update board and piece lists + // In order to not have to store the values of both board and unpromotedBoard, + // demote promoted pieces, but keep promoted pawns as promoted, + // and store demotion/promotion bitboards to disambiguate the piece state + bool capturedPromoted = is_promoted(bsq); + Piece unpromotedCaptured = unpromoted_piece_on(bsq); + st->unpromotedBycatch[bsq] = unpromotedCaptured ? unpromotedCaptured : bpc; + if (unpromotedCaptured) + st->demotedBycatch |= bsq; + else if (capturedPromoted) + st->promotedBycatch |= bsq; + remove_piece(bsq); + board[bsq] = NO_PIECE; + if (captures_to_hand()) + { + Piece pieceToHand = !capturedPromoted || drop_loop() ? ~bpc + : unpromotedCaptured ? ~unpromotedCaptured + : make_piece(~color_of(bpc), PAWN); + add_to_hand(pieceToHand); + k ^= Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)] - 1] + ^ Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]]; + + if (Eval::useNNUE) ++ { + dp.handPiece[dp.dirty_num - 1] = pieceToHand; ++ dp.handCount[dp.dirty_num - 1] = pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]; ++ } + } + + // Update material hash key + k ^= Zobrist::psq[bpc][bsq]; + st->materialKey ^= Zobrist::psq[bpc][pieceCount[bpc]]; + if (type_of(bpc) == PAWN) + st->pawnKey ^= Zobrist::psq[bpc][bsq]; + + // Update castling rights if needed + if (st->castlingRights && castlingRightsMask[bsq]) + { + int cr = castlingRightsMask[bsq]; + k ^= Zobrist::castling[st->castlingRights & cr]; + st->castlingRights &= ~cr; + } + } + } + // Update the key with the final value st->key = k; - // Calculate checkers bitboard (if move gives check) - st->checkersBB = givesCheck ? attackers_to(square(them)) & pieces(us) : 0; + st->checkersBB = givesCheck ? attackers_to(square(them), us) & pieces(us) : Bitboard(0); sideToMove = ~sideToMove; diff --cc src/types.h index f2f14a3,efebce1..b10ca04 --- a/src/types.h +++ b/src/types.h @@@ -535,18 -272,11 +535,19 @@@ struct DirtyPiece // 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]; + Piece piece[12]; + Piece handPiece[12]; ++ int handCount[12]; // From and to squares, which may be SQ_NONE - Square from[3]; - Square to[3]; + Square from[12]; + Square to[12]; +}; + +enum NnueFeatures { + NNUE_CHESS, + NNUE_SHOGI, + NNUE_VARIANT, }; /// Score enum stores a middlegame and an endgame value in a single integer (enum).