Generalize NNUE architecture
authorFabian Fichter <ianfab@users.noreply.github.com>
Fri, 30 Jul 2021 13:18:37 +0000 (15:18 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 21 Aug 2021 10:43:15 +0000 (12:43 +0200)
Remove restrictions on board size and piece types
in order to enable NNUE for most supported variants.

This change modifies the indexing for fairy pieces,
therefore existing networks for fairy variants, currently only Makruk,
will not work properly any more and need to be replaced.

Limitations that are not addressed as part of this are:
- A "king" piece type with a constant count of 1 is required.
- Pockets are not considered.

13 files changed:
src/Makefile
src/Makefile_js
src/evaluate.cpp
src/evaluate.h
src/nnue/evaluate_nnue.cpp
src/nnue/features/half_ka_v2.h
src/nnue/features/half_ka_v2_shogi.cpp [deleted file]
src/nnue/features/half_ka_v2_shogi.h [deleted file]
src/nnue/features/half_ka_v2_variants.cpp
src/nnue/features/half_ka_v2_variants.h
src/types.h
src/variant.cpp
src/variant.h

index 7e3f0cd..6fd7134 100644 (file)
@@ -43,7 +43,7 @@ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.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_ka_v2.cpp \
        partner.cpp parser.cpp piece.cpp variant.cpp xboard.cpp \
-       nnue/features/half_ka_v2_shogi.cpp nnue/features/half_ka_v2_variants.cpp
+       nnue/features/half_ka_v2_variants.cpp
 
 OBJS = $(notdir $(SRCS:.cpp=.o))
 
index fdc4b15..6e9c791 100644 (file)
@@ -21,7 +21,7 @@ SRCS = ffishjs.cpp benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.c
        search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
        nnue/evaluate_nnue.cpp nnue/features/half_ka_v2.cpp \
        partner.cpp parser.cpp piece.cpp variant.cpp xboard.cpp \
-       nnue/features/half_ka_v2_shogi.cpp nnue/features/half_ka_v2_variants.cpp
+       nnue/features/half_ka_v2_variants.cpp
 
 CXX=emcc
 CXXFLAGS += --bind -DNNUE_EMBEDDING_OFF -DNO_THREADS -std=c++17 -Wall
index b59df81..2475fa9 100644 (file)
@@ -59,7 +59,7 @@ using namespace std;
 
 namespace Stockfish {
 
-NnueFeatures currentNnueFeatures;
+const Variant* currentNnueVariant;
 
 namespace Eval {
 
@@ -104,7 +104,7 @@ namespace Eval {
     if (!useNNUE)
         return;
 
-    currentNnueFeatures = variants.find(variant)->second->nnueFeatures;
+    currentNnueVariant = variants.find(variant)->second;
 
     #if defined(DEFAULT_NNUE_DIRECTORY)
     #define stringify2(x) #x
index c8395df..4ee009b 100644 (file)
@@ -24,6 +24,8 @@
 
 #include "types.h"
 
+#include "variant.h"
+
 namespace Stockfish {
 
 class Position;
@@ -57,7 +59,7 @@ namespace Eval {
 
 } // namespace Eval
 
-extern NnueFeatures currentNnueFeatures;
+extern const Variant* currentNnueVariant;
 
 } // namespace Stockfish
 
index 94e7e5d..69471ed 100644 (file)
@@ -160,7 +160,7 @@ namespace Stockfish::Eval::NNUE {
     ASSERT_ALIGNED(transformedFeatures, alignment);
     ASSERT_ALIGNED(buffer, alignment);
 
-    const std::size_t bucket = (pos.count<ALL_PIECES>() - 1) / 4;
+    const std::size_t bucket = std::min((pos.count<ALL_PIECES>() - 1) * 8 / currentNnueVariant->nnueMaxPieces, 7);
     const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
     const auto output = network[bucket]->propagate(transformedFeatures, buffer);
 
@@ -210,7 +210,7 @@ namespace Stockfish::Eval::NNUE {
     ASSERT_ALIGNED(buffer, alignment);
 
     NnueEvalTrace t{};
-    t.correctBucket = (pos.count<ALL_PIECES>() - 1) / 4;
+    t.correctBucket = std::min((pos.count<ALL_PIECES>() - 1) * 8 / currentNnueVariant->nnueMaxPieces, 7);
     for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) {
       const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
       const auto output = network[bucket]->propagate(transformedFeatures, buffer);
@@ -301,13 +301,13 @@ namespace Stockfish::Eval::NNUE {
     char board[3*RANK_NB+1][8*FILE_NB+2];
     std::memset(board, ' ', sizeof(board));
     for (int row = 0; row < 3*pos.ranks()+1; ++row)
-      board[row][pos.ranks()*pos.files()+1] = '\0';
+      board[row][8*FILE_NB+1] = '\0';
 
     // A lambda to output one box of the board
     auto writeSquare = [&board, &pos](File file, Rank rank, Piece pc, Value value) {
 
       const int x = ((int)file) * 8;
-      const int y = (7 - (int)rank) * 3;
+      const int y = (pos.max_rank() - (int)rank) * 3;
       for (int i = 1; i < 8; ++i)
          board[y][x+i] = board[y+3][x+i] = '-';
       for (int i = 1; i < 3; ++i)
index ddee206..49161ff 100644 (file)
@@ -66,7 +66,7 @@ namespace Stockfish::Eval::NNUE::Features {
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_KING,
 
-        PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_QUEEN, PS_B_BISHOP,
+        PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE,
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
@@ -77,7 +77,7 @@ namespace Stockfish::Eval::NNUE::Features {
       },
 
       {
-        PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_QUEEN, PS_B_BISHOP,
+        PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE,
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
         PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE, PS_NONE,
diff --git a/src/nnue/features/half_ka_v2_shogi.cpp b/src/nnue/features/half_ka_v2_shogi.cpp
deleted file mode 100644 (file)
index 6c58d87..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
-  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 HalfKAv2 of NNUE evaluation function
-
-#include "half_ka_v2_shogi.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 HalfKAv2Shogi::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 HalfKAv2Shogi::make_index(Color perspective, Square s, Piece pc, Square ksq) {
-    return IndexType(orient(perspective, s) + PieceSquareIndexShogi[perspective][pc] + SHOGI_PS_NB * ksq);
-  }
-
-  // Index of a feature for a given king position and hand piece
-  inline IndexType HalfKAv2Shogi::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_NB * ksq);
-  }
-
-  // Get a list of indices for active features
-  void HalfKAv2Shogi::append_active_indices(
-    const Position& pos,
-    Color perspective,
-    ValueListInserter<IndexType> active
-  ) {
-    Square ksq = orient(perspective, pos.square<KING>(perspective));
-    Bitboard bb = pos.pieces();
-    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));
-  }
-
-  // append_changed_indices() : get a list of indices for recently changed features
-
-  void HalfKAv2Shogi::append_changed_indices(
-    Square ksq,
-    StateInfo* st,
-    Color perspective,
-    ValueListInserter<IndexType> removed,
-    ValueListInserter<IndexType> added
-  ) {
-    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 (dp.from[i] != SQ_NONE)
-        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), dp.handCount[i], type_of(handPc), oriented_ksq));
-      }
-      if (dp.to[i] != SQ_NONE)
-        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), dp.handCount[i] - 1, type_of(handPc), oriented_ksq));
-      }
-    }
-  }
-
-  int HalfKAv2Shogi::update_cost(StateInfo* st) {
-    return st->dirtyPiece.dirty_num;
-  }
-
-  int HalfKAv2Shogi::refresh_cost(const Position& pos) {
-    return pos.count<ALL_PIECES>();
-  }
-
-  bool HalfKAv2Shogi::requires_refresh(StateInfo* st, Color perspective) {
-    return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
-  }
-
-
-}  // namespace Stockfish::Eval::NNUE::Features
diff --git a/src/nnue/features/half_ka_v2_shogi.h b/src/nnue/features/half_ka_v2_shogi.h
deleted file mode 100644 (file)
index 17f4c59..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
-  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 HalfKAv2 of NNUE evaluation function
-
-#ifndef NNUE_FEATURES_HALF_KA_V2_SHOGI_H_INCLUDED
-#define NNUE_FEATURES_HALF_KA_V2_SHOGI_H_INCLUDED
-
-#include "../nnue_common.h"
-
-#include "../../evaluate.h"
-#include "../../misc.h"
-
-namespace Stockfish {
-  struct StateInfo;
-}
-
-namespace Stockfish::Eval::NNUE::Features {
-
-  // Feature HalfKAv2: Combination of the position of own king
-  // and the position of pieces
-  class HalfKAv2Shogi {
-
-    // unique number for each piece type on each square
-    enum {
-      PS_NONE             =   0,
-      SHOGI_HAND_W_PAWN   =   0,
-      SHOGI_HAND_B_PAWN   =  19,
-      SHOGI_HAND_W_LANCE  =  38,
-      SHOGI_HAND_B_LANCE  =  43,
-      SHOGI_HAND_W_KNIGHT =  48,
-      SHOGI_HAND_B_KNIGHT =  53,
-      SHOGI_HAND_W_SILVER =  58,
-      SHOGI_HAND_B_SILVER =  63,
-      SHOGI_HAND_W_GOLD   =  68,
-      SHOGI_HAND_B_GOLD   =  73,
-      SHOGI_HAND_W_BISHOP =  78,
-      SHOGI_HAND_B_BISHOP =  81,
-      SHOGI_HAND_W_ROOK   =  84,
-      SHOGI_HAND_B_ROOK   =  87,
-      SHOGI_HAND_END      =  89,
-
-      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_KING       = 18 * SQUARE_NB_SHOGI + SHOGI_HAND_END,
-      SHOGI_PS_NB         = 19 * 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_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_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_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_KING
-      }
-    };
-    static_assert(PieceSquareIndexShogi[WHITE][make_piece(WHITE, SHOGI_PAWN)] == SHOGI_PS_W_PAWN);
-    static_assert(PieceSquareIndexShogi[WHITE][make_piece(WHITE, KING)] == SHOGI_PS_KING);
-    static_assert(PieceSquareIndexShogi[WHITE][make_piece(BLACK, SHOGI_PAWN)] == SHOGI_PS_B_PAWN);
-    static_assert(PieceSquareIndexShogi[WHITE][make_piece(BLACK, KING)] == SHOGI_PS_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 = "HalfKAv2(Friend)";
-
-    // Hash value embedded in the evaluation file
-    static constexpr std::uint32_t HashValue = 0x5f234cb8u;
-
-    // Number of feature dimensions
-    static constexpr IndexType Dimensions =
-        static_cast<IndexType>(SQUARE_NB_SHOGI) * static_cast<IndexType>(SHOGI_PS_NB);
-
-    // Maximum number of simultaneously active features.
-    static constexpr IndexType MaxActiveDimensions = 40;
-
-    // Get a list of indices for active features
-    static void append_active_indices(
-      const Position& pos,
-      Color perspective,
-      ValueListInserter<IndexType> active);
-
-    // Get a list of indices for recently changed features
-    static void append_changed_indices(
-      Square ksq,
-      StateInfo* st,
-      Color perspective,
-      ValueListInserter<IndexType> removed,
-      ValueListInserter<IndexType> added);
-
-    // 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);
-  };
-
-}  // namespace Stockfish::Eval::NNUE::Features
-
-#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_SHOGI_H_INCLUDED
index fc07923..d351209 100644 (file)
 
 #include "half_ka_v2_variants.h"
 
