From ebed0bf67c60aa6d7a7dcd558a8d9a4a9c181250 Mon Sep 17 00:00:00 2001 From: Fabian Fichter Date: Sat, 17 Apr 2021 19:01:20 +0200 Subject: [PATCH] Support configurable pieces * 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 --- README.md | 4 +- src/apiutil.h | 71 +++++++++++++++------------ src/bitboard.cpp | 128 ++++++++++++++++++++++++++--------------------- src/bitboard.h | 1 + src/nnue/nnue_common.h | 123 ++++++++++++++++++++++------------------------ src/parser.cpp | 65 ++++++++++++++++++------- src/piece.cpp | 28 ++++++----- src/piece.h | 17 +++--- src/position.cpp | 6 ++ src/position.h | 3 +- src/psqt.cpp | 75 +++++++++++++++++++++++++++- src/types.h | 71 +++++---------------------- src/ucioption.cpp | 2 + src/variant.cpp | 85 ++++++++++++++++++++++++++++++++ src/variant.h | 2 + src/variants.ini | 52 ++++++++++++++++++- tests/js/README.md | 2 +- tests/js/test.js | 4 +- tests/perft.sh | 1 + 19 files changed, 480 insertions(+), 260 deletions(-) diff --git a/README.md b/README.md index cb3c1a3..26940c1 100644 --- 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) diff --git a/src/apiutil.h b/src/apiutil.h index 4472523..7ddcc17 100644 --- a/src/apiutil.h +++ b/src/apiutil.h @@ -15,6 +15,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ + +#ifndef APIUTIL_H_INCLUDED +#define APIUTIL_H_INCLUDED + #include #include #include @@ -23,6 +27,7 @@ #include #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 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(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 get_fen_parts(const std::string& fullFen, char delim) { +inline std::vector get_fen_parts(const std::string& fullFen, char delim) { std::vector fenParts; std::string curPart; std::stringstream ss(fullFen); @@ -455,7 +460,7 @@ std::vector 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& kingPositions) { +inline Validation check_touching_kings(const CharBoard& board, const std::array& 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& castlingInfoSplitted) { +inline Validation fill_castling_info_splitted(const std::string& castlingInfo, std::array& 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& castlingInfoSplitted, const CharBoard& board, const Variant* v) { +inline Validation check_castling_rank(const std::array& castlingInfoSplitted, const CharBoard& board, const Variant* v) { for (Color c : {WHITE, BLACK}) { @@ -606,7 +611,7 @@ Validation check_castling_rank(const std::array& castlingInfoSpl return OK; } -Validation check_standard_castling(std::array& castlingInfoSplitted, const CharBoard& board, +inline Validation check_standard_castling(std::array& castlingInfoSplitted, const CharBoard& board, const std::array& kingPositions, const std::array& kingPositionsStart, const std::array, 2>& rookPositionsStart, const Variant* v) { @@ -640,7 +645,7 @@ Validation check_standard_castling(std::array& 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 diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 2ff2316..cf7a45e 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -73,17 +73,29 @@ namespace { Bitboard JanggiElephantTable[0x5C00]; // To store janggi elephant attacks #endif + // Rider directions + const std::set RookDirectionsV { NORTH, SOUTH}; + const std::set RookDirectionsH { EAST, WEST }; + const std::set BishopDirections { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; + const std::set 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 ElephantDirections { 2 * NORTH_EAST, 2 * SOUTH_EAST, 2 * SOUTH_WEST, 2 * NORTH_WEST }; + const std::set 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 #ifdef PRECOMPUTED_MAGICS - void init_magics(Bitboard table[], Magic magics[], std::vector directions, Bitboard magicsInit[]); + void init_magics(Bitboard table[], Magic magics[], std::set directions, Bitboard magicsInit[]); #else - void init_magics(Bitboard table[], Magic magics[], std::vector directions); + void init_magics(Bitboard table[], Magic magics[], std::set directions); #endif template - Bitboard sliding_attack(std::vector directions, Square sq, Bitboard occupied, Color c = WHITE) { + Bitboard sliding_attack(std::set 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 directions, Square s) { + Bitboard lame_leaper_path(std::set directions, Square s) { Bitboard b = 0; for (Direction d : directions) b |= lame_leaper_path(d, s); return b; } - Bitboard lame_leaper_attack(std::vector directions, Square s, Bitboard occupied) { + Bitboard lame_leaper_attack(std::set 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 RookDirectionsV = { NORTH, SOUTH}; - std::vector RookDirectionsH = { EAST, WEST }; - std::vector BishopDirections = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; - std::vector 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 ElephantDirections = { 2 * NORTH_EAST, 2 * SOUTH_EAST, 2 * SOUTH_WEST, 2 * NORTH_WEST }; - std::vector 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(s1, s2), distance(s1, s2)); - -#ifdef PRECOMPUTED_MAGICS - init_magics(RookTableH, RookMagicsH, RookDirectionsH, RookMagicHInit); - init_magics(RookTableV, RookMagicsV, RookDirectionsV, RookMagicVInit); - init_magics(BishopTable, BishopMagics, BishopDirections, BishopMagicInit); - init_magics(CannonTableH, CannonMagicsH, RookDirectionsH, CannonMagicHInit); - init_magics(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit); - init_magics(HorseTable, HorseMagics, HorseDirections, HorseMagicInit); - init_magics(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit); - init_magics(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections, JanggiElephantMagicInit); -#else - init_magics(RookTableH, RookMagicsH, RookDirectionsH); - init_magics(RookTableV, RookMagicsV, RookDirectionsV); - init_magics(BishopTable, BishopMagics, BishopDirections); - init_magics(CannonTableH, CannonMagicsH, RookDirectionsH); - init_magics(CannonTableV, CannonMagicsV, RookDirectionsV); - init_magics(HorseTable, HorseMagics, HorseDirections); - init_magics(ElephantTable, ElephantMagics, ElephantDirections); - init_magics(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(s1, s2), distance(s1, s2)); + +#ifdef PRECOMPUTED_MAGICS + init_magics(RookTableH, RookMagicsH, RookDirectionsH, RookMagicHInit); + init_magics(RookTableV, RookMagicsV, RookDirectionsV, RookMagicVInit); + init_magics(BishopTable, BishopMagics, BishopDirections, BishopMagicInit); + init_magics(CannonTableH, CannonMagicsH, RookDirectionsH, CannonMagicHInit); + init_magics(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit); + init_magics(HorseTable, HorseMagics, HorseDirections, HorseMagicInit); + init_magics(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit); + init_magics(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections, JanggiElephantMagicInit); +#else + init_magics(RookTableH, RookMagicsH, RookDirectionsH); + init_magics(RookTableV, RookMagicsV, RookDirectionsV); + init_magics(BishopTable, BishopMagics, BishopDirections); + init_magics(CannonTableH, CannonMagicsH, RookDirectionsH); + init_magics(CannonTableV, CannonMagicsV, RookDirectionsV); + init_magics(HorseTable, HorseMagics, HorseDirections); + init_magics(ElephantTable, ElephantMagics, ElephantDirections); + init_magics(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections); +#endif + + init_pieces(); for (Square s1 = SQ_A1; s1 <= SQ_MAX; ++s1) { @@ -338,9 +352,9 @@ namespace { template #ifdef PRECOMPUTED_MAGICS - void init_magics(Bitboard table[], Magic magics[], std::vector directions, Bitboard magicsInit[]) { + void init_magics(Bitboard table[], Magic magics[], std::set directions, Bitboard magicsInit[]) { #else - void init_magics(Bitboard table[], Magic magics[], std::vector directions) { + void init_magics(Bitboard table[], Magic magics[], std::set directions) { #endif // Optimal PRNG seeds to pick the correct magics in the shortest time diff --git a/src/bitboard.h b/src/bitboard.h index 5672dcd..dd3d2c7 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -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); diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index f4b87af..9163b4a 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -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); diff --git a/src/parser.cpp b/src/parser.cpp index 7d53842..ed1762f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -19,6 +19,7 @@ #include #include +#include "apiutil.h" #include "parser.h" #include "piece.h" #include "types.h" @@ -183,6 +184,47 @@ Variant* VariantParser::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::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; diff --git a/src/piece.cpp b/src/piece.cpp index c750be3..584d17d 100644 --- a/src/piece.cpp +++ b/src/piece.cpp @@ -129,28 +129,28 @@ namespace { // Add moves for (char mt : moveTypes) { - std::vector& v = hopper ? (mt == 'c' ? p->hopperCapture : p->hopperQuiet) - : rider ? (mt == 'c' ? p->sliderCapture : p->sliderQuiet) - : (mt == 'c' ? p->stepsCapture : p->stepsQuiet); + std::set& 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) { diff --git a/src/piece.h b/src/piece.h index 66fa36f..2ce4a7e 100644 --- a/src/piece.h +++ b/src/piece.h @@ -21,9 +21,10 @@ #include #include -#include +#include #include "types.h" +#include "variant.h" /// PieceInfo struct stores information about the piece movements. @@ -31,17 +32,17 @@ struct PieceInfo { std::string name = ""; std::string betza = ""; - std::vector stepsQuiet = {}; - std::vector stepsCapture = {}; - std::vector sliderQuiet = {}; - std::vector sliderCapture = {}; - std::vector hopperQuiet = {}; - std::vector hopperCapture = {}; + std::set stepsQuiet = {}; + std::set stepsCapture = {}; + std::set sliderQuiet = {}; + std::set sliderCapture = {}; + std::set hopperQuiet = {}; + std::set hopperCapture = {}; bool lameLeaper = false; }; struct PieceMap : public std::map { - void init(); + void init(const Variant* v = nullptr); void add(PieceType pt, const PieceInfo* v); void clear_all(); }; diff --git a/src/position.cpp b/src/position.cpp index 7b63851..4504b55 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -527,8 +527,14 @@ void Position::set_check_info(StateInfo* si) const { Square ksq = count(~sideToMove) ? square(~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; diff --git a/src/position.h b/src/position.h index d67b99a..8bd3623 100644 --- a/src/position.h +++ b/src/position.h @@ -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 { diff --git a/src/psqt.cpp b/src/psqt.cpp index 2d52b7d..5b7f3a5 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -32,6 +32,46 @@ 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; diff --git a/src/types.h b/src/types.h index 503e340..10f133d 100644 --- a/src/types.h +++ b/src/types.h @@ -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) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 2aa2fbe..c30654b 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -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) { diff --git a/src/variant.cpp b/src/variant.cpp index e41f6f5..0cad420 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -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 diff --git a/src/variant.h b/src/variant.h index 14f74c8..dd03214 100644 --- a/src/variant.h +++ b/src/variant.h @@ -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 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, ' '); diff --git a/src/variants.ini b/src/variants.ini index f5e966e..437343e 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -87,6 +87,32 @@ # 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] @@ -99,10 +125,16 @@ # [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 diff --git a/tests/js/README.md b/tests/js/README.md index dee068a..ea58b1f 100644 --- a/tests/js/README.md +++ b/tests/js/README.md @@ -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: ``` diff --git a/tests/js/test.js b/tests/js/test.js index 68ea9e1..a7ad6c5 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -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(); }); }); diff --git a/tests/perft.sh b/tests/perft.sh index 252b521..4979416 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -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 -- 1.7.0.4