Support configurable pieces
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 17 Apr 2021 17:01:20 +0000 (19:01 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 17 Apr 2021 20:32:17 +0000 (22:32 +0200)
* Add >20 slots for user defined pieces
* Parse piece movement from Betza notation
* Precalculate piece values but allow overrides
* Support Tencubed, Yari shogi, and Okisaki shogi

Closes #58 and #146.

bench: 4039197

19 files changed:
README.md
src/apiutil.h
src/bitboard.cpp
src/bitboard.h
src/nnue/nnue_common.h
src/parser.cpp
src/piece.cpp
src/piece.h
src/position.cpp
src/position.h
src/psqt.cpp
src/types.h
src/ucioption.cpp
src/variant.cpp
src/variant.h
src/variants.ini
tests/js/README.md
tests/js/test.js
tests/perft.sh

index cb3c1a3..26940c1 100644 (file)
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ The games currently supported besides chess are listed below. Fairy-Stockfish ca
 
 ### Chess variants
 - [Capablanca](https://en.wikipedia.org/wiki/Capablanca_Chess), [Janus](https://en.wikipedia.org/wiki/Janus_Chess), [Modern](https://en.wikipedia.org/wiki/Modern_Chess_(chess_variant)), [Chancellor](https://en.wikipedia.org/wiki/Chancellor_Chess), [Embassy](https://en.wikipedia.org/wiki/Embassy_Chess), [Gothic](https://www.chessvariants.com/large.dir/gothicchess.html), [Capablanca random chess](https://en.wikipedia.org/wiki/Capablanca_Random_Chess)
-- [Grand](https://en.wikipedia.org/wiki/Grand_Chess), [Shako](https://www.chessvariants.com/large.dir/shako.html), [Centaur](https://www.chessvariants.com/large.dir/contest/royalcourt.html)
+- [Grand](https://en.wikipedia.org/wiki/Grand_Chess), [Shako](https://www.chessvariants.com/large.dir/shako.html), [Centaur](https://www.chessvariants.com/large.dir/contest/royalcourt.html), [Tencubed](https://www.chessvariants.com/contests/10/tencubedchess.html)
 - [Chess960](https://en.wikipedia.org/wiki/Chess960), [Placement/Pre-Chess](https://www.chessvariants.com/link/placement-chess)
 - [Crazyhouse](https://en.wikipedia.org/wiki/Crazyhouse), [Loop](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Chessgi](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Pocket Knight](http://www.chessvariants.com/other.dir/pocket.html), Capablanca-Crazyhouse
 - [Bughouse](https://en.wikipedia.org/wiki/Bughouse_chess), [Koedem](http://schachclub-oetigheim.de/wp-content/uploads/2016/04/Koedem-rules.pdf)
@@ -58,6 +58,8 @@ The games currently supported besides chess are listed below. Fairy-Stockfish ca
 - [Minishogi](https://en.wikipedia.org/wiki/Minishogi), [EuroShogi](https://en.wikipedia.org/wiki/EuroShogi), [Judkins shogi](https://en.wikipedia.org/wiki/Judkins_shogi)
 - [Kyoto shogi](https://en.wikipedia.org/wiki/Kyoto_shogi), [Microshogi](https://en.wikipedia.org/wiki/Micro_shogi)
 - [Dobutsu shogi](https://en.wikipedia.org/wiki/Dōbutsu_shōgi), [Goro goro shogi](https://en.wikipedia.org/wiki/D%C5%8Dbutsu_sh%C5%8Dgi#Variation)
+- [Yari shogi](https://en.wikipedia.org/wiki/Yari_shogi)
+- [Okisaki shogi](https://en.wikipedia.org/wiki/Okisaki_shogi)
 
 ### Related games
 - [Amazons](https://en.wikipedia.org/wiki/Game_of_the_Amazons)
index 4472523..7ddcc17 100644 (file)
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
+
+#ifndef APIUTIL_H_INCLUDED
+#define APIUTIL_H_INCLUDED
+
 #include <vector>
 #include <string>
 #include <sstream>
@@ -23,6 +27,7 @@
 #include <math.h>
 
 #include "types.h"
+#include "position.h"
 #include "variant.h"
 
 
@@ -41,7 +46,7 @@ enum Notation {
     NOTATION_XIANGQI_WXF,
 };
 
-Notation default_notation(const Variant* v) {
+inline Notation default_notation(const Variant* v) {
     if (v->variantTemplate == "shogi")
         return NOTATION_SHOGI_HODGES_NUMBER;
     return NOTATION_SAN;
@@ -56,11 +61,11 @@ enum Disambiguation {
     SQUARE_DISAMBIGUATION,
 };
 
-bool is_shogi(Notation n) {
+inline bool is_shogi(Notation n) {
     return n == NOTATION_SHOGI_HOSKING || n == NOTATION_SHOGI_HODGES || n == NOTATION_SHOGI_HODGES_NUMBER;
 }
 
-std::string piece(const Position& pos, Move m, Notation n) {
+inline std::string piece(const Position& pos, Move m, Notation n) {
     Color us = pos.side_to_move();
     Square from = from_sq(m);
     Piece pc = pos.moved_piece(m);
@@ -83,7 +88,7 @@ std::string piece(const Position& pos, Move m, Notation n) {
         return std::string(1, toupper(pos.piece_to_char()[pc]));
 }
 
-std::string file(const Position& pos, Square s, Notation n) {
+inline std::string file(const Position& pos, Square s, Notation n) {
     switch (n)
     {
     case NOTATION_SHOGI_HOSKING:
@@ -99,7 +104,7 @@ std::string file(const Position& pos, Square s, Notation n) {
     }
 }
 
-std::string rank(const Position& pos, Square s, Notation n) {
+inline std::string rank(const Position& pos, Square s, Notation n) {
     switch (n)
     {
     case NOTATION_SHOGI_HOSKING:
@@ -123,7 +128,7 @@ std::string rank(const Position& pos, Square s, Notation n) {
     }
 }
 
-std::string square(const Position& pos, Square s, Notation n) {
+inline std::string square(const Position& pos, Square s, Notation n) {
     switch (n)
     {
     case NOTATION_JANGGI:
@@ -133,7 +138,7 @@ std::string square(const Position& pos, Square s, Notation n) {
     }
 }
 
-Disambiguation disambiguation_level(const Position& pos, Move m, Notation n) {
+inline Disambiguation disambiguation_level(const Position& pos, Move m, Notation n) {
     // Drops never need disambiguation
     if (type_of(m) == DROP)
         return NO_DISAMBIGUATION;
@@ -197,7 +202,7 @@ Disambiguation disambiguation_level(const Position& pos, Move m, Notation n) {
         return SQUARE_DISAMBIGUATION;
 }
 
-std::string disambiguation(const Position& pos, Square s, Notation n, Disambiguation d) {
+inline std::string disambiguation(const Position& pos, Square s, Notation n, Disambiguation d) {
     switch (d)
     {
     case FILE_DISAMBIGUATION:
@@ -212,7 +217,7 @@ std::string disambiguation(const Position& pos, Square s, Notation n, Disambigua
     }
 }
 
-const std::string move_to_san(Position& pos, Move m, Notation n) {
+inline const std::string move_to_san(Position& pos, Move m, Notation n) {
     std::string san = "";
     Color us = pos.side_to_move();
     Square from = from_sq(m);
@@ -287,7 +292,7 @@ const std::string move_to_san(Position& pos, Move m, Notation n) {
 
 } // namespace SAN
 
-bool has_insufficient_material(Color c, const Position& pos) {
+inline bool has_insufficient_material(Color c, const Position& pos) {
 
     // Other win rules
     if (   pos.captures_to_hand()
@@ -350,18 +355,18 @@ struct CharSquare {
     int rowIdx;
     int fileIdx;
     CharSquare() : rowIdx(-1), fileIdx(-1) {}
-    CharSquare(int rowIdx, int fileIdx) : rowIdx(rowIdx), fileIdx(fileIdx) {}
+    CharSquare(int row, int file) : rowIdx(row), fileIdx(file) {}
 };
 
-bool operator==(const CharSquare& s1, const CharSquare& s2) {
+inline bool operator==(const CharSquare& s1, const CharSquare& s2) {
     return s1.rowIdx == s2.rowIdx && s1.fileIdx == s2.fileIdx;
 }
 
-bool operator!=(const CharSquare& s1, const CharSquare& s2) {
+inline bool operator!=(const CharSquare& s1, const CharSquare& s2) {
     return !(s1 == s2);
 }
 
-int non_root_euclidian_distance(const CharSquare& s1, const CharSquare& s2) {
+inline int non_root_euclidian_distance(const CharSquare& s1, const CharSquare& s2) {
     return pow(s1.rowIdx - s2.rowIdx, 2) + pow(s1.fileIdx - s2.fileIdx, 2);
 }
 
@@ -371,7 +376,7 @@ private:
     int nbFiles;
     std::vector<char> board;  // fill an array where the pieces are for later geometry checks
 public:
-    CharBoard(int nbRanks, int nbFiles) : nbRanks(nbRanks), nbFiles(nbFiles) {
+    CharBoard(int ranks, int files) : nbRanks(ranks), nbFiles(files) {
         assert(nbFiles > 0 && nbRanks > 0);
         board = std::vector<char>(nbRanks * nbFiles, ' ');
     }
@@ -423,7 +428,7 @@ public:
     friend std::ostream& operator<<(std::ostream& os, const CharBoard& board);
 };
 
-std::ostream& operator<<(std::ostream& os, const CharBoard& board) {
+inline std::ostream& operator<<(std::ostream& os, const CharBoard& board) {
     for (int r = 0; r < board.nbRanks; ++r)
     {
         for (int c = 0; c < board.nbFiles; ++c)
@@ -433,7 +438,7 @@ std::ostream& operator<<(std::ostream& os, const CharBoard& board) {
     return os;
 }
 
-Validation check_for_valid_characters(const std::string& firstFenPart, const std::string& validSpecialCharacters, const Variant* v) {
+inline Validation check_for_valid_characters(const std::string& firstFenPart, const std::string& validSpecialCharacters, const Variant* v) {
     for (char c : firstFenPart)
     {
         if (!isdigit(c) && v->pieceToChar.find(c) == std::string::npos && v->pieceToCharSynonyms.find(c) == std::string::npos && validSpecialCharacters.find(c) == std::string::npos)
@@ -445,7 +450,7 @@ Validation check_for_valid_characters(const std::string& firstFenPart, const std
     return OK;
 }
 
-std::vector<std::string> get_fen_parts(const std::string& fullFen, char delim) {
+inline std::vector<std::string> get_fen_parts(const std::string& fullFen, char delim) {
     std::vector<std::string> fenParts;
     std::string curPart;
     std::stringstream ss(fullFen);
@@ -455,7 +460,7 @@ std::vector<std::string> get_fen_parts(const std::string& fullFen, char delim) {
 }
 
 /// fills the character board according to a given FEN string
-Validation fill_char_board(CharBoard& board, const std::string& fenBoard, const std::string& validSpecialCharacters, const Variant* v) {
+inline Validation fill_char_board(CharBoard& board, const std::string& fenBoard, const std::string& validSpecialCharacters, const Variant* v) {
     int rankIdx = 0;
     int fileIdx = 0;
 
@@ -515,7 +520,7 @@ Validation fill_char_board(CharBoard& board, const std::string& fenBoard, const
     return OK;
 }
 
-Validation check_touching_kings(const CharBoard& board, const std::array<CharSquare, 2>& kingPositions) {
+inline Validation check_touching_kings(const CharBoard& board, const std::array<CharSquare, 2>& kingPositions) {
     if (non_root_euclidian_distance(kingPositions[WHITE], kingPositions[BLACK]) <= 2)
     {
         std::cerr << "King pieces are next to each other." << std::endl;
@@ -525,7 +530,7 @@ Validation check_touching_kings(const CharBoard& board, const std::array<CharSqu
     return OK;
 }
 
-Validation fill_castling_info_splitted(const std::string& castlingInfo, std::array<std::string, 2>& castlingInfoSplitted) {
+inline Validation fill_castling_info_splitted(const std::string& castlingInfo, std::array<std::string, 2>& castlingInfoSplitted) {
     for (char c : castlingInfo)
     {
         if (c != '-')
@@ -544,7 +549,7 @@ Validation fill_castling_info_splitted(const std::string& castlingInfo, std::arr
     return OK;
 }
 
-std::string color_to_string(Color c) {
+inline std::string color_to_string(Color c) {
     switch (c)
     {
     case WHITE:
@@ -558,7 +563,7 @@ std::string color_to_string(Color c) {
     }
 }
 
-std::string castling_rights_to_string(CastlingRights castlingRights) {
+inline std::string castling_rights_to_string(CastlingRights castlingRights) {
     switch (castlingRights)
     {
     case KING_SIDE:
@@ -586,7 +591,7 @@ std::string castling_rights_to_string(CastlingRights castlingRights) {
     }
 }
 
-Validation check_castling_rank(const std::array<std::string, 2>& castlingInfoSplitted, const CharBoard& board, const Variant* v) {
+inline Validation check_castling_rank(const std::array<std::string, 2>& castlingInfoSplitted, const CharBoard& board, const Variant* v) {
 
     for (Color c : {WHITE, BLACK})
     {
@@ -606,7 +611,7 @@ Validation check_castling_rank(const std::array<std::string, 2>& castlingInfoSpl
     return OK;
 }
 
-Validation check_standard_castling(std::array<std::string, 2>& castlingInfoSplitted, const CharBoard& board,
+inline Validation check_standard_castling(std::array<std::string, 2>& castlingInfoSplitted, const CharBoard& board,
                              const std::array<CharSquare, 2>& kingPositions, const std::array<CharSquare, 2>& kingPositionsStart,
                              const std::array<std::vector<CharSquare>, 2>& rookPositionsStart, const Variant* v) {
 
@@ -640,7 +645,7 @@ Validation check_standard_castling(std::array<std::string, 2>& castlingInfoSplit
     return OK;
 }
 
-Validation check_pocket_info(const std::string& fenBoard, int nbRanks, const Variant* v, std::string& pocket) {
+inline Validation check_pocket_info(const std::string& fenBoard, int nbRanks, const Variant* v, std::string& pocket) {
 
     char stopChar;
     int offset = 0;
@@ -682,11 +687,11 @@ Validation check_pocket_info(const std::string& fenBoard, int nbRanks, const Var
     return NOK;
 }
 
-int piece_count(const std::string& fenBoard, Color c, PieceType pt, const Variant* v) {
+inline int piece_count(const std::string& fenBoard, Color c, PieceType pt, const Variant* v) {
     return std::count(fenBoard.begin(), fenBoard.end(), v->pieceToChar[make_piece(c, pt)]);
 }
 
-Validation check_number_of_kings(const std::string& fenBoard, const std::string& startFenBoard, const Variant* v) {
+inline Validation check_number_of_kings(const std::string& fenBoard, const std::string& startFenBoard, const Variant* v) {
     int nbWhiteKings = piece_count(fenBoard, WHITE, KING, v);
     int nbBlackKings = piece_count(fenBoard, BLACK, KING, v);
     int nbWhiteKingsStart = piece_count(startFenBoard, WHITE, KING, v);
@@ -706,7 +711,7 @@ Validation check_number_of_kings(const std::string& fenBoard, const std::string&
 }
 
 
-Validation check_en_passant_square(const std::string& enPassantInfo) {
+inline Validation check_en_passant_square(const std::string& enPassantInfo) {
     if (enPassantInfo.size() != 1 || enPassantInfo[0] != '-')
     {
         if (enPassantInfo.size() != 2)
@@ -729,7 +734,7 @@ Validation check_en_passant_square(const std::string& enPassantInfo) {
 }
 
 
-Validation check_check_count(const std::string& checkCountInfo) {
+inline Validation check_check_count(const std::string& checkCountInfo) {
     if (checkCountInfo.size() != 3)
     {
         std::cerr << "Invalid check count '" << checkCountInfo << "'. Expects 3 characters. Actual: " << checkCountInfo.size() << " character(s)." << std::endl;
@@ -748,7 +753,7 @@ Validation check_check_count(const std::string& checkCountInfo) {
 }
 
 
-Validation check_digit_field(const std::string& field) {
+inline Validation check_digit_field(const std::string& field) {
     if (field.size() == 1 && field[0] == '-')
         return OK;
     for (char c : field)
@@ -758,7 +763,7 @@ Validation check_digit_field(const std::string& field) {
 }
 
 
-FenValidation validate_fen(const std::string& fen, const Variant* v, bool chess960 = false) {
+inline FenValidation validate_fen(const std::string& fen, const Variant* v, bool chess960 = false) {
 
     const std::string validSpecialCharacters = "/+~[]-";
     // 0) Layout
@@ -915,3 +920,5 @@ FenValidation validate_fen(const std::string& fen, const Variant* v, bool chess9
     return FEN_OK;
 }
 } // namespace FEN
+
+#endif // #ifndef APIUTIL_H_INCLUDED
index 2ff2316..cf7a45e 100644 (file)
@@ -73,17 +73,29 @@ namespace {
   Bitboard JanggiElephantTable[0x5C00];  // To store janggi elephant attacks
 #endif
 
+  // Rider directions
+  const std::set<Direction> RookDirectionsV { NORTH, SOUTH};
+  const std::set<Direction> RookDirectionsH { EAST, WEST };
+  const std::set<Direction> BishopDirections { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
+  const std::set<Direction> HorseDirections { 2 * SOUTH + WEST, 2 * SOUTH + EAST, SOUTH + 2 * WEST, SOUTH + 2 * EAST,
+                                              NORTH + 2 * WEST, NORTH + 2 * EAST, 2 * NORTH + WEST, 2 * NORTH + EAST };
+  const std::set<Direction> ElephantDirections { 2 * NORTH_EAST, 2 * SOUTH_EAST, 2 * SOUTH_WEST, 2 * NORTH_WEST };
+  const std::set<Direction> JanggiElephantDirections { NORTH + 2 * NORTH_EAST, EAST  + 2 * NORTH_EAST,
+                                                       EAST  + 2 * SOUTH_EAST, SOUTH + 2 * SOUTH_EAST,
+                                                       SOUTH + 2 * SOUTH_WEST, WEST  + 2 * SOUTH_WEST,
+                                                       WEST  + 2 * NORTH_WEST, NORTH + 2 * NORTH_WEST };
+
   enum MovementType { RIDER, HOPPER, LAME_LEAPER };
 
   template <MovementType MT>
 #ifdef PRECOMPUTED_MAGICS
-  void init_magics(Bitboard table[], Magic magics[], std::vector<Direction> directions, Bitboard magicsInit[]);
+  void init_magics(Bitboard table[], Magic magics[], std::set<Direction> directions, Bitboard magicsInit[]);
 #else
-  void init_magics(Bitboard table[], Magic magics[], std::vector<Direction> directions);
+  void init_magics(Bitboard table[], Magic magics[], std::set<Direction> directions);
 #endif
 
   template <MovementType MT>
-  Bitboard sliding_attack(std::vector<Direction> directions, Square sq, Bitboard occupied, Color c = WHITE) {
+  Bitboard sliding_attack(std::set<Direction> directions, Square sq, Bitboard occupied, Color c = WHITE) {
     assert(MT != LAME_LEAPER);
 
     Bitboard attack = 0;
@@ -134,14 +146,14 @@ namespace {
     return b;
   }
 
-  Bitboard lame_leaper_path(std::vector<Direction> directions, Square s) {
+  Bitboard lame_leaper_path(std::set<Direction> directions, Square s) {
     Bitboard b = 0;
     for (Direction d : directions)
         b |= lame_leaper_path(d, s);
     return b;
   }
 
-  Bitboard lame_leaper_attack(std::vector<Direction> directions, Square s, Bitboard occupied) {
+  Bitboard lame_leaper_attack(std::set<Direction> directions, Square s, Bitboard occupied) {
     Bitboard b = 0;
     for (Direction d : directions)
     {
@@ -183,63 +195,18 @@ const std::string Bitboards::pretty(Bitboard b) {
   return s;
 }
 
+/// Bitboards::init_pieces() initializes piece move/attack bitboards and rider types
 
-/// Bitboards::init() initializes various bitboard tables. It is called at
-/// startup and relies on global objects to be already zero-initialized.
-
-void Bitboards::init() {
-
-  // Piece moves
-  std::vector<Direction> RookDirectionsV = { NORTH, SOUTH};
-  std::vector<Direction> RookDirectionsH = { EAST, WEST };
-  std::vector<Direction> BishopDirections = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
-  std::vector<Direction> HorseDirections = {2 * SOUTH + WEST, 2 * SOUTH + EAST, SOUTH + 2 * WEST, SOUTH + 2 * EAST,
-                                            NORTH + 2 * WEST, NORTH + 2 * EAST, 2 * NORTH + WEST, 2 * NORTH + EAST };
-  std::vector<Direction> ElephantDirections = { 2 * NORTH_EAST, 2 * SOUTH_EAST, 2 * SOUTH_WEST, 2 * NORTH_WEST };
-  std::vector<Direction> JanggiElephantDirections = { NORTH + 2 * NORTH_EAST, EAST  + 2 * NORTH_EAST,
-                                                      EAST  + 2 * SOUTH_EAST, SOUTH + 2 * SOUTH_EAST,
-                                                      SOUTH + 2 * SOUTH_WEST, WEST + 2 * SOUTH_WEST,
-                                                      WEST  + 2 * NORTH_WEST, NORTH + 2 * NORTH_WEST };
-
-  for (unsigned i = 0; i < (1 << 16); ++i)
-      PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
-
-  for (Square s = SQ_A1; s <= SQ_MAX; ++s)
-      SquareBB[s] = make_bitboard(s);
-
-  for (File f = FILE_A; f <= FILE_MAX; ++f)
-      for (Rank r = RANK_1; r <= RANK_MAX; ++r)
-          BoardSizeBB[f][r] = forward_file_bb(BLACK, make_square(f, r)) | SquareBB[make_square(f, r)] | (f > FILE_A ? BoardSizeBB[f - 1][r] : Bitboard(0));
-
-  for (Square s1 = SQ_A1; s1 <= SQ_MAX; ++s1)
-      for (Square s2 = SQ_A1; s2 <= SQ_MAX; ++s2)
-              SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
-
-#ifdef PRECOMPUTED_MAGICS
-  init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH, RookMagicHInit);
-  init_magics<RIDER>(RookTableV, RookMagicsV, RookDirectionsV, RookMagicVInit);
-  init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections, BishopMagicInit);
-  init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH, CannonMagicHInit);
-  init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit);
-  init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections, HorseMagicInit);
-  init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit);
-  init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections, JanggiElephantMagicInit);
-#else
-  init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH);
-  init_magics<RIDER>(RookTableV, RookMagicsV, RookDirectionsV);
-  init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections);
-  init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH);
-  init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV);
-  init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections);
-  init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections);
-  init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections);
-#endif
+void Bitboards::init_pieces() {
 
   for (PieceType pt = PAWN; pt <= KING; ++pt)
   {
       const PieceInfo* pi = pieceMap.find(pt)->second;
 
       // Initialize rider types
+      AttackRiderTypes[pt] = NO_RIDER;
+      MoveRiderTypes[pt] = NO_RIDER;
+
       if (pi->lameLeaper)
       {
           for (Direction d : pi->stepsCapture)
@@ -299,6 +266,10 @@ void Bitboards::init() {
       {
           for (Square s = SQ_A1; s <= SQ_MAX; ++s)
           {
+              PseudoAttacks[c][pt][s] = 0;
+              PseudoMoves[c][pt][s] = 0;
+              LeaperAttacks[c][pt][s] = 0;
+              LeaperMoves[c][pt][s] = 0;
               for (Direction d : pi->stepsCapture)
               {
                   PseudoAttacks[c][pt][s] |= safe_destination(s, c == WHITE ? d : -d);
@@ -318,6 +289,49 @@ void Bitboards::init() {
           }
       }
   }
+}
+
+
+/// Bitboards::init() initializes various bitboard tables. It is called at
+/// startup and relies on global objects to be already zero-initialized.
+
+void Bitboards::init() {
+
+  for (unsigned i = 0; i < (1 << 16); ++i)
+      PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
+
+  for (Square s = SQ_A1; s <= SQ_MAX; ++s)
+      SquareBB[s] = make_bitboard(s);
+
+  for (File f = FILE_A; f <= FILE_MAX; ++f)
+      for (Rank r = RANK_1; r <= RANK_MAX; ++r)
+          BoardSizeBB[f][r] = forward_file_bb(BLACK, make_square(f, r)) | SquareBB[make_square(f, r)] | (f > FILE_A ? BoardSizeBB[f - 1][r] : Bitboard(0));
+
+  for (Square s1 = SQ_A1; s1 <= SQ_MAX; ++s1)
+      for (Square s2 = SQ_A1; s2 <= SQ_MAX; ++s2)
+              SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
+
+#ifdef PRECOMPUTED_MAGICS
+  init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH, RookMagicHInit);
+  init_magics<RIDER>(RookTableV, RookMagicsV, RookDirectionsV, RookMagicVInit);
+  init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections, BishopMagicInit);
+  init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH, CannonMagicHInit);
+  init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit);
+  init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections, HorseMagicInit);
+  init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit);
+  init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections, JanggiElephantMagicInit);
+#else
+  init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH);
+  init_magics<RIDER>(RookTableV, RookMagicsV, RookDirectionsV);
+  init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections);
+  init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH);
+  init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV);
+  init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections);
+  init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections);
+  init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections);
+#endif
+
+  init_pieces();
 
   for (Square s1 = SQ_A1; s1 <= SQ_MAX; ++s1)
   {
@@ -338,9 +352,9 @@ namespace {
 
   template <MovementType MT>
 #ifdef PRECOMPUTED_MAGICS
-  void init_magics(Bitboard table[], Magic magics[], std::vector<Direction> directions, Bitboard magicsInit[]) {
+  void init_magics(Bitboard table[], Magic magics[], std::set<Direction> directions, Bitboard magicsInit[]) {
 #else
-  void init_magics(Bitboard table[], Magic magics[], std::vector<Direction> directions) {
+  void init_magics(Bitboard table[], Magic magics[], std::set<Direction> directions) {
 #endif
 
     // Optimal PRNG seeds to pick the correct magics in the shortest time
index 5672dcd..dd3d2c7 100644 (file)
@@ -32,6 +32,7 @@ bool probe(Square wksq, Square wpsq, Square bksq, Color us);
 
 namespace Bitboards {
 
+void init_pieces();
 void init();
 const std::string pretty(Bitboard b);
 
index f4b87af..9163b4a 100644 (file)
@@ -134,41 +134,45 @@ namespace Eval::NNUE {
   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, 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, 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, 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, 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, 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, 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, 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, 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(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);
@@ -178,23 +182,27 @@ namespace Eval::NNUE {
   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, 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, 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, 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 }
+      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);
@@ -204,58 +212,45 @@ namespace Eval::NNUE {
   constexpr uint32_t kpp_board_index[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_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_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_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_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_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_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_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_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_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_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_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_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_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_W_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_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_B_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_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_B_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_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_W_KING,
+    }
   };
   // Check that the fragile array definition is correct
   static_assert(kpp_board_index[WHITE][make_piece(WHITE, PAWN)] == PS_W_PAWN);
index 7d53842..ed1762f 100644 (file)
@@ -19,6 +19,7 @@
 #include <string>
 #include <sstream>
 
+#include "apiutil.h"
 #include "parser.h"
 #include "piece.h"
 #include "types.h"
@@ -183,6 +184,47 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
             parse_attribute("mobilityRegion" + color + capitalizedPiece, v->mobilityRegion[c][pieceInfo.first]);
         }
     }
+    // custom piece types
+    for (PieceType pt = CUSTOM_PIECES; pt <= CUSTOM_PIECES_END; ++pt)
+    {
+        std::string customPieceName = "customPiece" + std::to_string(pt - CUSTOM_PIECES + 1);
+        const auto& itCustomPt = config.find(customPieceName);
+        if (itCustomPt != config.end() && !itCustomPt->second.empty())
+        {
+            // piece char
+            if (isalpha(itCustomPt->second.at(0)))
+                v->add_piece(pt, itCustomPt->second.at(0));
+            else
+            {
+                if (DoCheck && itCustomPt->second.at(0) != '-')
+                    std::cerr << customPieceName << " - Invalid letter: " << itCustomPt->second.at(0) << std::endl;
+                v->remove_piece(pt);
+            }
+            // betza
+            if (itCustomPt->second.size() > 1)
+                v->customPiece[pt - CUSTOM_PIECES] = itCustomPt->second.substr(2);
+            else if (DoCheck)
+                std::cerr << customPieceName << " - Missing Betza move notation" << std::endl;
+        }
+    }
+    // piece values
+    for (Phase phase : {MG, EG})
+    {
+        const std::string optionName = phase == MG ? "pieceValueMg" : "pieceValueEg";
+        const auto& pv = config.find(optionName);
+        if (pv != config.end())
+        {
+            char token;
+            size_t idx = 0;
+            std::stringstream ss(pv->second);
+            while (!ss.eof() && ss >> token && (idx = v->pieceToChar.find(toupper(token))) != std::string::npos
+                             && ss >> token && ss >> v->pieceValue[phase][idx]) {}
+            if (DoCheck && idx == std::string::npos)
+                std::cerr << optionName << " - Invalid piece type: " << token << std::endl;
+            else if (DoCheck && !ss.eof())
+                std::cerr << optionName << " - Invalid piece value for type: " << v->pieceToChar[idx] << std::endl;
+        }
+    }
     parse_attribute("variantTemplate", v->variantTemplate);
     parse_attribute("pieceToCharTable", v->pieceToCharTable);
     parse_attribute("pocketSize", v->pocketSize);
@@ -333,28 +375,15 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     if (DoCheck)
     {
         // startFen
-        std::string fenBoard = v->startFen.substr(0, v->startFen.find(' '));
-        std::stringstream ss(fenBoard);
-        char token;
-        ss >> std::noskipws;
-
-        while (ss >> token)
-        {
-            if (token == '+')
-            {
-                ss >> token;
-                size_t idx = v->pieceToChar.find(toupper(token));
-                if (idx == std::string::npos || !v->promotedPieceType[idx])
-                    std::cerr << "startFen - Invalid piece type: +" << token << std::endl;
-            }
-            else if (isalpha(token) && v->pieceToChar.find(toupper(token)) == std::string::npos)
-                std::cerr << "startFen - Invalid piece type: " << token << std::endl;
-        }
+        if (FEN::validate_fen(v->startFen, v, v->chess960) != FEN::FEN_OK)
+            std::cerr << "startFen - Invalid starting position: " << v->startFen << std::endl;
 
         // pieceToCharTable
         if (v->pieceToCharTable != "-")
         {
-            ss = std::stringstream(v->pieceToCharTable);
+            const std::string fenBoard = v->startFen.substr(0, v->startFen.find(' '));
+            std::stringstream ss(v->pieceToCharTable);
+            char token;
             while (ss >> token)
                 if (isalpha(token) && v->pieceToChar.find(toupper(token)) == std::string::npos)
                     std::cerr << "pieceToCharTable - Invalid piece type: " << token << std::endl;
index c750be3..584d17d 100644 (file)
@@ -129,28 +129,28 @@ namespace {
                   // Add moves
                   for (char mt : moveTypes)
                   {
-                      std::vector<Direction>& v = hopper ? (mt == 'c' ? p->hopperCapture : p->hopperQuiet)
-                                                 : rider ? (mt == 'c' ? p->sliderCapture : p->sliderQuiet)
-                                                         : (mt == 'c' ? p->stepsCapture : p->stepsQuiet);
+                      std::set<Direction>& v = hopper ? (mt == 'c' ? p->hopperCapture : p->hopperQuiet)
+                                              : rider ? (mt == 'c' ? p->sliderCapture : p->sliderQuiet)
+                                                      : (mt == 'c' ? p->stepsCapture : p->stepsQuiet);
                       auto has_dir = [&](std::string s) {
                         return std::find(directions.begin(), directions.end(), s) != directions.end();
                       };
                       if (directions.size() == 0 || has_dir("ff") || has_dir("vv") || has_dir("rf") || has_dir("rv") || has_dir("fh") || has_dir("rh") || has_dir("hr"))
-                          v.push_back(Direction(atom.first * FILE_NB + atom.second));
+                          v.insert(Direction(atom.first * FILE_NB + atom.second));
                       if (directions.size() == 0 || has_dir("bb") || has_dir("vv") || has_dir("lb") || has_dir("lv") || has_dir("bh") || has_dir("lh") || has_dir("hr"))
-                          v.push_back(Direction(-atom.first * FILE_NB - atom.second));
+                          v.insert(Direction(-atom.first * FILE_NB - atom.second));
                       if (directions.size() == 0 || has_dir("rr") || has_dir("ss") || has_dir("br") || has_dir("bs") || has_dir("bh") || has_dir("lh") || has_dir("hr"))
-                          v.push_back(Direction(-atom.second * FILE_NB + atom.first));
+                          v.insert(Direction(-atom.second * FILE_NB + atom.first));
                       if (directions.size() == 0 || has_dir("ll") || has_dir("ss") || has_dir("fl") || has_dir("fs") || has_dir("fh") || has_dir("rh") || has_dir("hr"))
-                          v.push_back(Direction(atom.second * FILE_NB - atom.first));
+                          v.insert(Direction(atom.second * FILE_NB - atom.first));
                       if (directions.size() == 0 || has_dir("rr") || has_dir("ss") || has_dir("fr") || has_dir("fs") || has_dir("fh") || has_dir("rh") || has_dir("hl"))
-                          v.push_back(Direction(atom.second * FILE_NB + atom.first));
+                          v.insert(Direction(atom.second * FILE_NB + atom.first));
                       if (directions.size() == 0 || has_dir("ll") || has_dir("ss") || has_dir("bl") || has_dir("bs") || has_dir("bh") || has_dir("lh") || has_dir("hl"))
-                          v.push_back(Direction(-atom.second * FILE_NB - atom.first));
+                          v.insert(Direction(-atom.second * FILE_NB - atom.first));
                       if (directions.size() == 0 || has_dir("bb") || has_dir("vv") || has_dir("rb") || has_dir("rv") || has_dir("bh") || has_dir("rh") || has_dir("hl"))
-                          v.push_back(Direction(-atom.first * FILE_NB + atom.second));
+                          v.insert(Direction(-atom.first * FILE_NB + atom.second));
                       if (directions.size() == 0 || has_dir("ff") || has_dir("vv") || has_dir("lf") || has_dir("lv") || has_dir("fh") || has_dir("lh") || has_dir("hl"))
-                          v.push_back(Direction(atom.first * FILE_NB - atom.second));
+                          v.insert(Direction(atom.first * FILE_NB - atom.second));
                   }
               }
               // Reset state
@@ -180,7 +180,8 @@ namespace {
   }
 }
 
-void PieceMap::init() {
+void PieceMap::init(const Variant* v) {
+  clear_all();
   add(PAWN, from_betza("fmWfceF", "pawn"));
   add(KNIGHT, from_betza("N", "knight"));
   add(BISHOP, from_betza("B", "bishop"));
@@ -221,6 +222,9 @@ void PieceMap::init() {
   add(COMMONER, from_betza("K", "commoner"));
   add(CENTAUR, from_betza("KN", "centaur"));
   add(KING, from_betza("K", "king"));
+  // Add custom pieces
+  for (PieceType pt = CUSTOM_PIECES; pt <= CUSTOM_PIECES_END; ++pt)
+      add(pt, from_betza(v != nullptr ? v->customPiece[pt - CUSTOM_PIECES] : "", ""));
 }
 
 void PieceMap::add(PieceType pt, const PieceInfo* p) {
index 66fa36f..2ce4a7e 100644 (file)
 
 #include <string>
 #include <map>
-#include <vector>
+#include <set>
 
 #include "types.h"
+#include "variant.h"
 
 
 /// PieceInfo struct stores information about the piece movements.
 struct PieceInfo {
   std::string name = "";
   std::string betza = "";
-  std::vector<Direction> stepsQuiet = {};
-  std::vector<Direction> stepsCapture = {};
-  std::vector<Direction> sliderQuiet = {};
-  std::vector<Direction> sliderCapture = {};
-  std::vector<Direction> hopperQuiet = {};
-  std::vector<Direction> hopperCapture = {};
+  std::set<Direction> stepsQuiet = {};
+  std::set<Direction> stepsCapture = {};
+  std::set<Direction> sliderQuiet = {};
+  std::set<Direction> sliderCapture = {};
+  std::set<Direction> hopperQuiet = {};
+  std::set<Direction> hopperCapture = {};
   bool lameLeaper = false;
 };
 
 struct PieceMap : public std::map<PieceType, const PieceInfo*> {
-  void init();
+  void init(const Variant* v = nullptr);
   void add(PieceType pt, const PieceInfo* v);
   void clear_all();
 };
index 7b63851..4504b55 100644 (file)
@@ -527,8 +527,14 @@ void Position::set_check_info(StateInfo* si) const {
   Square ksq = count<KING>(~sideToMove) ? square<KING>(~sideToMove) : SQ_NONE;
 
   // For unused piece types, the check squares are left uninitialized
+  si->nonSlidingRiders = 0;
   for (PieceType pt : piece_types())
+  {
       si->checkSquares[pt] = ksq != SQ_NONE ? attacks_bb(~sideToMove, pt, ksq, pieces()) : Bitboard(0);
+      // Collect special piece types that require slower check and evasion detection
+      if (AttackRiderTypes[pt] & NON_SLIDING_RIDERS)
+          si->nonSlidingRiders |= pieces(pt);
+  }
   si->checkSquares[KING]   = 0;
   si->shak = si->checkersBB & (byTypeBB[KNIGHT] | byTypeBB[ROOK] | byTypeBB[BERS]);
   si->bikjang = var->bikjangRule && ksq != SQ_NONE ? bool(attacks_bb(sideToMove, ROOK, ksq, pieces()) & pieces(sideToMove, KING)) : false;
index d67b99a..8bd3623 100644 (file)
@@ -67,6 +67,7 @@ struct StateInfo {
   Bitboard   blockersForKing[COLOR_NB];
   Bitboard   pinners[COLOR_NB];
   Bitboard   checkSquares[PIECE_TYPE_NB];
+  Bitboard   nonSlidingRiders;
   Bitboard   flippedPieces;
   Bitboard   pseudoRoyals;
   OptBool    legalCapture;
@@ -946,7 +947,7 @@ inline Bitboard Position::major_pieces(Color c) const {
 }
 
 inline Bitboard Position::non_sliding_riders() const {
-  return pieces(CANNON, BANNER) | pieces(HORSE, ELEPHANT) | pieces(JANGGI_CANNON, JANGGI_ELEPHANT);
+  return st->nonSlidingRiders;
 }
 
 inline int Position::count(Color c, PieceType pt) const {
index 2d52b7d..5b7f3a5 100644 (file)
 Value EvalPieceValue[PHASE_NB][PIECE_NB];
 Value CapturePieceValue[PHASE_NB][PIECE_NB];
 
+Value PieceValue[PHASE_NB][PIECE_NB] = {
+  {
+    VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, FersValueMg, AlfilValueMg,
+    FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg, ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg,
+    BiskniValueMg, KnirooValueMg, RookniValueMg, ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg,
+    DragonHorseValueMg, ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg, QuietQueenPieceValueMg, CannonPieceValueMg, JanggiCannonPieceValueMg,
+    SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg, WazirValueMg, CommonerValueMg, CentaurValueMg,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+
+    VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, FersValueMg, AlfilValueMg,
+    FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg, ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg,
+    BiskniValueMg, KnirooValueMg, RookniValueMg, ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg,
+    DragonHorseValueMg, ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg, QuietQueenPieceValueMg, CannonPieceValueMg, JanggiCannonPieceValueMg,
+    SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg, WazirValueMg, CommonerValueMg, CentaurValueMg,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+  },
+  {
+    VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, FersValueEg, AlfilValueEg,
+    FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg, ArchbishopValueEg, ChancellorValueEg, AmazonValueEg, KnibisValueEg,
+    BiskniValueEg, KnirooValueEg, RookniValueEg, ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg,
+    DragonHorseValueEg, ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg, QuietQueenPieceValueEg, CannonPieceValueEg, JanggiCannonPieceValueEg,
+    SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg, WazirValueEg, CommonerValueEg, CentaurValueEg,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+
+    VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, FersValueEg, AlfilValueEg,
+    FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg, ArchbishopValueEg, ChancellorValueEg, AmazonValueEg, KnibisValueEg,
+    BiskniValueEg, KnirooValueEg, RookniValueEg, ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg,
+    DragonHorseValueEg, ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg, QuietQueenPieceValueEg, CannonPieceValueEg, JanggiCannonPieceValueEg,
+    SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg, WazirValueEg, CommonerValueEg, CentaurValueEg,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+  },
+};
 
 
 namespace
@@ -108,6 +148,21 @@ constexpr Score PBonus[RANK_NB][FILE_NB] =
    { S( -7, -1), S(  6,-14), S( -2, 13), S(-11, 22), S(  4, 24), S(-14, 17), S( 10,  7), S( -9,  7) }
   };
 
+// Estimate piece value
+Value piece_value(Phase phase, PieceType pt)
+{
+    const PieceInfo* pi = pieceMap.find(pt)->second;
+    return Value(   70 * pi->stepsCapture.size()
+                 +  40 * pi->stepsQuiet.size()
+                 + 150 * pi->sliderCapture.size()
+                 +  80 * pi->sliderQuiet.size()
+                 // Rook sliding directions are more valuable
+                 +  60 * std::count_if(pi->sliderQuiet.begin(), pi->sliderQuiet.end(), [](Direction d) { return std::abs(d) == NORTH || std::abs(d) == 1; })
+                 // Hoppers are more useful with more pieces on the board
+                 + (phase == MG ? 120 : 80) * pi->hopperCapture.size()
+                 + (phase == MG ?  80 : 60) * pi->hopperQuiet.size());
+}
+
 } // namespace
 
 
@@ -123,8 +178,16 @@ void init(const Variant* v) {
 
   PieceType strongestPiece = NO_PIECE_TYPE;
   for (PieceType pt : v->pieceTypes)
+  {
+      if (pt >= CUSTOM_PIECES && pt <= CUSTOM_PIECES_END)
+      {
+          PieceValue[MG][pt] = piece_value(MG, pt);
+          PieceValue[EG][pt] = piece_value(EG, pt);
+      }
+
       if (PieceValue[MG][pt] > PieceValue[MG][strongestPiece])
           strongestPiece = pt;
+  }
 
   Value maxPromotion = VALUE_ZERO;
   for (PieceType pt : v->promotionPieceTypes)
@@ -202,13 +265,19 @@ void init(const Variant* v) {
               score += make_score(std::max(QueenValueMg - PieceValue[MG][pt], VALUE_ZERO) / 20,
                                   std::max(QueenValueEg - PieceValue[EG][pt], VALUE_ZERO) / 20);
 
-      CapturePieceValue[MG][pc] = CapturePieceValue[MG][~pc] = mg_value(score);
-      CapturePieceValue[EG][pc] = CapturePieceValue[EG][~pc] = eg_value(score);
-
       // For antichess variants, use negative piece values
       if (v->extinctionValue == VALUE_MATE)
           score = -make_score(mg_value(score) / 8, eg_value(score) / 8 / (1 + !pi->sliderCapture.size()));
 
+      // Override variant piece value
+      if (v->pieceValue[MG][pt])
+          score = make_score(v->pieceValue[MG][pt], eg_value(score));
+      if (v->pieceValue[EG][pt])
+          score = make_score(mg_value(score), v->pieceValue[EG][pt]);
+
+      CapturePieceValue[MG][pc] = CapturePieceValue[MG][~pc] = mg_value(score);
+      CapturePieceValue[EG][pc] = CapturePieceValue[EG][~pc] = eg_value(score);
+
       // For drop variants, halve the piece values to compensate for double changes by captures
       if (v->capturesToHand)
           score = score / 2;
index 503e340..10f133d 100644 (file)
@@ -387,11 +387,16 @@ enum PieceType {
   SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, DRAGON_HORSE,
   CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, ATAXX_PIECE, QUIET_QUEEN, CANNON, JANGGI_CANNON,
   SOLDIER, HORSE, ELEPHANT, JANGGI_ELEPHANT, BANNER,
-  WAZIR, COMMONER, CENTAUR, KING,
+  WAZIR, COMMONER, CENTAUR,
+
+  CUSTOM_PIECES,
+  FAIRY_PIECES = QUEEN + 1,
+  FAIRY_PIECES_END = CUSTOM_PIECES - 1,
+  PIECE_TYPE_NB = 1 << PIECE_TYPE_BITS,
+  KING = PIECE_TYPE_NB - 1,
+  CUSTOM_PIECES_END = KING - 1,
+  CUSTOM_PIECES_NB = CUSTOM_PIECES_END - CUSTOM_PIECES + 1,
   ALL_PIECES = 0,
-  FAIRY_PIECES = FERS,
-
-  PIECE_TYPE_NB = 1 << PIECE_TYPE_BITS
 };
 static_assert(KING < PIECE_TYPE_NB, "KING exceeds PIECE_TYPE_NB.");
 static_assert(PIECE_TYPE_BITS <= 6, "PIECE_TYPE uses more than 6 bit");
@@ -415,63 +420,12 @@ enum RiderType : int {
   RIDER_ELEPHANT = 1 << 6,
   RIDER_JANGGI_ELEPHANT = 1 << 7,
   HOPPING_RIDERS = RIDER_CANNON_H | RIDER_CANNON_V,
+  LAME_LEAPERS = RIDER_HORSE | RIDER_ELEPHANT | RIDER_JANGGI_ELEPHANT,
   ASYMMETRICAL_RIDERS = RIDER_HORSE | RIDER_JANGGI_ELEPHANT,
+  NON_SLIDING_RIDERS = HOPPING_RIDERS | LAME_LEAPERS,
 };
 
-constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
-  { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg,
-    FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
-    ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
-    ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
-    ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg, QuietQueenPieceValueMg,
-    CannonPieceValueMg, JanggiCannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg,
-    WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg,
-    FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
-    ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
-    ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
-    ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg, QuietQueenPieceValueMg,
-    CannonPieceValueMg, JanggiCannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg,
-    WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO },
-  { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg,
-    FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
-    ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
-    ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
-    ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg, QuietQueenPieceValueEg,
-    CannonPieceValueEg, JanggiCannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg,
-    WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg,
-    FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
-    ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
-    ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
-    ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg, QuietQueenPieceValueEg,
-    CannonPieceValueEg, JanggiCannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg,
-    WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
-    VALUE_ZERO, VALUE_ZERO, VALUE_ZERO }
-};
-
-static_assert(   PieceValue[MG][PIECE_TYPE_NB + 1] == PawnValueMg
-              && PieceValue[EG][PIECE_TYPE_NB + 1] == PawnValueEg, "PieceValue array broken");
-
+extern Value PieceValue[PHASE_NB][PIECE_NB];
 extern Value EvalPieceValue[PHASE_NB][PIECE_NB]; // variant piece values for evaluation
 extern Value CapturePieceValue[PHASE_NB][PIECE_NB]; // variant piece values for captures/search
 
@@ -647,6 +601,7 @@ ENABLE_INCR_OPERATORS_ON(CheckCount)
 
 ENABLE_BASE_OPERATORS_ON(Score)
 
+ENABLE_BASE_OPERATORS_ON(PieceType)
 ENABLE_BIT_OPERATORS_ON(RiderType)
 ENABLE_BASE_OPERATORS_ON(RiderType)
 
index 2aa2fbe..c30654b 100644 (file)
@@ -65,6 +65,8 @@ void on_variant_set(const Option &o) {
     Eval::NNUE::init();
 
     const Variant* v = variants.find(o)->second;
+    pieceMap.init(v);
+    Bitboards::init_pieces();
     PSQT::init(v);
 }
 void on_variant_change(const Option &o) {
index e41f6f5..0cad420 100644 (file)
@@ -811,6 +811,65 @@ namespace {
         v->nnueFeatures = NNUE_SHOGI;
         return v;
     }
+    // Yari shogi
+    // https://en.wikipedia.org/wiki/Yari_shogi
+    Variant* yarishogi_variant() {
+        Variant* v = variant_base();
+        v->variantTemplate = "shogi";
+        v->maxRank = RANK_9;
+        v->maxFile = FILE_G;
+        v->reset_pieces();
+        v->add_piece(KING, 'k');
+        v->add_piece(SHOGI_PAWN, 'p');
+        v->add_piece(ROOK, 'l');
+        v->customPiece[0] = "fRffN"; // Yari knight
+        v->add_piece(CUSTOM_PIECES, 'n');
+        v->customPiece[1] = "fFfR"; // Yari bishop
+        v->add_piece(CUSTOM_PIECES + 1, 'b');
+        v->customPiece[2] = "frlR"; // Yari rook
+        v->add_piece(CUSTOM_PIECES + 2, 'r');
+        v->customPiece[3] = "WfFbR"; // Yari gold
+        v->add_piece(CUSTOM_PIECES + 3, 'g');
+        v->customPiece[4] = "fKbR"; // Yari silver
+        v->add_piece(CUSTOM_PIECES + 4, 's');
+        v->startFen = "rnnkbbr/7/ppppppp/7/7/7/PPPPPPP/7/RBBKNNR[-] w 0 1";
+        v->promotionRank = RANK_7;
+        v->promotedPieceType[SHOGI_PAWN] = CUSTOM_PIECES + 4;
+        v->promotedPieceType[CUSTOM_PIECES] = CUSTOM_PIECES + 3;
+        v->promotedPieceType[CUSTOM_PIECES + 1] = CUSTOM_PIECES + 3;
+        v->promotedPieceType[CUSTOM_PIECES + 2] = ROOK;
+        v->pieceDrops = true;
+        v->capturesToHand = true;
+        v->promotionPieceTypes = {};
+        v->doubleStep = false;
+        v->castling = false;
+        v->dropNoDoubled = SHOGI_PAWN;
+        v->immobilityIllegal = true;
+        v->shogiPawnDropMateIllegal = false;
+        v->stalemateValue = -VALUE_MATE;
+        v->nFoldRule = 3;
+        v->nMoveRule = 0;
+        v->perpetualCheckIllegal = true;
+        return v;
+    }
+    // Okisaki shogi
+    // https://en.wikipedia.org/wiki/Okisaki_shogi
+    Variant* okisakishogi_variant() {
+        Variant* v = minishogi_variant_base();
+        v->maxRank = RANK_10;
+        v->maxFile = FILE_J;
+        v->customPiece[0] = "vR"; // Vertical slider
+        v->add_piece(CUSTOM_PIECES, 'l');
+        v->add_piece(KNIGHT, 'n');
+        v->add_piece(QUEEN, 'q');
+        v->startFen = "lnsgkqgsnl/1r6b1/pppppppppp/10/10/10/10/PPPPPPPPPP/1B6R1/LNSGQKGSNL[-] w 0 1";
+        v->promotionRank = RANK_8;
+        v->promotedPieceType[CUSTOM_PIECES] = GOLD;
+        v->promotedPieceType[KNIGHT] = GOLD;
+        return v;
+    }
+    // Capablanca chess
+    // https://en.wikipedia.org/wiki/Capablanca_chess
     Variant* capablanca_variant() {
         Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ..AC............Kpnbrq..ac............k";
@@ -959,6 +1018,27 @@ namespace {
         v->castling = false;
         return v;
     }
+    // Tencubed
+    // https://www.chessvariants.com/contests/10/tencubedchess.html
+    Variant* tencubed_variant() {
+        Variant* v = chess_variant_base();
+        v->pieceToCharTable = "PNBRQ.CAM...........WKpnbrq.cam...........wk";
+        v->maxRank = RANK_10;
+        v->maxFile = FILE_J;
+        v->startFen = "2cwamwc2/1rnbqkbnr1/pppppppppp/10/10/10/10/PPPPPPPPPP/1RNBQKBNR1/2CWAMWC2 w - - 0 1";
+        v->add_piece(ARCHBISHOP, 'a');
+        v->add_piece(CHANCELLOR, 'm');
+        v->customPiece[0] = "DAW"; // Champion
+        v->customPiece[1] = "CF"; // Wizard
+        v->add_piece(CUSTOM_PIECES, 'c');
+        v->add_piece(CUSTOM_PIECES + 1, 'w');
+        v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN};
+        v->promotionRank = RANK_10;
+        v->doubleStepRank = RANK_3;
+        v->doubleStepRankMin = RANK_3;
+        v->castling = false;
+        return v;
+    }
     Variant* shako_variant() {
         Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ.E....C.........Kpnbrq.e....c.........k";
@@ -1001,6 +1081,8 @@ namespace {
         return v;
     }
 #endif
+    // Xiangqi
+    // https://en.wikipedia.org/wiki/Xiangqi
     Variant* xiangqi_variant() {
         Variant* v = minixiangqi_variant();
         v->pieceToCharTable = "PN.R.AB..K.C..........pn.r.ab..k.c..........";
@@ -1164,6 +1246,8 @@ void VariantMap::init() {
     add("minixiangqi", minixiangqi_variant()->conclude());
 #ifdef LARGEBOARDS
     add("shogi", shogi_variant()->conclude());
+    add("yarishogi", yarishogi_variant()->conclude());
+    add("okisakishogi", okisakishogi_variant()->conclude());
     add("capablanca", capablanca_variant()->conclude());
     add("capahouse", capahouse_variant()->conclude());
     add("caparandom", caparandom_variant()->conclude());
@@ -1176,6 +1260,7 @@ void VariantMap::init() {
     add("jesonmor", jesonmor_variant()->conclude());
     add("courier", courier_variant()->conclude());
     add("grand", grand_variant()->conclude());
+    add("tencubed", tencubed_variant()->conclude());
     add("shako", shako_variant()->conclude());
     add("clobber10", clobber10_variant()->conclude());
 #ifdef ALLVARS
index 14f74c8..dd03214 100644 (file)
@@ -39,6 +39,8 @@ struct Variant {
   File maxFile = FILE_H;
   bool chess960 = false;
   bool twoBoards = false;
+  int pieceValue[PHASE_NB][PIECE_TYPE_NB] = {};
+  std::string customPiece[CUSTOM_PIECES_NB] = {};
   std::set<PieceType> pieceTypes = { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING };
   std::string pieceToChar =  " PNBRQ" + std::string(KING - QUEEN - 1, ' ') + "K" + std::string(PIECE_TYPE_NB - KING - 1, ' ')
                            + " pnbrq" + std::string(KING - QUEEN - 1, ' ') + "k" + std::string(PIECE_TYPE_NB - KING - 1, ' ');
index f5e966e..437343e 100644 (file)
 # centaur (=knight+commoner)
 # king
 
+### Custom pieces
+# Custom pieces can be defined by using one of the available slots:
+# customPiece1, customPiece2, ..., customPiece23
+# E.g., pawns without double steps could be described as:
+# customPiece1 = p:mfWcfF
+#
+# The movements of custom pieces can be defined using the Betza notation.
+# https://www.gnu.org/software/xboard/Betza.html
+# In Fairy-Stockfish only a subset of Betza notation can be used. The supported features are:
+# - all base moves/atoms (W, F, etc.)
+# - all directional modifiers (f, b, etc.)
+# - unlimited distance sliders for W/R and F/B directions
+# - hoppers for W/R directions, i.e., pR
+# - lame leapers (n) for N and A directions, i.e., nN and nA
+
+### Piece values
+# The predefined and precalculated piece values can be overriden
+# by specifying the pieceValueMg and pieceValueEg options, e.g.,
+# pieceValueMg = p:150 n:800
+# pieceValueEg = p:200 n:900
+#
+# For orientation, the internal predefined piece values can be found in types.h.
+# A suitable piece for gauging the piece values is the rook, which internally has:
+# pieceValueMg = r:1276
+# pieceValueEg = r:1380
+
 ### Option types
 # [bool]: boolean flag to enable/disable a feature [true, false]
 # [Rank]: denotes a rank of the board [1-10]
 # [CountingRule]: makruk or ASEAN counting rules [makruk, asean, none]
 # [EnclosingRule]: reversi or ataxx enclosing rules [reversi, ataxx, none]
 
-### Rule definition options
-# variantTemplate: only relevant for usage in XBoard/WinBoard GUI [values: fairy, shogi] (default: fairy)
-# pieceToCharTable: mapping of piece characters to images for XBoard/WinBoard GUI (default: -)
+### Additional options relevant for usage in Winboard/XBoard
+# A few options only have the purpose of improving compatibility with Winboard/Xboard.
+# These do not need to be specified when using other GUIs, but can be essential for Winboard/Xboard.
+#
+# variantTemplate: base variant to inherit GUI logic from [values: fairy, shogi, bughouse] (default: fairy)
+# pieceToCharTable: mapping of piece characters to images,
+#                   see https://www.gnu.org/software/xboard/whats_new/4.9.0/index.html#tag-B1 (default: -)
 # pocketSize: number of pockets shown by XBoard/WinBoard for drop variants [int] (default: 0)
+
+### Rule definition options
 # maxRank: maximum rank [Rank] (default: 8)
 # maxFile: maximum file [File] (default: 8)
 # chess960: allow chess960 castling [bool] (default: false)
@@ -432,3 +464,17 @@ startFen = lhaykahl/8/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1
 flagPiece = k
 whiteFlag = *8
 blackFlag = *1
+
+# Ordamirror
+# https://vchess.club/#/variants/Ordamirror
+[ordamirror:chess]
+pieceToCharTable = P...Q..AH.Y.........LKp...q..ah.y.........lk
+centaur = h
+knibis = a
+kniroo = l
+customPiece1 = y:mQcN
+promotionPieceTypes = qh
+startFen = lhaykahl/8/pppppppp/8/8/PPPPPPPP/8/LHAYKAHL w - - 0 1
+flagPiece = k
+whiteFlag = *8
+blackFlag = *1
index dee068a..ea58b1f 100644 (file)
@@ -149,7 +149,7 @@ console.log(board.toVerboseString())
 
 Fen: rnb1kbnr/ppp1pppp/8/3q4/8/8/PPPP1PPP/RNBQKBNR w KQkq - 0 3
 Sfen: rnb1kbnr/ppp1pppp/8/3q4/8/8/PPPP1PPP/RNBQKBNR b - 5
-Key: AE7D48F19DB356CD
+Key: 39B6F80E84D75BFB
 Checkers:
 ```
 
index 68ea9e1..a7ad6c5 100644 (file)
@@ -457,7 +457,7 @@ describe('board.toVerboseString()', function () {
                                                   "   a   b   c   d   e   f   g   h\n\n" +
                                                   "Fen: rnb1kbnr/ppp1pppp/8/3q4/8/8/PPPP1PPP/RNBQKBNR w KQkq - 0 3\n" +
                                                   "Sfen: rnb1kbnr/ppp1pppp/8/3q4/8/8/PPPP1PPP/RNBQKBNR b - 5\n" +
-                                                  "Key: AE7D48F19DB356CD\nCheckers: ")
+                                                  "Key: 39B6F80E84D75BFB\nCheckers: ")
     board.delete();
     const board2 = new ffish.Board("xiangqi");
     chai.expect(board2.toVerboseString()).to.equal("\n +---+---+---+---+---+---+---+---+---+\n" +
@@ -484,7 +484,7 @@ describe('board.toVerboseString()', function () {
                                                    "   a   b   c   d   e   f   g   h   i\n\n" +
                                                    "Fen: rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1\n" +
                                                    "Sfen: rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR b - 1\n" +
-                                                   "Key: 1FBADA178B89E4C3\nCheckers: ");
+                                                   "Key: 7D2D4DD861009613\nCheckers: ");
     board2.delete();
   });
 });
index 252b521..4979416 100755 (executable)
@@ -118,6 +118,7 @@ fi
 # large-board variants
 if [[ $1 == "all" ||  $1 == "largeboard" ]]; then
   expect perft.exp shogi startpos 4 719731 > /dev/null
+  expect perft.exp yarishogi startpos 4 158404 > /dev/null  # configurable pieces
   expect perft.exp capablanca startpos 4 805128 > /dev/null
   expect perft.exp embassy startpos 4 809539 > /dev/null
   expect perft.exp janus startpos 4 772074 > /dev/null