-#ifdef LARGEBOARDS
-#include "half_ka_v2_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));
+  // Map square to numbering on variant board
+  inline Square to_variant_square(Square s, const Position& pos) {
+    return Square(s - rank_of(s) * (FILE_MAX - pos.max_file()));
   }
 
   // Orient a square according to perspective (rotates by 180 for black)
   inline Square HalfKAv2Variants::orient(Color perspective, Square s, const Position& pos) {
-    return to_chess_square(  perspective == WHITE || (pos.capture_the_flag(BLACK) & Rank8BB) ? s
-                           : flip_rank(s, pos.max_rank()));
+    return to_variant_square(  perspective == WHITE || (pos.capture_the_flag(BLACK) & Rank8BB) ? s
+                             : flip_rank(s, pos.max_rank()), pos);
   }
 
   // Index of a feature for a given king position and another piece on some square
   inline IndexType HalfKAv2Variants::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);
+    return IndexType(orient(perspective, s, pos) + pos.variant()->pieceSquareIndex[perspective][pc] + ksq * pos.variant()->nnuePieceIndices);
   }
 
   // Get a list of indices for active features
@@ -50,15 +46,6 @@ namespace Stockfish::Eval::NNUE::Features {
     Color perspective,
     ValueListInserter<IndexType> active
   ) {
-    // Re-route to shogi features
-#ifdef LARGEBOARDS
-    if (currentNnueFeatures == NNUE_SHOGI)
-    {
-        assert(HalfKAv2Shogi::Dimensions <= Dimensions);
-        return HalfKAv2Shogi::append_active_indices(pos, perspective, active);
-    }
-#endif
-
     Square oriented_ksq = orient(perspective, pos.square(perspective, pos.nnue_king()), pos);
     Bitboard bb = pos.pieces();
     while (bb)
@@ -78,14 +65,6 @@ namespace Stockfish::Eval::NNUE::Features {
     ValueListInserter<IndexType> added,
     const Position& pos
   ) {
-    // Re-route to shogi features
-#ifdef LARGEBOARDS
-    if (currentNnueFeatures == NNUE_SHOGI)
-    {
-        assert(HalfKAv2Shogi::Dimensions <= Dimensions);
-        return HalfKAv2Shogi::append_changed_indices(ksq, st, perspective, removed, added);
-    }
-#endif
     const auto& dp = st->dirtyPiece;
     Square oriented_ksq = orient(perspective, ksq, pos);
     for (int i = 0; i < dp.dirty_num; ++i) {
index 55c4e98..b46a004 100644 (file)
@@ -26,7 +26,6 @@
 #include "../../evaluate.h"
 #include "../../misc.h"
 
-#include "half_ka_v2_shogi.h"
 #include "half_ka_v2.h"
 
 namespace Stockfish {
@@ -39,72 +38,6 @@ namespace Stockfish::Eval::NNUE::Features {
   // and the position of pieces
   class HalfKAv2Variants {
 
-    // unique number for each piece type on each square
-    enum {
-      PS_NONE     =  0,
-      PS_W_PAWN   =  0,
-      PS_B_PAWN   =  1 * SQUARE_NB_CHESS,
-      PS_W_KNIGHT =  2 * SQUARE_NB_CHESS,
-      PS_B_KNIGHT =  3 * SQUARE_NB_CHESS,
-      PS_W_BISHOP =  4 * SQUARE_NB_CHESS,
-      PS_B_BISHOP =  5 * SQUARE_NB_CHESS,
-      PS_W_ROOK   =  6 * SQUARE_NB_CHESS,
-      PS_B_ROOK   =  7 * SQUARE_NB_CHESS,
-      PS_W_QUEEN  =  8 * SQUARE_NB_CHESS,
-      PS_B_QUEEN  =  9 * SQUARE_NB_CHESS,
-      PS_KING     =  10 * SQUARE_NB_CHESS,
-      PS_NB = 11 * SQUARE_NB_CHESS
-    };
-
-    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_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, PS_NONE, PS_KING,
-
-        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_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, PS_NONE, PS_KING,
-      },
-
-      {
-        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_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, PS_NONE, PS_KING,
-
-        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_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, PS_NONE, PS_KING,
-      }
-    };
-    // 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_KING);
-    static_assert(PieceSquareIndex[WHITE][make_piece(BLACK, PAWN)] == PS_B_PAWN);
-    static_assert(PieceSquareIndex[WHITE][make_piece(BLACK, KING)] == PS_KING);
-
     // Orient a square according to perspective (rotates by 180 for black)
     static Square orient(Color perspective, Square s, const Position& pos);
 
@@ -119,17 +52,10 @@ namespace Stockfish::Eval::NNUE::Features {
     static constexpr std::uint32_t HashValue = 0x5f234cb8u;
 
     // Number of feature dimensions
-    static constexpr IndexType Dimensions =
-#ifdef LARGEBOARDS
-        HalfKAv2Shogi::Dimensions;
-#else
-        HalfKAv2::Dimensions;
-#endif
+    static constexpr IndexType Dimensions = static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(SQUARE_NB) * 19;
 
     static IndexType get_dimensions() {
-      return  currentNnueFeatures == NNUE_SHOGI ? HalfKAv2Shogi::Dimensions
-            : currentNnueFeatures == NNUE_CHESS ? HalfKAv2::Dimensions
-                                                : SQUARE_NB_CHESS * PS_NB;
+      return currentNnueVariant->nnueSquares * currentNnueVariant->nnuePieceIndices;
     }
 
     // Maximum number of simultaneously active features.
index 636cdba..c82e76f 100644 (file)
@@ -543,12 +543,6 @@ struct DirtyPiece {
   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 4f8bfc4..2d410c8 100644 (file)
@@ -47,7 +47,6 @@ namespace {
     // https://en.wikipedia.org/wiki/Chess
     Variant* chess_variant() {
         Variant* v = chess_variant_base();
-        v->nnueFeatures = NNUE_CHESS;
         return v;
     }
     // Chess960 aka Fischer random chess
@@ -901,7 +900,6 @@ namespace {
         v->promotionRank = RANK_7;
         v->promotedPieceType[LANCE]        = GOLD;
         v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
-        v->nnueFeatures = NNUE_SHOGI;
         return v;
     }
     // Sho-Shogi
index 9bb4aa0..a11e6c1 100644 (file)
@@ -24,6 +24,7 @@
 #include <vector>
 #include <string>
 #include <functional>
+#include <sstream>
 
 #include "types.h"
 #include "bitboard.h"
@@ -132,12 +133,14 @@ struct Variant {
   MaterialCounting materialCounting = NO_MATERIAL_COUNTING;
   CountingRule countingRule = NO_COUNTING;
 
-  NnueFeatures nnueFeatures = NNUE_VARIANT;
-
   // Derived properties
   bool fastAttacks = true;
   bool fastAttacks2 = true;
   PieceType nnueKing = KING;
+  int nnueSquares;
+  int nnuePieceIndices;
+  int pieceSquareIndex[COLOR_NB][PIECE_NB];
+  int nnueMaxPieces;
   bool endgameEval = false;
 
   void add_piece(PieceType pt, char c, std::string betza = "", char c2 = ' ') {
@@ -190,9 +193,34 @@ struct Variant {
                                 })
                     && !cambodianMoves
                     && !diagonalLines;
+
+      // Initialize calculated NNUE properties
       nnueKing =  pieceTypes.find(KING) != pieceTypes.end() ? KING
                 : extinctionPieceTypes.find(COMMONER) != extinctionPieceTypes.end() ? COMMONER
                 : NO_PIECE_TYPE;
+      nnueSquares = (maxRank + 1) * (maxFile + 1);
+      nnuePieceIndices = (2 * pieceTypes.size() - 1) * nnueSquares;
+      int i = 0;
+      for (PieceType pt : pieceTypes)
+      {
+          for (Color c : { WHITE, BLACK})
+          {
+              pieceSquareIndex[c][make_piece(c, pt)] = 2 * i * nnueSquares;
+              pieceSquareIndex[c][make_piece(~c, pt)] = (2 * i + (pt != nnueKing)) * nnueSquares;
+          }
+          i++;
+      }
+      // Determine maximum piece count
+      std::istringstream ss(startFen);
+      ss >> std::noskipws;
+      unsigned char token;
+      nnueMaxPieces = 0;
+      while ((ss >> token) && !isspace(token))
+      {
+          if (pieceToChar.find(token) != std::string::npos || pieceToCharSynonyms.find(token) != std::string::npos)
+              nnueMaxPieces++;
+      }
+
       // For endgame evaluation to be applicable, no special win rules must apply.
       // Furthermore, rules significantly changing game mechanics also invalidate it.
       endgameEval = std::none_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {