Support shogi NNUE evaluation (#199)
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 6 Mar 2021 15:19:33 +0000 (16:19 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sun, 7 Mar 2021 14:56:01 +0000 (15:56 +0100)
Refactor variant NNUE handling in order to support
shogi NNUE evaluation and to improve maintainability.

Credits to @tttak for the initial implementation of this feature.

20 files changed:
src/Makefile
src/Makefile_js
src/evaluate.cpp
src/evaluate.h
src/nnue/architectures/halfkp_256x2-32-32.h
src/nnue/architectures/halfkp_shogi_256x2-32-32.h [new file with mode: 0644]
src/nnue/architectures/halfkp_variants_256x2-32-32.h [new file with mode: 0644]
src/nnue/features/half_kp.cpp
src/nnue/features/half_kp.h
src/nnue/features/half_kp_shogi.cpp [new file with mode: 0644]
src/nnue/features/half_kp_shogi.h [new file with mode: 0644]
src/nnue/features/half_kp_variants.cpp [new file with mode: 0644]
src/nnue/features/half_kp_variants.h [new file with mode: 0644]
src/nnue/nnue_architecture.h
src/nnue/nnue_common.h
src/nnue/nnue_feature_transformer.h
src/position.cpp
src/types.h
src/variant.cpp
src/variant.h

index 46eebba..3531dbe 100644 (file)
@@ -40,7 +40,8 @@ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp
        material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
        search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
        nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp \
-       partner.cpp parser.cpp piece.cpp variant.cpp xboard.cpp
+       partner.cpp parser.cpp piece.cpp variant.cpp xboard.cpp \
+       nnue/features/half_kp_shogi.cpp nnue/features/half_kp_variants.cpp
 
 OBJS = $(notdir $(SRCS:.cpp=.o))
 
@@ -311,6 +312,9 @@ CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS)
 DEPENDFLAGS += -std=c++17
 LDFLAGS += $(EXTRALDFLAGS)
 
+# Ignore warning due to missing profile information from unused NNUE features
+CXXFLAGS += -Wno-profile-instr-out-of-date
+
 # Compile version with support for large board variants
 # Use precomputed magics by default if pext is not available
 ifneq ($(largeboards),no)
index 1e0c34d..0d67211 100644 (file)
@@ -20,7 +20,8 @@ SRCS = ffishjs.cpp benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.c
        material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
        search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
        nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp \
-       partner.cpp parser.cpp piece.cpp variant.cpp xboard.cpp
+       partner.cpp parser.cpp piece.cpp variant.cpp xboard.cpp \
+       nnue/features/half_kp_shogi.cpp nnue/features/half_kp_variants.cpp
 
 CXX=emcc
 CXXFLAGS += --bind -DNNUE_EMBEDDING_OFF -DNO_THREADS -std=c++17 -Wall
index 4262504..3440ad8 100644 (file)
@@ -57,6 +57,8 @@
 using namespace std;
 using namespace Eval::NNUE;
 
+NnueFeatures currentNnueFeatures;
+
 namespace Eval {
 
   bool useNNUE;
@@ -100,6 +102,8 @@ namespace Eval {
     if (!useNNUE)
         return;
 
+    currentNnueFeatures = variants.find(variant)->second->nnueFeatures;
+
     #if defined(DEFAULT_NNUE_DIRECTORY)
     #define stringify2(x) #x
     #define stringify(x) stringify2(x)
index 10af7ae..5b26806 100644 (file)
@@ -50,4 +50,6 @@ namespace Eval {
 
 } // namespace Eval
 
+extern NnueFeatures currentNnueFeatures;
+
 #endif // #ifndef EVALUATE_H_INCLUDED
index a0fe2e0..41d97b1 100644 (file)
 #include "../layers/affine_transform.h"
 #include "../layers/clipped_relu.h"
 
+namespace Eval::NNUE::Features {
+// Alias for compatibility with upstream code
+template <Side AssociatedKing>
+using HalfKP = HalfKPChess<AssociatedKing>;
+}
+
 namespace Eval::NNUE {
 
 // Input features used in evaluation function
diff --git a/src/nnue/architectures/halfkp_shogi_256x2-32-32.h b/src/nnue/architectures/halfkp_shogi_256x2-32-32.h
new file mode 100644 (file)
index 0000000..8879f4f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+  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 <http://www.gnu.org/licenses/>.
+*/
+
+// Definition of input features and network structure used in NNUE evaluation function
+
+#ifndef NNUE_HALFKP_SHOGI_256X2_32_32_H_INCLUDED
+#define NNUE_HALFKP_SHOGI_256X2_32_32_H_INCLUDED
+
+#include "../features/feature_set.h"
+#include "../features/half_kp_shogi.h"
+
+#include "../layers/input_slice.h"
+#include "../layers/affine_transform.h"
+#include "../layers/clipped_relu.h"
+
+namespace Eval::NNUE::Features {
+// Alias for compatibility with upstream code
+template <Side AssociatedKing>
+using HalfKP = HalfKPShogi<AssociatedKing>;
+}
+
+namespace Eval::NNUE {
+
+// Input features used in evaluation function
+using RawFeatures = Features::FeatureSet<
+    Features::HalfKPShogi<Features::Side::kFriend>>;
+
+// Number of input feature dimensions after conversion
+constexpr IndexType kTransformedFeatureDimensions = 256;
+
+namespace Layers {
+
+// Define network structure
+using InputLayer = InputSlice<kTransformedFeatureDimensions * 2>;
+using HiddenLayer1 = ClippedReLU<AffineTransform<InputLayer, 32>>;
+using HiddenLayer2 = ClippedReLU<AffineTransform<HiddenLayer1, 32>>;
+using OutputLayer = AffineTransform<HiddenLayer2, 1>;
+
+}  // namespace Layers
+
+using Network = Layers::OutputLayer;
+
+}  // namespace Eval::NNUE
+
+#endif // #ifndef NNUE_HALFKP_SHOGI_256X2_32_32_H_INCLUDED
diff --git a/src/nnue/architectures/halfkp_variants_256x2-32-32.h b/src/nnue/architectures/halfkp_variants_256x2-32-32.h
new file mode 100644 (file)
index 0000000..2116ea9
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+  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 <http://www.gnu.org/licenses/>.
+*/
+
+// Definition of input features and network structure used in NNUE evaluation function
+
+#ifndef NNUE_HALFKP_VARIANTS_256X2_32_32_H_INCLUDED
+#define NNUE_HALFKP_VARIANTS_256X2_32_32_H_INCLUDED
+
+#include "../features/feature_set.h"
+#include "../features/half_kp_variants.h"
+
+#include "../layers/input_slice.h"
+#include "../layers/affine_transform.h"
+#include "../layers/clipped_relu.h"
+
+namespace Eval::NNUE::Features {
+// Alias for compatibility with upstream code
+template <Side AssociatedKing>
+using HalfKP = HalfKPVariants<AssociatedKing>;
+}
+
+namespace Eval::NNUE {
+
+// Input features used in evaluation function
+using RawFeatures = Features::FeatureSet<
+    Features::HalfKPVariants<Features::Side::kFriend>>;
+
+// Number of input feature dimensions after conversion
+constexpr IndexType kTransformedFeatureDimensions = 256;
+
+namespace Layers {
+
+// Define network structure
+using InputLayer = InputSlice<kTransformedFeatureDimensions * 2>;
+using HiddenLayer1 = ClippedReLU<AffineTransform<InputLayer, 32>>;
+using HiddenLayer2 = ClippedReLU<AffineTransform<HiddenLayer1, 32>>;
+using OutputLayer = AffineTransform<HiddenLayer2, 1>;
+
+}  // namespace Layers
+
+using Network = Layers::OutputLayer;
+
+}  // namespace Eval::NNUE
+
+#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
index 508b024..085aa40 100644 (file)
 namespace Eval::NNUE::Features {
 
   // Map square to numbering on 8x8 board
-  constexpr Square map_to_standard_board(Square s) {
+  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) {
-    return map_to_standard_board(  perspective == WHITE || (pos.capture_the_flag(BLACK) & Rank8BB) ? s
-                                 : flip_rank(flip_file(s, pos.max_file()), pos.max_rank()));
+  inline Square 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(const Position& pos, Color perspective, Square s, Piece pc, Square ksq) {
-    return IndexType(orient(pos, perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq);
+  inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) {
+    return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq);
   }
 
   // Get a list of indices for active features
   template <Side AssociatedKing>
-  void HalfKP<AssociatedKing>::AppendActiveIndices(
+  void HalfKPChess<AssociatedKing>::AppendActiveIndices(
       const Position& pos, Color perspective, IndexList* active) {
 
-    Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king()));
-    Bitboard bb = pos.pieces() & ~pos.pieces(pos.nnue_king());
+    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(make_index(pos, perspective, s, pos.piece_on(s), ksq));
+      active->push_back(make_index(perspective, s, pos.piece_on(s), ksq));
     }
   }
 
   // Get a list of indices for recently changed features
   template <Side AssociatedKing>
-  void HalfKP<AssociatedKing>::AppendChangedIndices(
+  void HalfKPChess<AssociatedKing>::AppendChangedIndices(
       const Position& pos, const DirtyPiece& dp, Color perspective,
       IndexList* removed, IndexList* added) {
 
-    Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king()));
+    Square ksq = orient(perspective, pos.square<KING>(perspective));
     for (int i = 0; i < dp.dirty_num; ++i) {
       Piece pc = dp.piece[i];
-      if (type_of(pc) == pos.nnue_king()) continue;
+      if (type_of(pc) == 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, ksq));
       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, ksq));
     }
   }
 
-  template class HalfKP<Side::kFriend>;
+  template class HalfKPChess<Side::kFriend>;
 
 }  // namespace Eval::NNUE::Features
index 80f09a5..2dafcb7 100644 (file)
@@ -29,7 +29,7 @@ namespace Eval::NNUE::Features {
   // Feature HalfKP: Combination of the position of own king
   // and the position of pieces other than kings
   template <Side AssociatedKing>
-  class HalfKP {
+  class HalfKPChess {
 
    public:
     // Feature name
diff --git a/src/nnue/features/half_kp_shogi.cpp b/src/nnue/features/half_kp_shogi.cpp
new file mode 100644 (file)
index 0000000..8b3b1e3
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+  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 <http://www.gnu.org/licenses/>.
+*/
+
+//Definition of input features HalfKP of NNUE evaluation function
+
+#include "half_kp_shogi.h"
+#include "index_list.h"
+
+namespace 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) {
+    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) {
+    return IndexType(orient(perspective, s) + shogi_kpp_board_index[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) {
+    Color color = (c == perspective) ? WHITE : BLACK;
+    return IndexType(hand_index + shogi_kpp_hand_index[color][pt] + SHOGI_PS_END * ksq);
+  }
+
+  // Get a list of indices for active features
+  template <Side AssociatedKing>
+  void HalfKPShogi<AssociatedKing>::AppendActiveIndices(
+      const Position& pos, Color perspective, IndexList* active) {
+
+    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(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));
+  }
+
+  // Get a list of indices for recently changed features
+  template <Side AssociatedKing>
+  void HalfKPShogi<AssociatedKing>::AppendChangedIndices(
+      const Position& pos, const DirtyPiece& dp, Color perspective,
+      IndexList* removed, IndexList* added) {
+
+    Square ksq = orient(perspective, pos.square<KING>(perspective));
+    for (int i = 0; i < dp.dirty_num; ++i) {
+      Piece pc = dp.piece[i];
+      if (type_of(pc) == KING) continue;
+      if (dp.from[i] != SQ_NONE)
+        removed->push_back(make_index(perspective, dp.from[i], pc, 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));
+      }
+      if (dp.to[i] != SQ_NONE)
+        added->push_back(make_index(perspective, dp.to[i], pc, 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));
+      }
+    }
+  }
+
+  template class HalfKPShogi<Side::kFriend>;
+
+}  // namespace Eval::NNUE::Features
diff --git a/src/nnue/features/half_kp_shogi.h b/src/nnue/features/half_kp_shogi.h
new file mode 100644 (file)
index 0000000..1e56f85
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+  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 <http://www.gnu.org/licenses/>.
+*/
+
+//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 "../../evaluate.h"
+#include "features_common.h"
+
+namespace Eval::NNUE::Features {
+
+  // Feature HalfKP: Combination of the position of own king
+  // and the position of pieces other than kings
+  template <Side AssociatedKing>
+  class HalfKPShogi {
+
+   public:
+    // Feature name
+    static constexpr const char* kName = "HalfKP(Friend)";
+    // Hash value embedded in the evaluation file
+    static constexpr std::uint32_t kHashValue =
+        0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
+    // Number of feature dimensions
+    static constexpr IndexType kDimensions =
+        static_cast<IndexType>(SQUARE_NB_SHOGI) * static_cast<IndexType>(SHOGI_PS_END);
+    // Maximum number of simultaneously active features
+    static constexpr IndexType kMaxActiveDimensions = 38; // Kings don't count
+    // Trigger for full calculation instead of difference calculation
+    static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
+
+    // Get a list of indices for active features
+    static void AppendActiveIndices(const Position& pos, Color perspective,
+                                    IndexList* active);
+
+    // Get a list of indices for recently changed features
+    static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective,
+                                     IndexList* removed, IndexList* added);
+  };
+
+}  // namespace Eval::NNUE::Features
+
+#endif // #ifndef NNUE_FEATURES_HALF_KP_SHOGI_H_INCLUDED
diff --git a/src/nnue/features/half_kp_variants.cpp b/src/nnue/features/half_kp_variants.cpp
new file mode 100644 (file)
index 0000000..6167d14
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+  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 <http://www.gnu.org/licenses/>.
+*/
+
+//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
+
+namespace 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) {
+    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) + kpp_board_index[perspective][pc] + PS_END * ksq);
+  }
+
+  // Get a list of indices for active features
+  template <Side AssociatedKing>
+  void HalfKPVariants<AssociatedKing>::AppendActiveIndices(
+      const Position& pos, Color perspective, IndexList* active) {
+
+    // Re-route to shogi features
+#ifdef LARGEBOARDS
+    if (currentNnueFeatures == NNUE_SHOGI)
+    {
+        assert(HalfKPShogi<AssociatedKing>::kDimensions <= kDimensions);
+        return HalfKPShogi<AssociatedKing>::AppendActiveIndices(pos, perspective, active);
+    }
+#endif
+
+    Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king()));
+    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));
+    }
+  }
+
+  // Get a list of indices for recently changed features
+  template <Side AssociatedKing>
+  void HalfKPVariants<AssociatedKing>::AppendChangedIndices(
+      const Position& pos, const DirtyPiece& dp, Color perspective,
+      IndexList* removed, IndexList* added) {
+
+    // Re-route to shogi features
+#ifdef LARGEBOARDS
+    if (currentNnueFeatures == NNUE_SHOGI)
+    {
+        assert(HalfKPShogi<AssociatedKing>::kDimensions <= kDimensions);
+        return HalfKPShogi<AssociatedKing>::AppendChangedIndices(pos, dp, perspective, removed, added);
+    }
+#endif
+
+    Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king()));
+    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));
+      if (dp.to[i] != SQ_NONE)
+        added->push_back(make_index(pos, perspective, dp.to[i], pc, ksq));
+    }
+  }
+
+  template class HalfKPVariants<Side::kFriend>;
+
+}  // namespace Eval::NNUE::Features
diff --git a/src/nnue/features/half_kp_variants.h b/src/nnue/features/half_kp_variants.h
new file mode 100644 (file)
index 0000000..92517db
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+  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 <http://www.gnu.org/licenses/>.
+*/
+
+//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 "../../evaluate.h"
+#include "features_common.h"
+
+namespace Eval::NNUE::Features {
+
+  // Feature HalfKP: Combination of the position of own king
+  // and the position of pieces other than kings
+  template <Side AssociatedKing>
+  class HalfKPVariants {
+
+   public:
+    // Feature name
+    static constexpr const char* kName = "HalfKP(Friend)";
+    // Hash value embedded in the evaluation file
+    static constexpr std::uint32_t kHashValue =
+        0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
+    // Number of feature dimensions
+    static constexpr IndexType kDimensions =
+#ifdef LARGEBOARDS
+        static_cast<IndexType>(SQUARE_NB_SHOGI) * static_cast<IndexType>(SHOGI_PS_END);
+#else
+        static_cast<IndexType>(SQUARE_NB_CHESS) * static_cast<IndexType>(PS_END);
+#endif
+    // Maximum number of simultaneously active features
+    static constexpr IndexType kMaxActiveDimensions = 64; // Kings don't count
+    // Trigger for full calculation instead of difference calculation
+    static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
+
+    // Get a list of indices for active features
+    static void AppendActiveIndices(const Position& pos, Color perspective,
+                                    IndexList* active);
+
+    // Get a list of indices for recently changed features
+    static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective,
+                                     IndexList* removed, IndexList* added);
+  };
+
+}  // namespace Eval::NNUE::Features
+
+#endif // #ifndef NNUE_FEATURES_HALF_KP_VARIANTS_H_INCLUDED
index ad5be00..fba1d56 100644 (file)
@@ -22,7 +22,9 @@
 #define NNUE_ARCHITECTURE_H_INCLUDED
 
 // Defines the network structure
-#include "architectures/halfkp_256x2-32-32.h"
+// #include "architectures/halfkp_256x2-32-32.h"
+// #include "architectures/halfkp_shogi_256x2-32-32.h"
+#include "architectures/halfkp_variants_256x2-32-32.h"
 
 namespace Eval::NNUE {
 
index a53004e..f4b87af 100644 (file)
@@ -90,6 +90,117 @@ namespace Eval::NNUE {
     PS_END2     = 12 * SQUARE_NB_CHESS + 1
   };
 
+  enum {
+    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
+  };
+
+  constexpr uint32_t shogi_kpp_board_index[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, PS_NONE, 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,
+      SHOGI_PS_W_KING, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
+      PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, 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_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, PS_NONE, 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,
+      SHOGI_PS_B_KING, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
+      PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, 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_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, PS_NONE, 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,
+      SHOGI_PS_B_KING, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
+      PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, 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_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, PS_NONE, 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,
+      SHOGI_PS_W_KING, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
+      PS_NONE, PS_NONE, PS_NONE, PS_NONE, 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(shogi_kpp_board_index[WHITE][make_piece(WHITE, SHOGI_PAWN)] == SHOGI_PS_W_PAWN);
+  static_assert(shogi_kpp_board_index[WHITE][make_piece(WHITE, KING)] == SHOGI_PS_W_KING);
+  static_assert(shogi_kpp_board_index[WHITE][make_piece(BLACK, SHOGI_PAWN)] == SHOGI_PS_B_PAWN);
+  static_assert(shogi_kpp_board_index[WHITE][make_piece(BLACK, KING)] == SHOGI_PS_B_KING);
+
+  constexpr uint32_t shogi_kpp_hand_index[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, PS_NONE, 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, 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, PS_NONE, 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 }
+  };
+  static_assert(shogi_kpp_hand_index[WHITE][SHOGI_PAWN] == SHOGI_HAND_W_PAWN);
+  static_assert(shogi_kpp_hand_index[WHITE][GOLD] == SHOGI_HAND_W_GOLD);
+  static_assert(shogi_kpp_hand_index[BLACK][SHOGI_PAWN] == SHOGI_HAND_B_PAWN);
+  static_assert(shogi_kpp_hand_index[BLACK][GOLD] == SHOGI_HAND_B_GOLD);
+
   constexpr uint32_t kpp_board_index[COLOR_NB][PIECE_NB] = {
     // convention: W - us, B - them
     // viewed from other side, W and B are reversed
index 38395c0..e0c5eab 100644 (file)
@@ -114,7 +114,9 @@ namespace Eval::NNUE {
 
       for (std::size_t i = 0; i < kHalfDimensions; ++i)
         biases_[i] = read_little_endian<BiasType>(stream);
-      for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i)
+      for (std::size_t i = 0; i < kHalfDimensions * (  currentNnueFeatures == NNUE_SHOGI ? SQUARE_NB_SHOGI * SHOGI_PS_END
+                                                     : currentNnueFeatures == NNUE_CHESS ? SQUARE_NB_CHESS * PS_END
+                                                                                         : SQUARE_NB_CHESS * PS_END); ++i)
         weights_[i] = read_little_endian<WeightType>(stream);
       return !stream.fail();
     }
@@ -248,6 +250,7 @@ namespace Eval::NNUE {
       // of the estimated gain in terms of features to be added/subtracted.
       StateInfo *st = pos.state(), *next = nullptr;
       int gain = pos.count<ALL_PIECES>() - 2;
+
       while (st->accumulator.state[c] == EMPTY)
       {
         auto& dp = st->dirtyPiece;
index ce812a4..9f4bb90 100644 (file)
@@ -1395,7 +1395,12 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
           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;
       }
+      else
+          dp.handPiece[1] = NO_PIECE;
 
       // Update material hash key and prefetch access to materialTable
       k ^= Zobrist::psq[captured][capsq];
@@ -1479,6 +1484,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
       {
           // Add drop piece
           dp.piece[0] = pc;
+          dp.handPiece[0] = make_piece(us, in_hand_piece_type(m));
           dp.from[0] = SQ_NONE;
           dp.to[0] = to;
       }
@@ -1546,6 +1552,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
               // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
               dp.to[0] = SQ_NONE;
               dp.piece[dp.dirty_num] = promotion;
+              dp.handPiece[dp.dirty_num] = NO_PIECE;
               dp.from[dp.dirty_num] = SQ_NONE;
               dp.to[dp.dirty_num] = to;
               dp.dirty_num++;
@@ -1579,6 +1586,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
           // Promoting piece to SQ_NONE, promoted piece from SQ_NONE
           dp.to[0] = SQ_NONE;
           dp.piece[dp.dirty_num] = promotion;
+          dp.handPiece[dp.dirty_num] = NO_PIECE;
           dp.from[dp.dirty_num] = SQ_NONE;
           dp.to[dp.dirty_num] = to;
           dp.dirty_num++;
@@ -1604,6 +1612,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
           // Demoting piece to SQ_NONE, demoted piece from SQ_NONE
           dp.to[0] = SQ_NONE;
           dp.piece[dp.dirty_num] = demotion;
+          dp.handPiece[dp.dirty_num] = NO_PIECE;
           dp.from[dp.dirty_num] = SQ_NONE;
           dp.to[dp.dirty_num] = to;
           dp.dirty_num++;
@@ -1634,6 +1643,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
       {
           // Add gating piece
           dp.piece[dp.dirty_num] = gating_piece;
+          dp.handPiece[dp.dirty_num] = gating_piece;
           dp.from[dp.dirty_num] = SQ_NONE;
           dp.to[dp.dirty_num] = gate;
           dp.dirty_num++;
@@ -1675,6 +1685,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
           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++;
@@ -1701,6 +1712,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
               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;
           }
 
           // Update material hash key
index 04ff04c..5f501bc 100644 (file)
@@ -518,7 +518,8 @@ enum Square : int {
   SQUARE_BIT_MASK = 63,
 #endif
   SQ_MAX = SQUARE_NB - 1,
-  SQUARE_NB_CHESS = 64
+  SQUARE_NB_CHESS = 64,
+  SQUARE_NB_SHOGI = 81,
 };
 
 enum Direction : int {
@@ -567,12 +568,19 @@ struct DirtyPiece {
   // both the pawn and the captured piece to SQ_NONE and the piece promoted
   // to from SQ_NONE to the capture square.
   Piece piece[12];
+  Piece handPiece[12];
 
   // From and to squares, which may be SQ_NONE
   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).
 /// The least significant 16 bits are used to store the middlegame value and the
 /// upper 16 bits are used to store the endgame value. We have to take care to
index 9d2e52e..be41e42 100644 (file)
@@ -40,6 +40,7 @@ namespace {
     Variant* chess_variant() {
         Variant* v = fairy_variant_base();
         v->endgameEval = true;
+        v->nnueFeatures = NNUE_CHESS;
         return v;
     }
     Variant* chess960_variant() {
@@ -709,6 +710,7 @@ namespace {
         v->promotionRank = RANK_7;
         v->promotedPieceType[LANCE]        = GOLD;
         v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
+        v->nnueFeatures = NNUE_SHOGI;
         return v;
     }
     Variant* capablanca_variant() {
index 0dd36a3..5200a3f 100644 (file)
@@ -128,6 +128,8 @@ struct Variant {
   MaterialCounting materialCounting = NO_MATERIAL_COUNTING;
   CountingRule countingRule = NO_COUNTING;
 
+  NnueFeatures nnueFeatures = NNUE_VARIANT;
+
   // Derived properties
   bool fastAttacks = true;
   bool fastAttacks2 = true;