No functional change.
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef APIUTIL_H_INCLUDED
+#define APIUTIL_H_INCLUDED
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <cctype>
+#include <iostream>
+#include <math.h>
+
+#include "types.h"
+#include "position.h"
+#include "variant.h"
+
++namespace Stockfish {
+
+enum Notation {
+ NOTATION_DEFAULT,
+ // https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
+ NOTATION_SAN,
+ NOTATION_LAN,
+ // https://en.wikipedia.org/wiki/Shogi_notation#Western_notation
+ NOTATION_SHOGI_HOSKING, // Examples: P76, S’34
+ NOTATION_SHOGI_HODGES, // Examples: P-7f, S*3d
+ NOTATION_SHOGI_HODGES_NUMBER, // Examples: P-76, S*34
+ // http://www.janggi.pl/janggi-notation/
+ NOTATION_JANGGI,
+ // https://en.wikipedia.org/wiki/Xiangqi#Notation
+ NOTATION_XIANGQI_WXF,
+};
+
+inline Notation default_notation(const Variant* v) {
+ if (v->variantTemplate == "shogi")
+ return NOTATION_SHOGI_HODGES_NUMBER;
+ return NOTATION_SAN;
+}
+
+namespace SAN {
+
+enum Disambiguation {
+ NO_DISAMBIGUATION,
+ FILE_DISAMBIGUATION,
+ RANK_DISAMBIGUATION,
+ SQUARE_DISAMBIGUATION,
+};
+
+inline bool is_shogi(Notation n) {
+ return n == NOTATION_SHOGI_HOSKING || n == NOTATION_SHOGI_HODGES || n == NOTATION_SHOGI_HODGES_NUMBER;
+}
+
+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);
+ PieceType pt = type_of(pc);
+ // Quiet pawn moves
+ if ((n == NOTATION_SAN || n == NOTATION_LAN) && type_of(pc) == PAWN && type_of(m) != DROP)
+ return "";
+ // Tandem pawns
+ else if (n == NOTATION_XIANGQI_WXF && popcount(pos.pieces(us, pt) & file_bb(from)) > 2)
+ return std::to_string(popcount(forward_file_bb(us, from) & pos.pieces(us, pt)) + 1);
+ // Moves of promoted pieces
+ else if (is_shogi(n) && type_of(m) != DROP && pos.unpromoted_piece_on(from))
+ return "+" + std::string(1, toupper(pos.piece_to_char()[pos.unpromoted_piece_on(from)]));
+ // Promoted drops
+ else if (is_shogi(n) && type_of(m) == DROP && dropped_piece_type(m) != in_hand_piece_type(m))
+ return "+" + std::string(1, toupper(pos.piece_to_char()[in_hand_piece_type(m)]));
+ else if (pos.piece_to_char_synonyms()[pc] != ' ')
+ return std::string(1, toupper(pos.piece_to_char_synonyms()[pc]));
+ else
+ return std::string(1, toupper(pos.piece_to_char()[pc]));
+}
+
+inline std::string file(const Position& pos, Square s, Notation n) {
+ switch (n)
+ {
+ case NOTATION_SHOGI_HOSKING:
+ case NOTATION_SHOGI_HODGES:
+ case NOTATION_SHOGI_HODGES_NUMBER:
+ return std::to_string(pos.max_file() - file_of(s) + 1);
+ case NOTATION_JANGGI:
+ return std::to_string(file_of(s) + 1);
+ case NOTATION_XIANGQI_WXF:
+ return std::to_string((pos.side_to_move() == WHITE ? pos.max_file() - file_of(s) : file_of(s)) + 1);
+ default:
+ return std::string(1, char('a' + file_of(s)));
+ }
+}
+
+inline std::string rank(const Position& pos, Square s, Notation n) {
+ switch (n)
+ {
+ case NOTATION_SHOGI_HOSKING:
+ case NOTATION_SHOGI_HODGES_NUMBER:
+ return std::to_string(pos.max_rank() - rank_of(s) + 1);
+ case NOTATION_SHOGI_HODGES:
+ return std::string(1, char('a' + pos.max_rank() - rank_of(s)));
+ case NOTATION_JANGGI:
+ return std::to_string((pos.max_rank() - rank_of(s) + 1) % 10);
+ case NOTATION_XIANGQI_WXF:
+ {
+ if (pos.empty(s))
+ return std::to_string(relative_rank(pos.side_to_move(), s, pos.max_rank()) + 1);
+ else if (pos.pieces(pos.side_to_move(), type_of(pos.piece_on(s))) & forward_file_bb(pos.side_to_move(), s))
+ return "-";
+ else
+ return "+";
+ }
+ default:
+ return std::to_string(rank_of(s) + 1);
+ }
+}
+
+inline std::string square(const Position& pos, Square s, Notation n) {
+ switch (n)
+ {
+ case NOTATION_JANGGI:
+ return rank(pos, s, n) + file(pos, s, n);
+ default:
+ return file(pos, s, n) + rank(pos, s, n);
+ }
+}
+
+inline Disambiguation disambiguation_level(const Position& pos, Move m, Notation n) {
+ // Drops never need disambiguation
+ if (type_of(m) == DROP)
+ return NO_DISAMBIGUATION;
+
+ // NOTATION_LAN and Janggi always use disambiguation
+ if (n == NOTATION_LAN || n == NOTATION_JANGGI)
+ return SQUARE_DISAMBIGUATION;
+
+ Color us = pos.side_to_move();
+ Square from = from_sq(m);
+ Square to = to_sq(m);
+ Piece pc = pos.moved_piece(m);
+ PieceType pt = type_of(pc);
+
+ // Xiangqi uses either file disambiguation or +/- if two pieces on file
+ if (n == NOTATION_XIANGQI_WXF)
+ {
+ // Disambiguate by rank (+/-) if target square of other piece is valid
+ if (popcount(pos.pieces(us, pt) & file_bb(from)) == 2)
+ {
+ Square otherFrom = lsb((pos.pieces(us, pt) & file_bb(from)) ^ from);
+ Square otherTo = otherFrom + Direction(to) - Direction(from);
+ if (is_ok(otherTo) && (pos.board_bb(us, pt) & otherTo))
+ return RANK_DISAMBIGUATION;
+ }
+ return FILE_DISAMBIGUATION;
+ }
+
+ // Pawn captures always use disambiguation
+ if (n == NOTATION_SAN && pt == PAWN)
+ {
+ if (pos.capture(m))
+ return FILE_DISAMBIGUATION;
+ if (type_of(m) == PROMOTION && from != to && pos.sittuyin_promotion())
+ return SQUARE_DISAMBIGUATION;
+ }
+
+ // A disambiguation occurs if we have more then one piece of type 'pt'
+ // that can reach 'to' with a legal move.
+ Bitboard b = pos.pieces(us, pt) ^ from;
+ Bitboard others = 0;
+
+ while (b)
+ {
+ Square s = pop_lsb(&b);
+ if ( pos.pseudo_legal(make_move(s, to))
+ && pos.legal(make_move(s, to))
+ && !(is_shogi(n) && pos.unpromoted_piece_on(s) != pos.unpromoted_piece_on(from)))
+ others |= s;
+ }
+
+ if (!others)
+ return NO_DISAMBIGUATION;
+ else if (is_shogi(n))
+ return SQUARE_DISAMBIGUATION;
+ else if (!(others & file_bb(from)))
+ return FILE_DISAMBIGUATION;
+ else if (!(others & rank_bb(from)))
+ return RANK_DISAMBIGUATION;
+ else
+ return SQUARE_DISAMBIGUATION;
+}
+
+inline std::string disambiguation(const Position& pos, Square s, Notation n, Disambiguation d) {
+ switch (d)
+ {
+ case FILE_DISAMBIGUATION:
+ return file(pos, s, n);
+ case RANK_DISAMBIGUATION:
+ return rank(pos, s, n);
+ case SQUARE_DISAMBIGUATION:
+ return square(pos, s, n);
+ default:
+ assert(d == NO_DISAMBIGUATION);
+ return "";
+ }
+}
+
+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);
+ Square to = to_sq(m);
+
+ if (type_of(m) == CASTLING)
+ {
+ san = to > from ? "O-O" : "O-O-O";
+
+ if (is_gating(m))
+ {
+ san += std::string("/") + pos.piece_to_char()[make_piece(WHITE, gating_type(m))];
+ san += square(pos, gating_square(m), n);
+ }
+ }
+ else
+ {
+ // Piece
+ san += piece(pos, m, n);
+
+ // Origin square, disambiguation
+ Disambiguation d = disambiguation_level(pos, m, n);
+ san += disambiguation(pos, from, n, d);
+
+ // Separator/Operator
+ if (type_of(m) == DROP)
+ san += n == NOTATION_SHOGI_HOSKING ? '\'' : is_shogi(n) ? '*' : '@';
+ else if (n == NOTATION_XIANGQI_WXF)
+ {
+ if (rank_of(from) == rank_of(to))
+ san += '=';
+ else if (relative_rank(us, to, pos.max_rank()) > relative_rank(us, from, pos.max_rank()))
+ san += '+';
+ else
+ san += '-';
+ }
+ else if (pos.capture(m))
+ san += 'x';
+ else if (n == NOTATION_LAN || (is_shogi(n) && (n != NOTATION_SHOGI_HOSKING || d == SQUARE_DISAMBIGUATION)) || n == NOTATION_JANGGI)
+ san += '-';
+
+ // Destination square
+ if (n == NOTATION_XIANGQI_WXF && type_of(m) != DROP)
+ san += file_of(to) == file_of(from) ? std::to_string(std::abs(rank_of(to) - rank_of(from))) : file(pos, to, n);
+ else
+ san += square(pos, to, n);
+
+ // Suffix
+ if (type_of(m) == PROMOTION)
+ san += std::string("=") + pos.piece_to_char()[make_piece(WHITE, promotion_type(m))];
+ else if (type_of(m) == PIECE_PROMOTION)
+ san += is_shogi(n) ? std::string("+") : std::string("=") + pos.piece_to_char()[make_piece(WHITE, pos.promoted_piece_type(type_of(pos.moved_piece(m))))];
+ else if (type_of(m) == PIECE_DEMOTION)
+ san += is_shogi(n) ? std::string("-") : std::string("=") + std::string(1, pos.piece_to_char()[pos.unpromoted_piece_on(from)]);
+ else if (type_of(m) == NORMAL && is_shogi(n) && pos.pseudo_legal(make<PIECE_PROMOTION>(from, to)))
+ san += std::string("=");
+ if (is_gating(m))
+ san += std::string("/") + pos.piece_to_char()[make_piece(WHITE, gating_type(m))];
+ }
+
+ // Check and checkmate
+ if (pos.gives_check(m) && !is_shogi(n))
+ {
+ StateInfo st;
+ pos.do_move(m, st);
+ san += MoveList<LEGAL>(pos).size() ? "+" : "#";
+ pos.undo_move(m);
+ }
+
+ return san;
+}
+
+} // namespace SAN
+
+inline bool has_insufficient_material(Color c, const Position& pos) {
+
+ // Other win rules
+ if ( pos.captures_to_hand()
+ || pos.count_in_hand(c, ALL_PIECES)
+ || pos.extinction_value() != VALUE_NONE
+ || (pos.capture_the_flag_piece() && pos.count(c, pos.capture_the_flag_piece())))
+ return false;
+
+ // Restricted pieces
+ Bitboard restricted = pos.pieces(~c, KING);
+ for (PieceType pt : pos.piece_types())
+ if (pt == KING || !(pos.board_bb(c, pt) & pos.board_bb(~c, KING)))
+ restricted |= pos.pieces(c, pt);
+ else if (is_custom(pt) && pos.count(c, pt) > 0)
+ // to be conservative, assume any custom piece has mating potential
+ return false;
+
+ // Mating pieces
+ for (PieceType pt : { ROOK, QUEEN, ARCHBISHOP, CHANCELLOR, SILVER, GOLD, COMMONER, CENTAUR })
+ if ((pos.pieces(c, pt) & ~restricted) || (pos.count(c, PAWN) && pos.promotion_piece_types().find(pt) != pos.promotion_piece_types().end()))
+ return false;
+
+ // Color-bound pieces
+ Bitboard colorbound = 0, unbound;
+ for (PieceType pt : { BISHOP, FERS, FERS_ALFIL, ALFIL, ELEPHANT })
+ colorbound |= pos.pieces(pt) & ~restricted;
+ unbound = pos.pieces() ^ restricted ^ colorbound;
+ if ((colorbound & pos.pieces(c)) && (((DarkSquares & colorbound) && (~DarkSquares & colorbound)) || unbound || pos.stalemate_value() != VALUE_DRAW || pos.check_counting()))
+ return false;
+
+ // Unbound pieces require one helper piece of either color
+ if ((pos.pieces(c) & unbound) && (popcount(pos.pieces() ^ restricted) >= 2 || pos.stalemate_value() != VALUE_DRAW || pos.check_counting()))
+ return false;
+
+ return true;
+}
+
+namespace FEN {
+
+enum FenValidation : int {
+ FEN_INVALID_COUNTING_RULE = -14,
+ FEN_INVALID_CHECK_COUNT = -13,
+ FEN_INVALID_NB_PARTS = -11,
+ FEN_INVALID_CHAR = -10,
+ FEN_TOUCHING_KINGS = -9,
+ FEN_INVALID_BOARD_GEOMETRY = -8,
+ FEN_INVALID_POCKET_INFO = -7,
+ FEN_INVALID_SIDE_TO_MOVE = -6,
+ FEN_INVALID_CASTLING_INFO = -5,
+ FEN_INVALID_EN_PASSANT_SQ = -4,
+ FEN_INVALID_NUMBER_OF_KINGS = -3,
+ FEN_INVALID_HALF_MOVE_COUNTER = -2,
+ FEN_INVALID_MOVE_COUNTER = -1,
+ FEN_EMPTY = 0,
+ FEN_OK = 1
+};
+enum Validation : int {
+ NOK,
+ OK
+};
+
+struct CharSquare {
+ int rowIdx;
+ int fileIdx;
+ CharSquare() : rowIdx(-1), fileIdx(-1) {}
+ CharSquare(int row, int file) : rowIdx(row), fileIdx(file) {}
+};
+
+inline bool operator==(const CharSquare& s1, const CharSquare& s2) {
+ return s1.rowIdx == s2.rowIdx && s1.fileIdx == s2.fileIdx;
+}
+
+inline bool operator!=(const CharSquare& s1, const CharSquare& s2) {
+ return !(s1 == 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);
+}
+
+class CharBoard {
+private:
+ int nbRanks;
+ int nbFiles;
+ std::vector<char> board; // fill an array where the pieces are for later geometry checks
+public:
+ CharBoard(int ranks, int files) : nbRanks(ranks), nbFiles(files) {
+ assert(nbFiles > 0 && nbRanks > 0);
+ board = std::vector<char>(nbRanks * nbFiles, ' ');
+ }
+ void set_piece(int rankIdx, int fileIdx, char c) {
+ board[rankIdx * nbFiles + fileIdx] = c;
+ }
+ char get_piece(int rowIdx, int fileIdx) const {
+ return board[rowIdx * nbFiles + fileIdx];
+ }
+ int get_nb_ranks() const {
+ return nbRanks;
+ }
+ int get_nb_files() const {
+ return nbFiles;
+ }
+ /// Returns the square of a given character
+ CharSquare get_square_for_piece(char piece) const {
+ CharSquare s;
+ for (int r = 0; r < nbRanks; ++r)
+ {
+ for (int c = 0; c < nbFiles; ++c)
+ {
+ if (get_piece(r, c) == piece)
+ {
+ s.rowIdx = r;
+ s.fileIdx = c;
+ return s;
+ }
+ }
+ }
+ return s;
+ }
+ /// Returns all square positions for a given piece
+ std::vector<CharSquare> get_squares_for_piece(char piece) const {
+ std::vector<CharSquare> squares;
+ for (int r = 0; r < nbRanks; ++r)
+ for (int c = 0; c < nbFiles; ++c)
+ if (get_piece(r, c) == piece)
+ squares.emplace_back(CharSquare(r, c));
+ return squares;
+ }
+ /// Checks if a given character is on a given rank index
+ bool is_piece_on_rank(char piece, int rowIdx) const {
+ for (int f = 0; f < nbFiles; ++f)
+ if (get_piece(rowIdx, f) == piece)
+ return true;
+ return false;
+ }
+ friend 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)
+ os << "[" << board.get_piece(r, c) << "] ";
+ os << std::endl;
+ }
+ return os;
+}
+
+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)
+ {
+ std::cerr << "Invalid piece character: '" << c << "'." << std::endl;
+ return NOK;
+ }
+ }
+ return OK;
+}
+
+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);
+ while (std::getline(ss, curPart, delim))
+ fenParts.emplace_back(curPart);
+ return fenParts;
+}
+
+/// fills the character board according to a given FEN string
+inline Validation fill_char_board(CharBoard& board, const std::string& fenBoard, const std::string& validSpecialCharacters, const Variant* v) {
+ int rankIdx = 0;
+ int fileIdx = 0;
+
+ char prevChar = '?';
+ for (char c : fenBoard)
+ {
+ if (c == ' ' || c == '[')
+ break;
+ if (isdigit(c))
+ {
+ fileIdx += c - '0';
+ // if we have multiple digits attached we can add multiples of 9 to compute the resulting number (e.g. -> 21 = 2 + 2 * 9 + 1)
+ if (isdigit(prevChar))
+ fileIdx += 9 * (prevChar - '0');
+ }
+ else if (c == '/')
+ {
+ ++rankIdx;
+ if (fileIdx != board.get_nb_files())
+ {
+ std::cerr << "curRankWidth != nbFiles: " << fileIdx << " != " << board.get_nb_files() << std::endl;
+ return NOK;
+ }
+ if (rankIdx == board.get_nb_ranks())
+ break;
+ fileIdx = 0;
+ }
+ else if (validSpecialCharacters.find(c) == std::string::npos)
+ { // normal piece
+ if (fileIdx == board.get_nb_files())
+ {
+ std::cerr << "File index: " << fileIdx << " for piece '" << c << "' exceeds maximum of allowed number of files: " << board.get_nb_files() << "." << std::endl;
+ return NOK;
+ }
+ board.set_piece(v->maxRank-rankIdx, fileIdx, c); // we mirror the rank index because the black pieces are given first in the FEN
+ ++fileIdx;
+ }
+ prevChar = c;
+ }
+
+ if (v->pieceDrops)
+ { // pockets can either be defined by [] or /
+ if (rankIdx+1 != board.get_nb_ranks() && rankIdx != board.get_nb_ranks())
+ {
+ std::cerr << "Invalid number of ranks. Expected: " << board.get_nb_ranks() << " Actual: " << rankIdx+1 << std::endl;
+ return NOK;
+ }
+ }
+ else
+ {
+ if (rankIdx+1 != board.get_nb_ranks())
+ {
+ std::cerr << "Invalid number of ranks. Expected: " << board.get_nb_ranks() << " Actual: " << rankIdx+1 << std::endl;
+ return NOK;
+ }
+ }
+ return OK;
+}
+
+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;
+ std::cerr << board << std::endl;
+ return NOK;
+ }
+ return OK;
+}
+
+inline Validation fill_castling_info_splitted(const std::string& castlingInfo, std::array<std::string, 2>& castlingInfoSplitted) {
+ for (char c : castlingInfo)
+ {
+ if (c != '-')
+ {
+ if (!isalpha(c))
+ {
+ std::cerr << "Invalid castling specification: '" << c << "'." << std::endl;
+ return NOK;
+ }
+ else if (isupper(c))
+ castlingInfoSplitted[WHITE] += tolower(c);
+ else
+ castlingInfoSplitted[BLACK] += c;
+ }
+ }
+ return OK;
+}
+
+inline std::string color_to_string(Color c) {
+ switch (c)
+ {
+ case WHITE:
+ return "WHITE";
+ case BLACK:
+ return "BLACK";
+ case COLOR_NB:
+ return "COLOR_NB";
+ default:
+ return "INVALID_COLOR";
+ }
+}
+
+inline std::string castling_rights_to_string(CastlingRights castlingRights) {
+ switch (castlingRights)
+ {
+ case KING_SIDE:
+ return "KING_SIDE";
+ case QUEEN_SIDE:
+ return "QUEENS_SIDE";
+ case WHITE_OO:
+ return "WHITE_OO";
+ case WHITE_OOO:
+ return "WHITE_OOO";
+ case BLACK_OO:
+ return "BLACK_OO";
+ case BLACK_OOO:
+ return "BLACK_OOO";
+ case WHITE_CASTLING:
+ return "WHITE_CASTLING";
+ case BLACK_CASTLING:
+ return "BLACK_CASTLING";
+ case ANY_CASTLING:
+ return "ANY_CASTLING";
+ case CASTLING_RIGHT_NB:
+ return "CASTLING_RIGHT_NB";
+ default:
+ return "INVALID_CASTLING_RIGHTS";
+ }
+}
+
+inline Validation check_castling_rank(const std::array<std::string, 2>& castlingInfoSplitted, const CharBoard& board, const Variant* v) {
+
+ for (Color c : {WHITE, BLACK})
+ {
+ for (char charPiece : {v->pieceToChar[make_piece(c, v->castlingKingPiece)],
+ v->pieceToChar[make_piece(c, v->castlingRookPiece)]})
+ {
+ if (castlingInfoSplitted[c].size() == 0)
+ continue;
+ const Rank castlingRank = relative_rank(c, v->castlingRank, v->maxRank);
+ if (!board.is_piece_on_rank(charPiece, castlingRank))
+ {
+ std::cerr << "The " << color_to_string(c) << " king and rook must be on rank " << castlingRank << " if castling is enabled for " << color_to_string(c) << "." << std::endl;
+ return NOK;
+ }
+ }
+ }
+ return OK;
+}
+
+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) {
+
+ for (Color c : {WHITE, BLACK})
+ {
+ if (castlingInfoSplitted[c].size() == 0)
+ continue;
+ if (kingPositions[c] != kingPositionsStart[c])
+ {
+ std::cerr << "The " << color_to_string(c) << " KING has moved. Castling is no longer valid for " << color_to_string(c) << "." << std::endl;
+ return NOK;
+ }
+
+ for (CastlingRights castling: {KING_SIDE, QUEEN_SIDE})
+ {
+ CharSquare rookStartingSquare = castling == QUEEN_SIDE ? rookPositionsStart[c][0] : rookPositionsStart[c][1];
+ char targetChar = castling == QUEEN_SIDE ? 'q' : 'k';
+ char rookChar = v->pieceToChar[make_piece(c, v->castlingRookPiece)];
+ if (castlingInfoSplitted[c].find(targetChar) != std::string::npos)
+ {
+ if (board.get_piece(rookStartingSquare.rowIdx, rookStartingSquare.fileIdx) != rookChar)
+ {
+ std::cerr << "The " << color_to_string(c) << " ROOK on the "<< castling_rights_to_string(castling) << " has moved. "
+ << castling_rights_to_string(castling) << " castling is no longer valid for " << color_to_string(c) << "." << std::endl;
+ return NOK;
+ }
+ }
+
+ }
+ }
+ return OK;
+}
+
+inline Validation check_pocket_info(const std::string& fenBoard, int nbRanks, const Variant* v, std::string& pocket) {
+
+ char stopChar;
+ int offset = 0;
+ if (std::count(fenBoard.begin(), fenBoard.end(), '/') == nbRanks)
+ {
+ // look for last '/'
+ stopChar = '/';
+ }
+ else if (std::count(fenBoard.begin(), fenBoard.end(), '[') == 1)
+ {
+ // pocket is defined as [ and ]
+ stopChar = '[';
+ offset = 1;
+ if (*(fenBoard.end()-1) != ']')
+ {
+ std::cerr << "Pocket specification does not end with ']'." << std::endl;
+ return NOK;
+ }
+ }
+ else
+ // allow to skip pocket
+ return OK;
+
+ // look for last '/'
+ for (auto it = fenBoard.rbegin()+offset; it != fenBoard.rend(); ++it)
+ {
+ const char c = *it;
+ if (c == stopChar)
+ return OK;
+ if (c != '-')
+ {
+ if (v->pieceToChar.find(c) == std::string::npos && v->pieceToCharSynonyms.find(c) == std::string::npos)
+ {
+ std::cerr << "Invalid pocket piece: '" << c << "'." << std::endl;
+ return NOK;
+ }
+ else
+ pocket += c;
+ }
+ }
+ std::cerr << "Pocket piece closing character '" << stopChar << "' was not found." << std::endl;
+ return NOK;
+}
+
+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)]);
+}
+
+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);
+ int nbBlackKingsStart = piece_count(startFenBoard, BLACK, KING, v);
+
+ if (nbWhiteKings != nbWhiteKingsStart)
+ {
+ std::cerr << "Invalid number of white kings. Expected: " << nbWhiteKingsStart << ". Given: " << nbWhiteKings << std::endl;
+ return NOK;
+ }
+ if (nbBlackKings != nbBlackKingsStart)
+ {
+ std::cerr << "Invalid number of black kings. Expected: " << nbBlackKingsStart << ". Given: " << nbBlackKings << std::endl;
+ return NOK;
+ }
+ return OK;
+}
+
+
+inline Validation check_en_passant_square(const std::string& enPassantInfo) {
+ if (enPassantInfo.size() != 1 || enPassantInfo[0] != '-')
+ {
+ if (enPassantInfo.size() != 2)
+ {
+ std::cerr << "Invalid en-passant square '" << enPassantInfo << "'. Expects 2 characters. Actual: " << enPassantInfo.size() << " character(s)." << std::endl;
+ return NOK;
+ }
+ if (!isalpha(enPassantInfo[0]))
+ {
+ std::cerr << "Invalid en-passant square '" << enPassantInfo << "'. Expects 1st character to be a letter." << std::endl;
+ return NOK;
+ }
+ if (!isdigit(enPassantInfo[1]))
+ {
+ std::cerr << "Invalid en-passant square '" << enPassantInfo << "'. Expects 2nd character to be a digit." << std::endl;
+ return NOK;
+ }
+ }
+ return OK;
+}
+
+
+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;
+ return NOK;
+ }
+ if (!isdigit(checkCountInfo[0]))
+ {
+ std::cerr << "Invalid check count '" << checkCountInfo << "'. Expects 1st character to be a digit." << std::endl;
+ return NOK;
+ }
+ if (!isdigit(checkCountInfo[2])) {
+ std::cerr << "Invalid check count '" << checkCountInfo << "'. Expects 3rd character to be a digit." << std::endl;
+ return NOK;
+ }
+ return OK;
+}
+
+
+inline Validation check_digit_field(const std::string& field) {
+ if (field.size() == 1 && field[0] == '-')
+ return OK;
+ for (char c : field)
+ if (!isdigit(c))
+ return NOK;
+ return OK;
+}
+
+
+inline FenValidation validate_fen(const std::string& fen, const Variant* v, bool chess960 = false) {
+
+ const std::string validSpecialCharacters = "/+~[]-";
+ // 0) Layout
+ // check for empty fen
+ if (fen.size() == 0)
+ {
+ std::cerr << "Fen is empty." << std::endl;
+ return FEN_EMPTY;
+ }
+
+ std::vector<std::string> fenParts = get_fen_parts(fen, ' ');
+ std::vector<std::string> starFenParts = get_fen_parts(v->startFen, ' ');
+
+ // check for number of parts
+ const unsigned int maxNumberFenParts = 6 + v->checkCounting;
+ if (fenParts.size() < 1 || fenParts.size() > maxNumberFenParts)
+ {
+ std::cerr << "Invalid number of fen parts. Expected: >= 1 and <= " << maxNumberFenParts
+ << " Actual: " << fenParts.size() << std::endl;
+ return FEN_INVALID_NB_PARTS;
+ }
+
+ // 1) Part
+ // check for valid characters
+ if (check_for_valid_characters(fenParts[0], validSpecialCharacters, v) == NOK)
+ return FEN_INVALID_CHAR;
+
+ // check for number of ranks
+ const int nbRanks = v->maxRank + 1;
+ // check for number of files
+ const int nbFiles = v->maxFile + 1;
+ CharBoard board(nbRanks, nbFiles); // create a 2D character board for later geometry checks
+
+ if (fill_char_board(board, fenParts[0], validSpecialCharacters, v) == NOK)
+ return FEN_INVALID_BOARD_GEOMETRY;
+
+ // check for pocket
+ std::string pocket = "";
+ if (v->pieceDrops || v->seirawanGating || v->arrowGating)
+ {
+ if (check_pocket_info(fenParts[0], nbRanks, v, pocket) == NOK)
+ return FEN_INVALID_POCKET_INFO;
+ }
+
+ // check for number of kings
+ if (v->pieceTypes.find(KING) != v->pieceTypes.end())
+ {
+ // we have a royal king in this variant,
+ // ensure that each side has exactly as many kings as in the starting position
+ // (variants like giveaway use the COMMONER piece type instead)
+ if (check_number_of_kings(fenParts[0], starFenParts[0], v) == NOK)
+ return FEN_INVALID_NUMBER_OF_KINGS;
+
+ // check for touching kings if there are exactly two royal kings on the board (excluding pocket)
+ if ( v->kingType == KING
+ && piece_count(fenParts[0], WHITE, KING, v) - piece_count(pocket, WHITE, KING, v) == 1
+ && piece_count(fenParts[0], BLACK, KING, v) - piece_count(pocket, BLACK, KING, v) == 1)
+ {
+ std::array<CharSquare, 2> kingPositions;
+ kingPositions[WHITE] = board.get_square_for_piece(v->pieceToChar[make_piece(WHITE, KING)]);
+ kingPositions[BLACK] = board.get_square_for_piece(v->pieceToChar[make_piece(BLACK, KING)]);
+ if (check_touching_kings(board, kingPositions) == NOK)
+ return FEN_TOUCHING_KINGS;
+ }
+ }
+
+ // 2) Part
+ // check side to move char
+ if (fenParts.size() >= 2 && fenParts[1][0] != 'w' && fenParts[1][0] != 'b')
+ {
+ std::cerr << "Invalid side to move specification: '" << fenParts[1][0] << "'." << std::endl;
+ return FEN_INVALID_SIDE_TO_MOVE;
+ }
+
+ // Castling and en passant can be skipped
+ bool skipCastlingAndEp = fenParts.size() >= 4 && fenParts.size() <= 5 && isdigit(fenParts[2][0]);
+
+ // 3) Part
+ // check castling rights
+ if (fenParts.size() >= 3 && !skipCastlingAndEp && v->castling)
+ {
+ std::array<std::string, 2> castlingInfoSplitted;
+ if (fill_castling_info_splitted(fenParts[2], castlingInfoSplitted) == NOK)
+ return FEN_INVALID_CASTLING_INFO;
+
+ if (castlingInfoSplitted[WHITE].size() != 0 || castlingInfoSplitted[BLACK].size() != 0)
+ {
+ std::array<CharSquare, 2> kingPositions;
+ kingPositions[WHITE] = board.get_square_for_piece(toupper(v->pieceToChar[v->castlingKingPiece]));
+ kingPositions[BLACK] = board.get_square_for_piece(tolower(v->pieceToChar[v->castlingKingPiece]));
+
+ CharBoard startBoard(board.get_nb_ranks(), board.get_nb_files());
+ fill_char_board(startBoard, v->startFen, validSpecialCharacters, v);
+
+ // skip check for gating variants to avoid confusion with gating squares
+ if (!v->gating && check_castling_rank(castlingInfoSplitted, board, v) == NOK)
+ return FEN_INVALID_CASTLING_INFO;
+
+ // only check exact squares if starting position of castling pieces is known
+ if (!v->chess960 && !v->castlingDroppedPiece && !chess960)
+ {
+ std::array<CharSquare, 2> kingPositionsStart;
+ kingPositionsStart[WHITE] = startBoard.get_square_for_piece(v->pieceToChar[make_piece(WHITE, v->castlingKingPiece)]);
+ kingPositionsStart[BLACK] = startBoard.get_square_for_piece(v->pieceToChar[make_piece(BLACK, v->castlingKingPiece)]);
+ std::array<std::vector<CharSquare>, 2> rookPositionsStart;
+ rookPositionsStart[WHITE] = startBoard.get_squares_for_piece(v->pieceToChar[make_piece(WHITE, v->castlingRookPiece)]);
+ rookPositionsStart[BLACK] = startBoard.get_squares_for_piece(v->pieceToChar[make_piece(BLACK, v->castlingRookPiece)]);
+
+ if (check_standard_castling(castlingInfoSplitted, board, kingPositions, kingPositionsStart, rookPositionsStart, v) == NOK)
+ return FEN_INVALID_CASTLING_INFO;
+ }
+ }
+ }
+
+ // 4) Part
+ // check en-passant square
+ if (fenParts.size() >= 4 && !skipCastlingAndEp)
+ {
+ if (v->doubleStep && v->pieceTypes.find(PAWN) != v->pieceTypes.end())
+ {
+ if (check_en_passant_square(fenParts[3]) == NOK)
+ return FEN_INVALID_EN_PASSANT_SQ;
+ }
+ else if (v->countingRule && !check_digit_field(fenParts[3]))
+ return FEN_INVALID_COUNTING_RULE;
+ }
+
+ // 5) Part
+ // check check count
+ unsigned int optionalFields = 2 * !skipCastlingAndEp;
+ if (fenParts.size() >= 3 + optionalFields && v->checkCounting && fenParts.size() % 2)
+ {
+ if (check_check_count(fenParts[2 + optionalFields]) == NOK)
+ return FEN_INVALID_CHECK_COUNT;
+ optionalFields++;
+ }
+
+ // 6) Part
+ // check half move counter
+ if (fenParts.size() >= 3 + optionalFields && !check_digit_field(fenParts[fenParts.size()-2]))
+ {
+ std::cerr << "Invalid half move counter: '" << fenParts[fenParts.size()-2] << "'." << std::endl;
+ return FEN_INVALID_HALF_MOVE_COUNTER;
+ }
+
+ // 7) Part
+ // check move counter
+ if (fenParts.size() >= 4 + optionalFields && !check_digit_field(fenParts[fenParts.size()-1]))
+ {
+ std::cerr << "Invalid move counter: '" << fenParts[fenParts.size()-1] << "'." << std::endl;
+ return FEN_INVALID_MOVE_COUNTER;
+ }
+
+ return FEN_OK;
+}
+} // namespace FEN
+
++} // namespace Stockfish
++
+#endif // #ifndef APIUTIL_H_INCLUDED
#include <bitset>
#include "bitboard.h"
+#include "magic.h"
#include "misc.h"
+#include "piece.h"
+ namespace Stockfish {
+
uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
}
}
-
namespace {
- Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
-
- Bitboard attacks = 0;
- Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
- Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
-
- for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
- {
- Square s = sq;
- while(safe_destination(s, d) && !(occupied & s))
- attacks |= (s += d);
- }
-
- return attacks;
- }
-
-
// init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
}
}
}
+
+ delete[] occupancy;
+ delete[] reference;
+ delete[] epoch;
}
}
+
+ } // namespace Stockfish
void init();
std::string pretty(Bitboard b);
- }
+ } // namespace Stockfish::Bitboards
+#ifdef LARGEBOARDS
+constexpr Bitboard AllSquares = ((~Bitboard(0)) >> 8);
+#else
constexpr Bitboard AllSquares = ~Bitboard(0);
+#endif
+#ifdef LARGEBOARDS
+constexpr Bitboard DarkSquares = (Bitboard(0xAAA555AAA555AAULL) << 64) ^ Bitboard(0xA555AAA555AAA555ULL);
+#else
constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
+#endif
+#ifdef LARGEBOARDS
+constexpr Bitboard FileABB = (Bitboard(0x00100100100100ULL) << 64) ^ Bitboard(0x1001001001001001ULL);
+#else
constexpr Bitboard FileABB = 0x0101010101010101ULL;
+#endif
constexpr Bitboard FileBBB = FileABB << 1;
constexpr Bitboard FileCBB = FileABB << 2;
constexpr Bitboard FileDBB = FileABB << 3;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn.
+ if ( pos.promotion_rank() != RANK_8
+ || RANK_MAX != RANK_8
+ || pos.promotion_piece_types().find(QUEEN) == pos.promotion_piece_types().end())
+ return SCALE_FACTOR_NONE;
+
return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
}
+
+ } // namespace Stockfish
using namespace std;
- using namespace Eval::NNUE;
+ using namespace Stockfish::Eval::NNUE;
+
+ namespace Stockfish {
+NnueFeatures currentNnueFeatures;
+
namespace Eval {
bool useNNUE;
} // namespace Eval
+extern NnueFeatures currentNnueFeatures;
+
+ } // namespace Stockfish
+
#endif // #ifndef EVALUATE_H_INCLUDED
--- /dev/null
+/*
+ ffish.js, a JavaScript chess variant library derived from Fairy-Stockfish
+ Copyright (C) 2021 Fabian Fichter, Johannes Czech
+
+ ffish.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ffish.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <emscripten.h>
+#include <emscripten/bind.h>
+#include <vector>
+#include <string>
+#include <sstream>
+#include<iostream>
+
+#include "misc.h"
+#include "types.h"
+#include "bitboard.h"
+#include "evaluate.h"
+#include "position.h"
+#include "search.h"
+#include "syzygy/tbprobe.h"
+#include "thread.h"
+#include "tt.h"
+#include "uci.h"
+#include "piece.h"
+#include "variant.h"
+#include "movegen.h"
+#include "apiutil.h"
+
+using namespace emscripten;
+
++using namespace Stockfish;
+
+void initialize_stockfish() {
+ pieceMap.init();
+ variants.init();
+ UCI::init(Options);
+ Bitboards::init();
+ Position::init();
+ Bitbases::init();
+}
+
+#define DELIM " "
+
+inline void save_pop_back(std::string& s) {
+ if (s.size() != 0) {
+ s.pop_back();
+ }
+}
+
+const Variant* get_variant(const std::string& uciVariant) {
+ if (uciVariant.size() == 0 || uciVariant == "Standard" || uciVariant == "standard")
+ return variants.find("chess")->second;
+ return variants.find(uciVariant)->second;
+}
+
+template <bool isUCI>
+inline bool is_move_none(Move move, const std::string& strMove, const Position& pos) {
+ if (move == MOVE_NONE) {
+ std::cerr << "The given ";
+ isUCI ? std::cerr << "uciMove" : std::cerr << "sanMove";
+ std::cerr << " '" << strMove << "' for position '" << pos.fen() << "' is invalid." << std::endl;
+ return true;
+ }
+ return false;
+}
+
+class Board {
+ // note: we can't use references for strings here due to conversion to JavaScript
+private:
+ const Variant* v;
+ StateListPtr states;
+ Position pos;
+ Thread* thread;
+ std::vector<Move> moveStack;
+ bool is960;
+
+public:
+ static bool sfInitialized;
+
+ Board():
+ Board("chess", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" , false) {
+ }
+
+ Board(std::string uciVariant):
+ Board(uciVariant, "", false) {
+ }
+
+ Board(std::string uciVariant, std::string fen):
+ Board(uciVariant, fen, false) {
+ }
+
+ Board(std::string uciVariant, std::string fen, bool is960) {
+ init(uciVariant, fen, is960);
+ }
+
+ std::string legal_moves() {
+ std::string moves;
+ for (const ExtMove& move : MoveList<LEGAL>(this->pos)) {
+ moves += UCI::move(this->pos, move);
+ moves += DELIM;
+ }
+ save_pop_back(moves);
+ return moves;
+ }
+
+ std::string legal_moves_san() {
+ std::string movesSan;
+ for (const ExtMove& move : MoveList<LEGAL>(this->pos)) {
+ movesSan += SAN::move_to_san(this->pos, move, NOTATION_SAN);
+ movesSan += DELIM;
+ }
+ save_pop_back(movesSan);
+ return movesSan;
+ }
+
+ int number_legal_moves() const {
+ return MoveList<LEGAL>(pos).size();
+ }
+
+ bool push(std::string uciMove) {
+ const Move move = UCI::to_move(this->pos, uciMove);
+ if (is_move_none<true>(move, uciMove, pos))
+ return false;
+ do_move(move);
+ return true;
+ }
+
+ bool push_san(std::string sanMove) {
+ return push_san(sanMove, NOTATION_SAN);
+ }
+
+ // TODO: This is a naive implementation which compares all legal SAN moves with the requested string.
+ // If the SAN move wasn't found the position remains unchanged. Alternatively, implement a direct conversion.
+ bool push_san(std::string sanMove, Notation notation) {
+ Move foundMove = MOVE_NONE;
+ for (const ExtMove& move : MoveList<LEGAL>(pos)) {
+ if (sanMove == SAN::move_to_san(this->pos, move, notation)) {
+ foundMove = move;
+ break;
+ }
+ }
+ if (is_move_none<false>(foundMove, sanMove, pos))
+ return false;
+ do_move(foundMove);
+ return true;
+ }
+
+ void pop() {
+ pos.undo_move(this->moveStack.back());
+ moveStack.pop_back();
+ states->pop_back();
+ }
+
+ void reset() {
+ set_fen(v->startFen);
+ }
+
+ bool is_960() const {
+ return is960;
+ }
+
+ std::string fen() const {
+ return this->pos.fen();
+ }
+
+ void set_fen(std::string fen) {
+ resetStates();
+ moveStack.clear();
+ pos.set(v, fen, is960, &states->back(), thread);
+ }
+
+ // note: const identifier for pos not possible due to SAN::move_to_san()
+ std::string san_move(std::string uciMove) {
+ return san_move(uciMove, NOTATION_SAN);
+ }
+
+ std::string san_move(std::string uciMove, Notation notation) {
+ const Move move = UCI::to_move(this->pos, uciMove);
+ if (is_move_none<true>(move, uciMove, pos))
+ return "";
+ return SAN::move_to_san(this->pos, UCI::to_move(this->pos, uciMove), notation);
+ }
+
+ std::string variation_san(std::string uciMoves) {
+ return variation_san(uciMoves, NOTATION_SAN, true);
+ }
+
+ std::string variation_san(std::string uciMoves, Notation notation) {
+ return variation_san(uciMoves, notation, true);
+ }
+
+ std::string variation_san(std::string uciMoves, Notation notation, bool moveNumbers) {
+ std::stringstream ss(uciMoves);
+ StateListPtr tempStates;
+ std::vector<Move> moves;
+ std::string variationSan = "";
+ std::string uciMove;
+ bool first = true;
+
+ while (std::getline(ss, uciMove, ' ')) {
+ const Move move = UCI::to_move(this->pos, uciMove);
+ if (is_move_none<true>(move, uciMove, pos))
+ return "";
+ moves.emplace_back(UCI::to_move(this->pos, uciMove));
+ if (first) {
+ first = false;
+ if (moveNumbers) {
+ variationSan = std::to_string(fullmove_number());
+ if (pos.side_to_move() == WHITE)
+ variationSan += ". ";
+ else
+ variationSan += "...";
+ }
+ variationSan += SAN::move_to_san(this->pos, moves.back(), Notation(notation));
+ }
+ else {
+ if (moveNumbers && pos.side_to_move() == WHITE) {
+ variationSan += DELIM;
+ variationSan += std::to_string(fullmove_number());
+ variationSan += ".";
+ }
+ variationSan += DELIM;
+ variationSan += SAN::move_to_san(this->pos, moves.back(), Notation(notation));
+ }
+ states->emplace_back();
+ pos.do_move(moves.back(), states->back());
+ }
+
+ // recover initial state
+ for(auto rIt = std::rbegin(moves); rIt != std::rend(moves); ++rIt) {
+ pos.undo_move(*rIt);
+ }
+
+ return variationSan;
+ }
+
+ // returns true for WHITE and false for BLACK
+ bool turn() const {
+ return !pos.side_to_move();
+ }
+
+ int fullmove_number() const {
+ return pos.game_ply() / 2 + 1;
+ }
+
+ int halfmove_clock() const {
+ return pos.rule50_count();
+ }
+
+ int game_ply() const {
+ return pos.game_ply();
+ }
+
+ bool is_game_over() const {
+ for (const ExtMove& move: MoveList<LEGAL>(pos))
+ return false;
+ return true;
+ }
+
+ bool is_check() const {
+ return pos.checkers();
+ }
+
+ bool is_bikjang() const {
+ return pos.bikjang();
+ }
+
+ std::string move_stack() const {
+ std::string moves;
+ for(auto it = std::begin(moveStack); it != std::end(moveStack); ++it) {
+ moves += UCI::move(pos, *it);
+ moves += DELIM;
+ }
+ save_pop_back(moves);
+ return moves;
+ }
+
+ void push_moves(std::string uciMoves) {
+ std::stringstream ss(uciMoves);
+ std::string uciMove;
+ while (std::getline(ss, uciMove, ' ')) {
+ push(uciMove);
+ }
+ }
+
+ void push_san_moves(std::string sanMoves) {
+ return push_san_moves(sanMoves, NOTATION_SAN);
+ }
+
+ void push_san_moves(std::string sanMoves, Notation notation) {
+ std::stringstream ss(sanMoves);
+ std::string sanMove;
+ while (std::getline(ss, sanMove, ' '))
+ push_san(sanMove, notation);
+ }
+
+ std::string pocket(bool color) {
+ const Color c = Color(!color);
+ std::string pocket;
+ for (PieceType pt = KING; pt >= PAWN; --pt) {
+ for (int i = 0; i < pos.count_in_hand(c, pt); ++i) {
+ // only create BLACK pieces in order to convert to lower case
+ pocket += std::string(1, pos.piece_to_char()[make_piece(BLACK, pt)]);
+ }
+ }
+ return pocket;
+ }
+
+ std::string to_string() {
+ std::string stringBoard;
+ for (Rank r = pos.max_rank(); r >= RANK_1; --r) {
+ for (File f = FILE_A; f <= pos.max_file(); ++f) {
+ if (f != FILE_A)
+ stringBoard += " ";
+ const Piece p = pos.piece_on(make_square(f, r));
+ switch(p) {
+ case NO_PIECE:
+ stringBoard += '.';
+ break;
+ default:
+ stringBoard += pos.piece_to_char()[p];
+ }
+ }
+ if (r != RANK_1)
+ stringBoard += "\n";
+ }
+ return stringBoard;
+ }
+
+ std::string to_verbose_string() {
+ std::stringstream ss;
+ operator<<(ss, pos);
+ return ss.str();
+ }
+
+ std::string variant() {
+ // Iterate through the variants map
+ for (auto it = variants.begin(); it != variants.end(); ++it)
+ if (it->second == v)
+ return it->first;
+
+ std::cerr << "Current variant is not registered." << std::endl;
+ return "unknown";
+ }
+
+private:
+ void resetStates() {
+ this->states = StateListPtr(new std::deque<StateInfo>(1));
+ }
+
+ void do_move(Move move) {
+ states->emplace_back();
+ this->pos.do_move(move, states->back());
+ this->moveStack.emplace_back(move);
+ }
+
+ void init(std::string uciVariant, std::string fen, bool is960) {
+ if (!Board::sfInitialized) {
+ initialize_stockfish();
+ Board::sfInitialized = true;
+ }
+ v = get_variant(uciVariant);
+ this->resetStates();
+ if (fen == "")
+ fen = v->startFen;
+ this->pos.set(this->v, fen, is960, &this->states->back(), this->thread);
+ this->is960 = is960;
+ }
+};
+
+bool Board::sfInitialized = false;
+
+namespace ffish {
+ // returns the version of the Fairy-Stockfish binary
+ std::string info() {
+ return engine_info();
+ }
+
+ template <typename T>
+ void set_option(std::string name, T value) {
+ Options[name] = value;
+ Board::sfInitialized = false;
+ }
+
+ std::string available_variants() {
+ std::string availableVariants;
+ for (std::string variant : variants.get_keys()) {
+ availableVariants += variant;
+ availableVariants += DELIM;
+ }
+ save_pop_back(availableVariants);
+ return availableVariants;
+ }
+
+ void load_variant_config(std::string variantInitContent) {
+ std::stringstream ss(variantInitContent);
+ if (!Board::sfInitialized)
+ initialize_stockfish();
+ variants.parse_istream<false>(ss);
+ Options["UCI_Variant"].set_combo(variants.get_keys());
+ Board::sfInitialized = true;
+ }
+
+ std::string starting_fen(std::string uciVariant) {
+ const Variant* v = get_variant(uciVariant);
+ return v->startFen;
+ }
+
+ int validate_fen(std::string fen, std::string uciVariant, bool chess960) {
+ const Variant* v = get_variant(uciVariant);
+ return FEN::validate_fen(fen, v, chess960);
+ }
+
+ int validate_fen(std::string fen, std::string uciVariant) {
+ return validate_fen(fen, uciVariant, false);
+ }
+
+ int validate_fen(std::string fen) {
+ return validate_fen(fen, "chess");
+ }
+}
+
+class Game {
+private:
+ std::unordered_map<std::string, std::string> header;
+ std::unique_ptr<Board> board;
+ std::string variant = "chess";
+ std::string fen = ""; // start pos
+ bool is960 = false;
+ bool parsedGame = false;
+public:
+ std::string header_keys() {
+ std::string keys;
+ for (auto it = header.begin(); it != header.end(); ++it) {
+ keys += it->first;
+ keys += DELIM;
+ }
+ save_pop_back(keys);
+ return keys;
+ }
+
+ std::string headers(std::string item) {
+ auto it = header.find(item);
+ if (it == header.end())
+ return "";
+ return it->second;
+ }
+
+ std::string mainline_moves() {
+ if (!parsedGame)
+ return "";
+ return board->move_stack();
+ }
+
+ friend Game read_game_pgn(std::string);
+};
+
+
+bool skip_comment(const std::string& pgn, size_t& curIdx, size_t& lineEnd) {
+ curIdx = pgn.find('}', curIdx);
+ if (curIdx == std::string::npos) {
+ std::cerr << "Missing '}' for move comment while reading pgn." << std::endl;
+ return false;
+ }
+ if (curIdx > lineEnd)
+ lineEnd = pgn.find('\n', curIdx);
+ return true;
+}
+
+Game read_game_pgn(std::string pgn) {
+ Game game;
+ size_t lineStart = 0;
+ bool headersParsed = false;
+
+ while(true) {
+ size_t lineEnd = pgn.find('\n', lineStart);
+
+ if (lineEnd == std::string::npos)
+ lineEnd = pgn.size();
+
+ if (!headersParsed && pgn[lineStart] == '[') {
+ // parse header
+ // look for item
+ size_t headerKeyStart = lineStart+1;
+ size_t headerKeyEnd = pgn.find(' ', lineStart);
+ size_t headerItemStart = pgn.find('"', headerKeyEnd)+1;
+ size_t headerItemEnd = pgn.find('"', headerItemStart);
+
+ // put item into list
+ game.header[pgn.substr(headerKeyStart, headerKeyEnd-headerKeyStart)] = pgn.substr(headerItemStart, headerItemEnd-headerItemStart);
+ }
+ else {
+ if (!headersParsed) {
+ headersParsed = true;
+ auto it = game.header.find("Variant");
+ if (it != game.header.end()) {
+ game.variant = it->second;
+ std::transform(game.variant.begin(), game.variant.end(), game.variant.begin(),
+ [](unsigned char c){ return std::tolower(c); });
+ game.is960 = it->second.find("960") != std::string::npos;
+ }
+
+ it = game.header.find("FEN");
+ if (it != game.header.end())
+ game.fen = it->second;
+
+ game.board = std::make_unique<Board>(game.variant, game.fen, game.is960);
+ game.parsedGame = true;
+ }
+
+ // game line
+ size_t curIdx = lineStart;
+ while (curIdx <= lineEnd) {
+ if (pgn[curIdx] == '*')
+ return game;
+
+ if (pgn[curIdx] == '{') {
+ if (!skip_comment(pgn, curIdx, lineEnd))
+ return game;
+ ++curIdx;
+ }
+
+ // Movetext RAV (Recursive Annotation Variation)
+ size_t openedRAV = 0;
+ if (pgn[curIdx] == '(') {
+ openedRAV = 1;
+ ++curIdx;
+ }
+ while (openedRAV != 0) {
+ switch (pgn[curIdx]) {
+ case '(':
+ ++openedRAV;
+ break;
+ case ')':
+ --openedRAV;
+ break;
+ case '{':
+ if (!skip_comment(pgn, curIdx, lineEnd))
+ return game;
+ default: ; // pass
+ }
+ ++curIdx;
+ if (curIdx > lineEnd)
+ lineEnd = pgn.find('\n', curIdx);
+ }
+
+ if (pgn[curIdx] == '$') {
+ // we are at a glyph
+ curIdx = pgn.find(' ', curIdx);
+ }
+
+ if (pgn[curIdx] >= '0' && pgn[curIdx] <= '9') {
+ // we are at a move number -> look for next point
+ curIdx = pgn.find('.', curIdx);
+ if (curIdx == std::string::npos)
+ break;
+ ++curIdx;
+ // increment if we're at a space
+ while (curIdx < pgn.size() && pgn[curIdx] == ' ')
+ ++curIdx;
+ // increment if we're at a point
+ while (curIdx < pgn.size() && pgn[curIdx] == '.')
+ ++curIdx;
+ }
+ // extract sanMove
+ size_t sanMoveEnd = std::min(pgn.find(' ', curIdx), lineEnd);
+ if (sanMoveEnd > curIdx) {
+ std::string sanMove = pgn.substr(curIdx, sanMoveEnd-curIdx);
+ // clean possible ? and ! from string
+ size_t annotationChar1 = sanMove.find('?');
+ size_t annotationChar2 = sanMove.find('!');
+ if (annotationChar1 != std::string::npos || annotationChar2 != std::string::npos)
+ sanMove = sanMove.substr(0, std::min(annotationChar1, annotationChar2));
+ std::cout << sanMove << " ";
+ game.board->push_san(sanMove);
+ }
+ curIdx = sanMoveEnd+1;
+ }
+ }
+ lineStart = lineEnd+1;
+
+ if (lineStart >= pgn.size())
+ return game;
+ }
+ return game;
+}
+
+
+// binding code
+EMSCRIPTEN_BINDINGS(ffish_js) {
+ class_<Board>("Board")
+ .constructor<>()
+ .constructor<std::string>()
+ .constructor<std::string, std::string>()
+ .constructor<std::string, std::string, bool>()
+ .function("legalMoves", &Board::legal_moves)
+ .function("legalMovesSan", &Board::legal_moves_san)
+ .function("numberLegalMoves", &Board::number_legal_moves)
+ .function("push", &Board::push)
+ .function("pushSan", select_overload<bool(std::string)>(&Board::push_san))
+ .function("pushSan", select_overload<bool(std::string, Notation)>(&Board::push_san))
+ .function("pop", &Board::pop)
+ .function("reset", &Board::reset)
+ .function("is960", &Board::is_960)
+ .function("fen", &Board::fen)
+ .function("setFen", &Board::set_fen)
+ .function("sanMove", select_overload<std::string(std::string)>(&Board::san_move))
+ .function("sanMove", select_overload<std::string(std::string, Notation)>(&Board::san_move))
+ .function("variationSan", select_overload<std::string(std::string)>(&Board::variation_san))
+ .function("variationSan", select_overload<std::string(std::string, Notation)>(&Board::variation_san))
+ .function("variationSan", select_overload<std::string(std::string, Notation, bool)>(&Board::variation_san))
+ .function("turn", &Board::turn)
+ .function("fullmoveNumber", &Board::fullmove_number)
+ .function("halfmoveClock", &Board::halfmove_clock)
+ .function("gamePly", &Board::game_ply)
+ .function("isGameOver", &Board::is_game_over)
+ .function("isCheck", &Board::is_check)
+ .function("isBikjang", &Board::is_bikjang)
+ .function("moveStack", &Board::move_stack)
+ .function("pushMoves", &Board::push_moves)
+ .function("pushSanMoves", select_overload<void(std::string)>(&Board::push_san_moves))
+ .function("pushSanMoves", select_overload<void(std::string, Notation)>(&Board::push_san_moves))
+ .function("pocket", &Board::pocket)
+ .function("toString", &Board::to_string)
+ .function("toVerboseString", &Board::to_verbose_string)
+ .function("variant", &Board::variant);
+ class_<Game>("Game")
+ .function("headerKeys", &Game::header_keys)
+ .function("headers", &Game::headers)
+ .function("mainlineMoves", &Game::mainline_moves);
+ // usage: e.g. ffish.Notation.DEFAULT
+ enum_<Notation>("Notation")
+ .value("DEFAULT", NOTATION_DEFAULT)
+ .value("SAN", NOTATION_SAN)
+ .value("LAN", NOTATION_LAN)
+ .value("SHOGI_HOSKING", NOTATION_SHOGI_HOSKING)
+ .value("SHOGI_HODGES", NOTATION_SHOGI_HODGES)
+ .value("SHOGI_HODGES_NUMBER", NOTATION_SHOGI_HODGES_NUMBER)
+ .value("JANGGI", NOTATION_JANGGI)
+ .value("XIANGQI_WXF", NOTATION_XIANGQI_WXF);
+ function("info", &ffish::info);
+ function("setOption", &ffish::set_option<std::string>);
+ function("setOptionInt", &ffish::set_option<int>);
+ function("setOptionBool", &ffish::set_option<bool>);
+ function("readGamePGN", &read_game_pgn);
+ function("variants", &ffish::available_variants);
+ function("loadVariantConfig", &ffish::load_variant_config);
+ function("startingFen", &ffish::starting_fen);
+ function("validateFen", select_overload<int(std::string)>(&ffish::validate_fen));
+ function("validateFen", select_overload<int(std::string, std::string)>(&ffish::validate_fen));
+ function("validateFen", select_overload<int(std::string, std::string, bool)>(&ffish::validate_fen));
+ // TODO: enable to string conversion method
+ // .class_function("getStringFromInstance", &Board::get_string_from_instance);
+}
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MAGIC_H_INCLUDED
+#define MAGIC_H_INCLUDED
+
++namespace Stockfish {
++
+#ifdef PRECOMPUTED_MAGICS
+#define B(a, b) (Bitboard(a) << 64) ^ Bitboard(b)
+ // Use precomputed magics if pext is not available,
+ // since the magics generation is very slow.
+ constexpr Bitboard RookMagicHInit[SQUARE_NB] = {
+ B(0x120000880110000, 0x1008000000020020),
+ B(0x24200C080840A052, 0x2004004000010008),
+ B(0xC030024000228800, 0x4000010400000020),
+ B(0x1A0020802008802, 0x206010208000),
+ B(0x12002000D001024, 0x80100800090138),
+ B(0x4220010000241010, 0x3098000602001500),
+ B(0x401010004801040, 0x8000280480100000),
+ B(0x820082024921836, 0x220028000),
+ B(0x100400502411400, 0x220402120240D14),
+ B(0x880202020010404, 0xA80202510000),
+ B(0x140002801000018, 0x1000346490040),
+ B(0x120000880110000, 0x1008000000020020),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x8420060100020000, 0x800280400000120),
+ B(0x4010020018010, 0x40A00001100000),
+ B(0x40006A0004000200, 0x40000000110),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x8908A20028110011, 0x800080000001A114),
+ B(0x200042000080F009, 0x20001000004000),
+ B(0x2820008820100, 0x10002400058000B9),
+ B(0x6083100420008050, 0x4040012600280080),
+ B(0x216020000000446, 0x4080204000000211),
+ B(0x340140003002089, 0x2402008000000911),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x8C0488120024214, 0x8414880202291),
+ B(0x1010000060050000, 0x4000004050002602),
+ B(0x4022983A0060000, 0x80000040010400),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x6020101802002840, 0x31000003000004),
+ B(0x9000420008840, 0x4881300000000210),
+ B(0xA200808865, 0x41C0048023000128),
+ B(0x31801100400000, 0x8802DC001221240),
+ B(0x884000080200920, 0x1004002410401001),
+ B(0x2400040000884, 0x421006208040C0),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x1800040009040200, 0x180000A1004E408A),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x4060402008080, 0xC240080000110000),
+ B(0x20080100920020, 0x2002248010242052),
+ B(0x10001010802050, 0x880000001C98420),
+ B(0x4000800100420022, 0x502022010A00D0),
+ B(0x4C18104500200885, 0x400880800),
+ B(0x8080810081020090, 0x8000000000000),
+ B(0x8000062812080201, 0x8004C8300800),
+ B(0xC010220920198, 0x85000A08000),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x80102204040, 0x1000000900000000),
+ B(0x2080000004202804, 0x120880003461),
+ B(0x102004090A4030, 0x801020589240),
+ B(0x20001100814000A0, 0x420202000820004),
+ B(0x100800000A000120, 0x208000800010000),
+ B(0x1008205000040802, 0x80002000400040),
+ B(0x1480000098008401, 0xA0010000581010),
+ B(0x30C0008200100820, 0x102800080904834),
+ B(0x4810821884000500, 0x4400000200000212),
+ B(0x1811D00128A0180, 0x2500848803000000),
+ B(0x41618A0300040040, 0x21200200A421801),
+ B(0x80102204040, 0x1000000900000000),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x8C890020410000A0, 0xA010000048000400),
+ B(0x40006002210044, 0x600008000408000),
+ B(0x1200447220090042, 0x80001000160012),
+ B(0x48410010AB000000, 0x9200600000000100),
+ B(0x2040000000240003, 0x8020080288000600),
+ B(0x9080000088848088, 0x4010210500000041),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x480100400024, 0x1004800018200000),
+ B(0x808403080080200, 0x802601000000500),
+ B(0x8C890020410000A0, 0xA010000048000400),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x400014006000000, 0x10006000810001F5),
+ B(0xC410062001414, 0x820080041B01044),
+ B(0x20000800310, 0x430040000201000),
+ B(0xA40010008000008, 0x4002200028000040),
+ B(0xC00102000008021C, 0x10C2000A010E024),
+ B(0x80004200104008, 0x50A00800C400020),
+ B(0x20200080012542, 0x910F0040000402C0),
+ B(0xB040100504000300, 0x24802002000040),
+ B(0x800001000014008, 0x400031004000),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x84008002041081C0, 0x8080500200000000),
+ B(0x440090001012001, 0x4020004010),
+ B(0x100A0028088020, 0x80040E00010020),
+ B(0x2180808000810, 0xB018040A00040000),
+ B(0x40C80920304C4001, 0x42800B200800000),
+ B(0x85000425001000, 0x4810048020001100),
+ B(0x600C000801000004, 0x8015084010200020),
+ B(0x20020050000240C0, 0x100202008600800),
+ B(0x38000050001220, 0x9200010200145900),
+ B(0x1042108040005, 0x1402A0802201001),
+ B(0x824240000C20400, 0x1000000400080010),
+ B(0x84008002041081C0, 0x8080500200000000),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x8000402308483, 0x20006020100100),
+ B(0x80880120000080, 0x8000240100084),
+ B(0x5840020004882001, 0x1004528000A00010),
+ B(0x8001018800300002, 0x84010040804),
+ B(0x180D10004000A008, 0xA001080008020004),
+ B(0x400080B, 0x10A0000004010000),
+ B(0x8080000200000, 0x2001000082004E0),
+ B(0x40040001000C2000, 0x2024800001004008),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x8000402308483, 0x20006020100100),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x2000200000, 0x1201011000802),
+ B(0x100100000000C4, 0x208004084048201),
+ B(0x400084000044, 0x100810140300),
+ B(0x29040C0C01010, 0x300204010820080),
+ B(0x1A808000020200, 0x1000000005210040),
+ B(0x20000400150000, 0x85008020),
+ B(0x40C040008184014, 0x8002AA00024010),
+ B(0x202000081B00804, 0x10001002008),
+ B(0x40011000210060, 0x6080C40000021004),
+ B(0x2000200000, 0x1201011000802),
+ B(0x4100480203840, 0x300080100804),
+ B(0x2000200000, 0x1201011000802),
+ };
+ constexpr Bitboard RookMagicVInit[SQUARE_NB] = {
+ B(0x202000812104400, 0x24800B01C0000303),
+ B(0x340020400010D, 0x88060150C00400),
+ B(0x400802040609, 0x49010200501A0002),
+ B(0x8002680301000208, 0x628006C0C020200),
+ B(0x20400209001C0804, 0xA044000800143110),
+ B(0xC400082060010202, 0x4000480401014000),
+ B(0x22500200144040, 0x8204820084704C00),
+ B(0x8C1204009030020, 0x328400805000000),
+ B(0x84800800D0001640, 0x200080040060108),
+ B(0x804810208020040, 0x140010108020000),
+ B(0x1102010B008004, 0x300208006220020),
+ B(0x140080404A0A2428, 0x6308010100080),
+ B(0x20444002120408, 0xA080010508010001),
+ B(0x82011044000D02, 0x4112028620110809),
+ B(0x81010831000C02, 0x408802085000000),
+ B(0x81010831000C02, 0x408802085000000),
+ B(0x920008920600040, 0x8053801004000028),
+ B(0x81140283208300, 0x10040C004200420),
+ B(0x103080022201, 0xC01000081312620),
+ B(0x2200221100008, 0x1000408104000A4),
+ B(0x4402088080042008, 0x210401501040340),
+ B(0x898400202170001, 0x80040404208000),
+ B(0x20080004051012, 0x5100048200081800),
+ B(0x2320020000401018, 0x108501021040210),
+ B(0x21080410A422021, 0x83040180008800),
+ B(0x44E8100000408224, 0x20010008040400),
+ B(0x1800240002810405, 0x23004820000020),
+ B(0x80A0100400110, 0x80104020100C4028),
+ B(0x1002050001222C0, 0x5100818004024020),
+ B(0x104000200040, 0xC010A09800102000),
+ B(0x1020003A058120, 0x450900809000302),
+ B(0x40040045008B1, 0x202800400383010),
+ B(0x4640200220034, 0x8800485420304000),
+ B(0x5001042100084288, 0x110820001240080A),
+ B(0x2002C04004010120, 0xA15008020880001),
+ B(0x2800004080C4190, 0x890808280020080),
+ B(0x40C0401000104000, 0x2020880008002580),
+ B(0x40020C002400802, 0x801104010000000),
+ B(0x44842000040080, 0x2050011084000400),
+ B(0x4110040800000401, 0x2023810029008000),
+ B(0x20884000840, 0x8017102004008000),
+ B(0x10411104000480, 0x1414042000201001),
+ B(0x220040000008, 0x800306021000000),
+ B(0x41400A0008080, 0x501000298ACAD10),
+ B(0x800240012831810, 0x80120004468050E),
+ B(0x800005020801008, 0x20102400240000),
+ B(0x20C00040C114C010, 0x88080820200C00),
+ B(0x1044010100820081, 0x20080841004000),
+ B(0x8041048400022, 0x8020836040005002),
+ B(0x2001004010205, 0x8001002884042009),
+ B(0x128088400087, 0x20008002201002),
+ B(0x8084108040402000, 0x80809000A080400),
+ B(0x408081840880, 0x201002088000040),
+ B(0xA40180010280, 0x241004006000010),
+ B(0x4204100080048140, 0x2002C4F104202020),
+ B(0x100140A10204, 0x980200800840060),
+ B(0x1005140010202048, 0x1442280800202815),
+ B(0x2000082025008600, 0x1108400040600003),
+ B(0x1005050648000, 0x200020240008002),
+ B(0x202010208044000, 0x8210404060008),
+ B(0x8011040402000210, 0xC840180408016004),
+ B(0x404098801028, 0x80020A0001000400),
+ B(0x404098801028, 0x80020A0001000400),
+ B(0x80101002180140, 0x40C2080820000C0),
+ B(0x208202081260800, 0x14090E4C04000050),
+ B(0x4221201084004C2, 0x110480A011060),
+ B(0x8000008421090204, 0x1C01010800024),
+ B(0x8000008421090204, 0x1C01010800024),
+ B(0x200180C840088A0, 0x401100400820000),
+ B(0x10084043A021070, 0x202041600080200),
+ B(0x210E6202001040C, 0x10100800080B0),
+ B(0x848008021204002, 0x801004308100BAD),
+ B(0xC082C0390A000601, 0x4040080189008),
+ B(0x431200240210402D, 0x58102820000),
+ B(0x202020100A0019B0, 0x4010C0D018000000),
+ B(0x800800908402203, 0x102948C84C184),
+ B(0x26801100080845, 0x4009702022A00820),
+ B(0x8880520010401040, 0x1060084832052000),
+ B(0x100100022042081, 0x10000600008C121),
+ B(0x46020384100040, 0x800200320882021),
+ B(0xC0002010148, 0x4200800800040003),
+ B(0x2002208020090040, 0x40820210021410),
+ B(0x9000A41160002004, 0x2A09000100080043),
+ B(0x800004010008001, 0x1108002020104600),
+ B(0x800540C000A4E041, 0x18021180000401),
+ B(0x808200900A900202, 0x8364202140012005),
+ B(0x1DBA52000081010, 0x4008000023000010),
+ B(0x4100110204401481, 0x800040091020001C),
+ B(0x4100110204401481, 0x800040091020001C),
+ B(0x4101100020400482, 0x2000402302100120),
+ B(0x100408000A020212, 0xA000400111000020),
+ B(0x2000010488080104, 0x3000404410208100),
+ B(0x2684220180008DD0, 0x422040200004000A),
+ B(0x2021200C0424, 0x1010100000080200),
+ B(0x8908020020801006, 0x3010800020C2000),
+ B(0x4000030008062044, 0x244010202688000),
+ B(0x242101200408009, 0x8150040000200015),
+ B(0x42004C02180204, 0x210208014241040),
+ B(0x4E1A01C208410804, 0x8890041000012004),
+ B(0x2080200401000080, 0x8001098429008004),
+ B(0xA01400121804104, 0x280200C400000500),
+ B(0xD0080408040420, 0x1006040100224000),
+ B(0x28400205000800C9, 0x6021101401040075),
+ B(0x4000900040020104, 0x88129801100D0C),
+ B(0x8000004002180410, 0x400380200400204),
+ B(0x4002A430043008, 0x400200340100020),
+ B(0x401960004140A42, 0x100880710000464),
+ B(0x58014090102, 0xB8D30004010080),
+ B(0xA004C08000244000, 0x11280100E0000040),
+ B(0x2102008089208804, 0x110001004080040),
+ B(0x700010084E003004, 0x8080864112000D40),
+ B(0x4080881000200C20, 0x30324040880E0600),
+ B(0x2024A40401810820, 0x3000888002000000),
+ B(0x8200100400014, 0x4400340800252844),
+ B(0x24A00804288281, 0x410103002201140),
+ B(0x4080005022A08, 0x1000402200100264),
+ B(0x200080032244040, 0x200502189010001),
+ B(0x28108110404001, 0x400600120008412),
+ B(0xA00002102810020, 0xB1080240015408),
+ B(0x810080200806, 0x410440804080046)
+ };
+ constexpr Bitboard BishopMagicInit[SQUARE_NB] = {
+ B(0x2001040305000010, 0x830200040400082),
+ B(0x1042400080E01200, 0x2004904010811400),
+ B(0x400010120200, 0x880080D080018000),
+ B(0x240190C00100040, 0x100A020140044404),
+ B(0x1018010404010004, 0x1001010018081E0),
+ B(0x41200A804C0904, 0x40000322000008),
+ B(0x4001180A004, 0x8000001106000000),
+ B(0x6006020020030600, 0x1840002100004841),
+ B(0x4200200100, 0x4001041808002000),
+ B(0x4100020050124600, 0x1001802902400CA0),
+ B(0x448C0081440161, 0x200206010008000),
+ B(0x400008008008408, 0x1000080210100080),
+ B(0x200280C01008200, 0x210200813000080),
+ B(0x1A000204400, 0x222200401023000),
+ B(0x10081040640A00, 0x8410021881400000),
+ B(0x1840400318080008, 0x800800840080000),
+ B(0x4204050C040, 0x6500600200140000),
+ B(0x1012100040204, 0x402404444400000),
+ B(0x6000012680008240, 0x410140000004220),
+ B(0x1000020810040008, 0x2D0011000060000),
+ B(0x1020020400, 0x400108059001001),
+ B(0x400020001100808, 0x480204800200000B),
+ B(0x10000010030084, 0x2042000848900022),
+ B(0x10000010030084, 0x2042000848900022),
+ B(0x100D801402400, 0x1512404009000400),
+ B(0x8000208005112400, 0xA02040401000000),
+ B(0x1000420002800200, 0x4CA000183020000),
+ B(0x800811480020, 0x408801010224001),
+ B(0xC805200810900100, 0x9000084204004020),
+ B(0x8200160204100004, 0x8040004004002022),
+ B(0x104514013080080, 0x146410040001000),
+ B(0x140844000080002, 0x1008102020040001),
+ B(0x4040400041A2002, 0x8040000A8802510),
+ B(0x801014041008002, 0x80068008025200),
+ B(0xA00540A414040, 0x4101040010A0000),
+ B(0x6484008010810002, 0x1100506884024000),
+ B(0x2800401008006000, 0x1005420884029020),
+ B(0x6822091010004421, 0x2000458080480),
+ B(0x40101000200101, 0x10020100001C4E0),
+ B(0x100400008C42, 0x4000100009008000),
+ B(0x851220018800400, 0x1681800040080080),
+ B(0x64200002010, 0x900020200040002),
+ B(0x20800080000022, 0x80040810002010),
+ B(0xA88408000802080, 0x20808001000000),
+ B(0x200000400C005040, 0x100140020290108),
+ B(0x224100000800408, 0x4204802004400020),
+ B(0x80080620010210, 0x91080088804040),
+ B(0x4008002100010, 0x80AC201001000001),
+ B(0x10008200902C046, 0x8080D03004000010),
+ B(0x3002100081000180, 0x2210002121528408),
+ B(0x8C101800804420, 0x1019880200043008),
+ B(0x200022000920D0, 0x8000800081300020),
+ B(0x1D40800880000, 0x400040001400050),
+ B(0x2020004100040, 0x200008040008008),
+ B(0x4840800040100001, 0x100100040203040),
+ B(0x40084001105, 0x8800080088000089),
+ B(0x4000128008020008, 0x4004200200440020),
+ B(0x210040008520000, 0x820219001080022),
+ B(0x1494040018002116, 0x400101047020008),
+ B(0x510008001910C224, 0x80200148118000),
+ B(0xC0301002301000, 0x4211A08004801),
+ B(0x50008E0C01001080, 0x100C004102845100),
+ B(0x400600020060400, 0x88024100250050),
+ B(0x8202920002002040, 0x810012000003),
+ B(0x800004208800200, 0x18AA00201000048),
+ B(0x402100800100002, 0x411000081000400),
+ B(0x101000022004044, 0x9000100040000),
+ B(0x41068001001, 0xC00400010001),
+ B(0x310210001040, 0x1A1200020010000),
+ B(0xA082409200004048, 0x490040800124101),
+ B(0x18844820E0040212, 0x1000404420D10000),
+ B(0x802908A40003348, 0x20200040104140),
+ B(0x1800404028205003, 0xC020010401089020),
+ B(0x802100044D01000, 0x8C41888000800040),
+ B(0x1D0161011410081, 0x10008000100200),
+ B(0x401000480040100, 0x286800404002212),
+ B(0x821030000100009, 0x2000090200A00000),
+ B(0x200020800200800, 0x2000480900841012),
+ B(0x80A000048030080, 0x200000120200008),
+ B(0x40B1400008020020, 0x148000200008004),
+ B(0xA021700002002010, 0x3040E400040100),
+ B(0x400242C200200640, 0x20440210200281),
+ B(0x80AC140040206240, 0x120000102801401),
+ B(0x2020340040832040, 0x10402100A44000),
+ B(0x420100400040220, 0x80014C8004000106),
+ B(0x504300822421120, 0x8004004008400100),
+ B(0x2001100008040, 0x2020104302000000),
+ B(0xA500802000A, 0x2008008000114100),
+ B(0x8A0020000200, 0x9C00101001002408),
+ B(0x104000001001008, 0x9001000204040060),
+ B(0x1000820080108200, 0xA401000008100001),
+ B(0x2008600009000480, 0x9008020001400000),
+ B(0x4000800200040200, 0xA00030400308082),
+ B(0x4004300202004709, 0x1000100180010020),
+ B(0xC014800100440010, 0x402020280002C010),
+ B(0x220208010884680, 0x1040280000042110),
+ B(0x40B0018019202801, 0x1008408000100040),
+ B(0x8269010206080044, 0x8001810000000040),
+ B(0x4000020880081040, 0x208A44000028000),
+ B(0x4004004E9004220A, 0x2104004001400024),
+ B(0x8035006008C0904, 0x402002001080120),
+ B(0x1800884002, 0x404400820000000),
+ B(0x8088000004008910, 0x8024100401000000),
+ B(0x142200086000100, 0x28021040020002E),
+ B(0x1000409141004018, 0x100410820080040A),
+ B(0x1800801800140, 0x810801060C0801),
+ B(0x1000C00100402220, 0x808023420000000),
+ B(0x8A0A202414305008, 0x100040200000021),
+ B(0xC0208024050, 0x8003088008020401),
+ B(0x8044004201440101, 0x400820080C024022),
+ B(0x406018884120099, 0xB00088018002000),
+ B(0x2000800010403010, 0xC5A002002010010),
+ B(0x800020040840, 0x201800202800200),
+ B(0x201280120020008D, 0x258809001000040),
+ B(0x9100002020181, 0x80400082204000),
+ B(0x104010080201001, 0x40080080181080),
+ B(0x8440248092000430, 0xA200804900100000),
+ B(0x2031010C01000C20, 0x200310A560082008),
+ B(0x400202081811400, 0x40081802050000C),
+ B(0x1011002100821300, 0x2400825040804100)
+ };
+ constexpr Bitboard CannonMagicHInit[SQUARE_NB] = {
+ B(0x120000880110000, 0x1008000000020020),
+ B(0x24200C080840A052, 0x2004004000010008),
+ B(0xC030024000228800, 0x4000010400000020),
+ B(0x1A0020802008802, 0x206010208000),
+ B(0x12002000D001024, 0x80100800090138),
+ B(0x4220010000241010, 0x3098000602001500),
+ B(0x401010004801040, 0x8000280480100000),
+ B(0x820082024921836, 0x220028000),
+ B(0x100400502411400, 0x220402120240D14),
+ B(0x880202020010404, 0xA80202510000),
+ B(0x140002801000018, 0x1000346490040),
+ B(0x120000880110000, 0x1008000000020020),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x8420060100020000, 0x800280400000120),
+ B(0x4010020018010, 0x40A00001100000),
+ B(0x40006A0004000200, 0x40000000110),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x8908A20028110011, 0x800080000001A114),
+ B(0x200042000080F009, 0x20001000004000),
+ B(0x2820008820100, 0x10002400058000B9),
+ B(0x6083100420008050, 0x4040012600280080),
+ B(0x216020000000446, 0x4080204000000211),
+ B(0x340140003002089, 0x2402008000000911),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x8C0488120024214, 0x8414880202291),
+ B(0x1010000060050000, 0x4000004050002602),
+ B(0x4022983A0060000, 0x80000040010400),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x6020101802002840, 0x31000003000004),
+ B(0x9000420008840, 0x4881300000000210),
+ B(0xA200808865, 0x41C0048023000128),
+ B(0x31801100400000, 0x8802DC001221240),
+ B(0x884000080200920, 0x1004002410401001),
+ B(0x2400040000884, 0x421006208040C0),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x1800040009040200, 0x180000A1004E408A),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x4060402008080, 0xC240080000110000),
+ B(0x20080100920020, 0x2002248010242052),
+ B(0x10001010802050, 0x880000001C98420),
+ B(0x4000800100420022, 0x502022010A00D0),
+ B(0x4C18104500200885, 0x400880800),
+ B(0x8080810081020090, 0x8000000000000),
+ B(0x8000062812080201, 0x8004C8300800),
+ B(0x1800040009040200, 0x180000A1004E408A),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x80102204040, 0x1000000900000000),
+ B(0x2080000004202804, 0x120880003461),
+ B(0x102004090A4030, 0x801020589240),
+ B(0x20001100814000A0, 0x420202000820004),
+ B(0x100800000A000120, 0x208000800010000),
+ B(0x1008205000040802, 0x80002000400040),
+ B(0x1480000098008401, 0xA0010000581010),
+ B(0x30C0008200100820, 0x102800080904834),
+ B(0x4810821884000500, 0x4400000200000212),
+ B(0x1811D00128A0180, 0x2500848803000000),
+ B(0x41618A0300040040, 0x21200200A421801),
+ B(0x80102204040, 0x1000000900000000),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x8C890020410000A0, 0xA010000048000400),
+ B(0x40006002210044, 0x600008000408000),
+ B(0x1200447220090042, 0x80001000160012),
+ B(0x48410010AB000000, 0x9200600000000100),
+ B(0x2040000000240003, 0x8020080288000600),
+ B(0x9080000088848088, 0x4010210500000041),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x480100400024, 0x1004800018200000),
+ B(0x808403080080200, 0x802601000000500),
+ B(0x8C890020410000A0, 0xA010000048000400),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x400014006000000, 0x10006000810001F5),
+ B(0xC410062001414, 0x820080041B01044),
+ B(0x20000800310, 0x430040000201000),
+ B(0xA40010008000008, 0x4002200028000040),
+ B(0xC00102000008021C, 0x10C2000A010E024),
+ B(0x80004200104008, 0x50A00800C400020),
+ B(0x20200080012542, 0x910F0040000402C0),
+ B(0xB040100504000300, 0x24802002000040),
+ B(0x800001000014008, 0x400031004000),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x84008002041081C0, 0x8080500200000000),
+ B(0x440090001012001, 0x4020004010),
+ B(0x100A0028088020, 0x80040E00010020),
+ B(0x2180808000810, 0xB018040A00040000),
+ B(0x40C80920304C4001, 0x42800B200800000),
+ B(0x85000425001000, 0x4810048020001100),
+ B(0x600C000801000004, 0x8015084010200020),
+ B(0x20020050000240C0, 0x100202008600800),
+ B(0x38000050001220, 0x9200010200145900),
+ B(0x1042108040005, 0x1402A0802201001),
+ B(0x824240000C20400, 0x1000000400080010),
+ B(0x84008002041081C0, 0x8080500200000000),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x8000402308483, 0x20006020100100),
+ B(0x80880120000080, 0x8000240100084),
+ B(0x5840020004882001, 0x1004528000A00010),
+ B(0x8001018800300002, 0x84010040804),
+ B(0x180D10004000A008, 0xA001080008020004),
+ B(0x400080B, 0x10A0000004010000),
+ B(0x8080000200000, 0x2001000082004E0),
+ B(0x40040001000C2000, 0x2024800001004008),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x8000402308483, 0x20006020100100),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x2000200000, 0x1201011000802),
+ B(0x100100000000C4, 0x208004084048201),
+ B(0x400084000044, 0x100810140300),
+ B(0x29040C0C01010, 0x300204010820080),
+ B(0x1A808000020200, 0x1000000005210040),
+ B(0x20000400150000, 0x85008020),
+ B(0x40C040008184014, 0x8002AA00024010),
+ B(0x202000081B00804, 0x10001002008),
+ B(0x40011000210060, 0x6080C40000021004),
+ B(0x2000200000, 0x1201011000802),
+ B(0x4100480203840, 0x300080100804),
+ B(0x2000200000, 0x1201011000802),
+ };
+ constexpr Bitboard CannonMagicVInit[SQUARE_NB] = {
+ B(0x202000812104400, 0x24800B01C0000303),
+ B(0x340020400010D, 0x88060150C00400),
+ B(0x400802040609, 0x49010200501A0002),
+ B(0x8002680301000208, 0x628006C0C020200),
+ B(0x20400209001C0804, 0xA044000800143110),
+ B(0xC400082060010202, 0x4000480401014000),
+ B(0x22500200144040, 0x8204820084704C00),
+ B(0x8C1204009030020, 0x328400805000000),
+ B(0x84800800D0001640, 0x200080040060108),
+ B(0x804810208020040, 0x140010108020000),
+ B(0x1102010B008004, 0x300208006220020),
+ B(0x140080404A0A2428, 0x6308010100080),
+ B(0x20444002120408, 0xA080010508010001),
+ B(0x82011044000D02, 0x4112028620110809),
+ B(0x81010831000C02, 0x408802085000000),
+ B(0x81010831000C02, 0x408802085000000),
+ B(0x920008920600040, 0x8053801004000028),
+ B(0x81140283208300, 0x10040C004200420),
+ B(0x103080022201, 0xC01000081312620),
+ B(0x2200221100008, 0x1000408104000A4),
+ B(0x4402088080042008, 0x210401501040340),
+ B(0x898400202170001, 0x80040404208000),
+ B(0x20080004051012, 0x5100048200081800),
+ B(0x2320020000401018, 0x108501021040210),
+ B(0x21080410A422021, 0x83040180008800),
+ B(0x44E8100000408224, 0x20010008040400),
+ B(0x1800240002810405, 0x23004820000020),
+ B(0x80A0100400110, 0x80104020100C4028),
+ B(0x1002050001222C0, 0x5100818004024020),
+ B(0x104000200040, 0xC010A09800102000),
+ B(0x1020003A058120, 0x450900809000302),
+ B(0x40040045008B1, 0x202800400383010),
+ B(0x4640200220034, 0x8800485420304000),
+ B(0x5001042100084288, 0x110820001240080A),
+ B(0x2002C04004010120, 0xA15008020880001),
+ B(0x2800004080C4190, 0x890808280020080),
+ B(0x40C0401000104000, 0x2020880008002580),
+ B(0x40020C002400802, 0x801104010000000),
+ B(0x44842000040080, 0x2050011084000400),
+ B(0x4110040800000401, 0x2023810029008000),
+ B(0x20884000840, 0x8017102004008000),
+ B(0x10411104000480, 0x1414042000201001),
+ B(0x220040000008, 0x800306021000000),
+ B(0x41400A0008080, 0x501000298ACAD10),
+ B(0x800240012831810, 0x80120004468050E),
+ B(0x800005020801008, 0x20102400240000),
+ B(0x20C00040C114C010, 0x88080820200C00),
+ B(0x1044010100820081, 0x20080841004000),
+ B(0x8041048400022, 0x8020836040005002),
+ B(0x2001004010205, 0x8001002884042009),
+ B(0x128088400087, 0x20008002201002),
+ B(0x8084108040402000, 0x80809000A080400),
+ B(0x408081840880, 0x201002088000040),
+ B(0xA40180010280, 0x241004006000010),
+ B(0x4204100080048140, 0x2002C4F104202020),
+ B(0x100140A10204, 0x980200800840060),
+ B(0x1005140010202048, 0x1442280800202815),
+ B(0x2000082025008600, 0x1108400040600003),
+ B(0x1005050648000, 0x200020240008002),
+ B(0x202010208044000, 0x8210404060008),
+ B(0x8011040402000210, 0xC840180408016004),
+ B(0x404098801028, 0x80020A0001000400),
+ B(0x404098801028, 0x80020A0001000400),
+ B(0x80101002180140, 0x40C2080820000C0),
+ B(0x208202081260800, 0x14090E4C04000050),
+ B(0x4221201084004C2, 0x110480A011060),
+ B(0x8000008421090204, 0x1C01010800024),
+ B(0x8000008421090204, 0x1C01010800024),
+ B(0x200180C840088A0, 0x401100400820000),
+ B(0x10084043A021070, 0x202041600080200),
+ B(0x210E6202001040C, 0x10100800080B0),
+ B(0x848008021204002, 0x801004308100BAD),
+ B(0xC082C0390A000601, 0x4040080189008),
+ B(0x431200240210402D, 0x58102820000),
+ B(0x202020100A0019B0, 0x4010C0D018000000),
+ B(0x800800908402203, 0x102948C84C184),
+ B(0x26801100080845, 0x4009702022A00820),
+ B(0x8880520010401040, 0x1060084832052000),
+ B(0x100100022042081, 0x10000600008C121),
+ B(0x46020384100040, 0x800200320882021),
+ B(0xC0002010148, 0x4200800800040003),
+ B(0x2002208020090040, 0x40820210021410),
+ B(0x9000A41160002004, 0x2A09000100080043),
+ B(0x800004010008001, 0x1108002020104600),
+ B(0x800540C000A4E041, 0x18021180000401),
+ B(0x808200900A900202, 0x8364202140012005),
+ B(0x1DBA52000081010, 0x4008000023000010),
+ B(0x4100110204401481, 0x800040091020001C),
+ B(0x4100110204401481, 0x800040091020001C),
+ B(0x4101100020400482, 0x2000402302100120),
+ B(0x100408000A020212, 0xA000400111000020),
+ B(0x2000010488080104, 0x3000404410208100),
+ B(0x2684220180008DD0, 0x422040200004000A),
+ B(0x2021200C0424, 0x1010100000080200),
+ B(0x8908020020801006, 0x3010800020C2000),
+ B(0x4000030008062044, 0x244010202688000),
+ B(0x242101200408009, 0x8150040000200015),
+ B(0x42004C02180204, 0x210208014241040),
+ B(0x4E1A01C208410804, 0x8890041000012004),
+ B(0x2080200401000080, 0x8001098429008004),
+ B(0xA01400121804104, 0x280200C400000500),
+ B(0xD0080408040420, 0x1006040100224000),
+ B(0x28400205000800C9, 0x6021101401040075),
+ B(0x4000900040020104, 0x88129801100D0C),
+ B(0x8000004002180410, 0x400380200400204),
+ B(0x4002A430043008, 0x400200340100020),
+ B(0x401960004140A42, 0x100880710000464),
+ B(0x58014090102, 0xB8D30004010080),
+ B(0xA004C08000244000, 0x11280100E0000040),
+ B(0x2102008089208804, 0x110001004080040),
+ B(0x700010084E003004, 0x8080864112000D40),
+ B(0x4080881000200C20, 0x30324040880E0600),
+ B(0x2024A40401810820, 0x3000888002000000),
+ B(0x8200100400014, 0x4400340800252844),
+ B(0x24A00804288281, 0x410103002201140),
+ B(0x4080005022A08, 0x1000402200100264),
+ B(0x200080032244040, 0x200502189010001),
+ B(0x28108110404001, 0x400600120008412),
+ B(0xA00002102810020, 0xB1080240015408),
+ B(0x810080200806, 0x410440804080046),
+ };
+ constexpr Bitboard HorseMagicInit[SQUARE_NB] = {
+ B(0x3C080482A592000C, 0x540104000020000),
+ B(0x2802C40008000420, 0x4A00000001818009),
+ B(0x1083040280804000, 0x120004C20100880),
+ B(0x6840940880000892, 0x2014A01080800C2),
+ B(0x8401489004000180, 0x2000800000400000),
+ B(0x820161C800000110, 0x8000100000204020),
+ B(0x610011A122000109, 0x1000004020008004),
+ B(0x83282004023000, 0xE000020004848446),
+ B(0x6840940880000892, 0x2014A01080800C2),
+ B(0x4020120800800002, 0x88008000010020),
+ B(0x30025B140A1000, 0x3141801401000040),
+ B(0x41104D1810100050, 0x8141002010910),
+ B(0x4200828A298400, 0x400340001040C000),
+ B(0x8016A4900110040, 0x844812001068020),
+ B(0x2250035820400A2, 0x8012010080900),
+ B(0x820080083A009000, 0x880404091080110),
+ B(0x80401500AF0020, 0x240000082201A04),
+ B(0x668020020C081005, 0x4008001004100021),
+ B(0x240100910000000, 0x82000A0030454000),
+ B(0xA24091400008, 0x200014880004A921),
+ B(0x840110042200410, 0x100080000A400000),
+ B(0x40024024102000, 0x1000000002180404),
+ B(0x92828423000530, 0x118800020110),
+ B(0x1122404A1C90A8, 0x822040280020D00),
+ B(0x41201A40900A000, 0x80C0480040605100),
+ B(0x2504A85005488280, 0x3028112120022800),
+ B(0x210180080626B048, 0x8000401000014000),
+ B(0x1000410401040200, 0x41014000050C0106),
+ B(0x1040650210802200, 0x80C0041000000),
+ B(0x4020C10110900002, 0x2140C2001050009),
+ B(0x191180092200022, 0x6010008400400800),
+ B(0x8010821088080202, 0xCA240011008208),
+ B(0x8C0488120024214, 0x8414880202291),
+ B(0x8C0488120024214, 0x8414880202291),
+ B(0x22080C8A0161401, 0x200C10004C002002),
+ B(0x8430818023034080, 0x210090800000801),
+ B(0x4845087008200, 0x40661480000),
+ B(0x1202804428812050, 0x100022038020000),
+ B(0x400016001201080, 0x24002200402060),
+ B(0x680E041300800800, 0xE00130080004000),
+ B(0x3409080200, 0x282840210000000),
+ B(0x803310108400, 0x85200000080100A0),
+ B(0xE180008A04162104, 0x9088240412404),
+ B(0x20080100920020, 0x2002248010242052),
+ B(0x8A000400C2410, 0x1000024086014300),
+ B(0x1821040024663, 0x100000100010009),
+ B(0x4000822310611, 0x120280406014008),
+ B(0x1004008010818D08, 0x800000141892000),
+ B(0x8010800004024042, 0x44B106008800896),
+ B(0xA0063423444, 0x41002C15811008),
+ B(0x2040012381001282, 0x4804080104A4000),
+ B(0x10840101820880, 0xA800008000020020),
+ B(0x10840101820880, 0xA800008000020020),
+ B(0x60201D8300408190, 0x2010020920200000),
+ B(0x4048100200090090, 0x2008090100000900),
+ B(0x24200000280210, 0xD440050008004000),
+ B(0x1280001000580020, 0x2200040089000A4),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x4108000010209089, 0x913000000024840),
+ B(0x410C208008008E02, 0xE8000000000001),
+ B(0x802208004005, 0x94206000022080),
+ B(0xC00290018902002, 0x4204100000000000),
+ B(0x2102801400093816, 0x9810004001000202),
+ B(0x8008304000015800, 0x4A5C000000020000),
+ B(0x1020108380800514, 0x1144210000000080),
+ B(0xC0001000008090, 0x2812060000204000),
+ B(0x1001100200003100, 0x246240060A004004),
+ B(0xA00020A008002030, 0x2440C40000110B00),
+ B(0x80502104000C008, 0x8222200042100010),
+ B(0xC020200088014, 0x422094000000480),
+ B(0x1029002000001030, 0x8105841120000210),
+ B(0x49040D, 0x2310808A14042C0),
+ B(0x200040200080A02C, 0xB890290400080000),
+ B(0x2240180C0800002, 0x4151050280000100),
+ B(0x2240180C0800002, 0x4151050280000100),
+ B(0x8220224180420006, 0x4024501212011000),
+ B(0x1806810A0881000, 0x802002048400080),
+ B(0x400400A080842, 0x9305000401180000),
+ B(0x10008001444110, 0x4420401040041833),
+ B(0x2000002C02010E00, 0x400408D08009804),
+ B(0x69D008200020100, 0x100842240049021),
+ B(0x42C24450020000, 0xD38400880090884),
+ B(0x485800800100001, 0x2484086522018840),
+ B(0x900200020820042, 0x22302421400040C0),
+ B(0x50B0413001818000, 0x452014040800C40),
+ B(0x8004040021008, 0x20088A08000290),
+ B(0x600C000801000004, 0x8015084010200020),
+ B(0x208000C00, 0xE004804021100100),
+ B(0x20001000040204, 0x948110C0B2081),
+ B(0x268502400100021, 0x80A201840802080),
+ B(0x408C000008, 0x8822102408014),
+ B(0x1182080410100000, 0x608002046A0100),
+ B(0x100820A083C00002, 0x3100100410A00),
+ B(0x8401040000400124, 0x2000081288202200),
+ B(0xB014040003000800, 0x11960D1101210),
+ B(0x10040001900C000, 0x85603C1001280),
+ B(0x2000844000000100, 0x2000024C60800800),
+ B(0x120004234800900, 0x210010841040),
+ B(0x8010300040000002, 0x4200008222104100),
+ B(0x1000120402200100, 0x209080CC040108B4),
+ B(0x110049A00000800, 0x80000420022180A8),
+ B(0x80001C00080384, 0x1400101111081001),
+ B(0x8011200008100428, 0x2020000880800922),
+ B(0x10001000000204C8, 0x280C11104240),
+ B(0x50100C82C000500, 0x28000280618DD1),
+ B(0x8800498020000, 0x20500A0200320128),
+ B(0x20010104000860, 0x8021720186008),
+ B(0x4000000000100080, 0x35040084270C04),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0x10400000000000, 0x20080051100130C2),
+ B(0x10400000000000, 0x20080051100130C2),
+ B(0x2000002110202014, 0x121004004004681),
+ B(0x400202001006D40, 0x82240082202424),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0xC6000000D00804, 0x1050020C0081090C),
+ B(0x200080000000042, 0x10800661),
+ B(0x2000001011200200, 0x2A420000802A0222),
+ B(0x802020001202412, 0x2400404148426),
+ B(0x8000440801040002, 0x444002800010052A),
+ };
+ constexpr Bitboard ElephantMagicInit[SQUARE_NB] = {
+ B(0x64D2990200008, 0x4401880001C000),
+ B(0x29BAA00010020, 0x200000400800600),
+ B(0x3024240000000, 0x4100400010080),
+ B(0xA490A00480020, 0x20084001041010A4),
+ B(0x328C021008042, 0x100000000C10204),
+ B(0x1964090001018, 0x7002040148001205),
+ B(0x800302098404080, 0x4983020000000001),
+ B(0x8812244630A02080, 0x8200006204003C08),
+ B(0x41120231008000, 0x240441401020),
+ B(0x840091030C00040, 0x1400008200023400),
+ B(0x8001040E77030200, 0x100040090022000),
+ B(0x602022139D835040, 0x101002010025900),
+ B(0x405707C48400, 0x40010000008001),
+ B(0x982003456A82050, 0x60800820040030),
+ B(0x204184849200088, 0x101800004006),
+ B(0x300222470949200, 0x2A0800200200800),
+ B(0x400001211914000, 0x8200001407001),
+ B(0x2000008614831020, 0x4000020001404000),
+ B(0x84000024A2048048, 0x1200102000042),
+ B(0x424010A58422008, 0x88440242212A0110),
+ B(0x20020812C0C4408, 0x4121400000080010),
+ B(0x680200062042420, 0x2001100000800000),
+ B(0x200010060AEC855, 0x8083002040200000),
+ B(0x4000008BAA85810, 0x82000805C0200A90),
+ B(0x81450B200A025400, 0x4400101050000040),
+ B(0x820A2241409010, 0x888420030000),
+ B(0x909203000028, 0xC000004C00200041),
+ B(0x8021400A84880240, 0x100180002010020),
+ B(0x8001A20061410000, 0x14008499A000000),
+ B(0x8201444800A00080, 0x402010040588120),
+ B(0x100C06280020, 0x60010104840130),
+ B(0x520040800080044, 0x8220000080001402),
+ B(0x102021410040202, 0x2004400410006000),
+ B(0x5401832090020400, 0x300010020001),
+ B(0x180003105A84C108, 0x1012008800081000),
+ B(0x480C10210026904, 0xA006000004200418),
+ B(0x48050820210843A6, 0x108001004000C00),
+ B(0x1030101182206324, 0x4401008921502002),
+ B(0x40281060800800, 0x406000201260022),
+ B(0xC29002440040C820, 0x400001002008020),
+ B(0x40000400800241, 0xC220000000400280),
+ B(0x40880126014208, 0x2A8004C008940000),
+ B(0x121028100114080, 0x5010280481100082),
+ B(0x4000088280442, 0x908420140008041),
+ B(0x808C42400C0020, 0x3028100840801000),
+ B(0x4000000410078488, 0x501000000620000),
+ B(0x90080001421020A4, 0x4118400101060406),
+ B(0x280420004855, 0xD200100400820000),
+ B(0xA0063423444, 0x41002C15811008),
+ B(0x200061201808102, 0x4286969000200002),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x4001A04880402000, 0x8100824080000001),
+ B(0x60201D8300408190, 0x2010020920200000),
+ B(0x20018C04908019, 0x2010884002002040),
+ B(0x800000000C40810, 0x680100081150000D),
+ B(0x2002002000040040, 0x8810049000010600),
+ B(0x41618A0300040040, 0x21200200A421801),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x5A04001400412854, 0x8A44006000010002),
+ B(0x13000C0810072432, 0x50049001021104),
+ B(0x400048801142130, 0x4C1204100226010C),
+ B(0x80001048, 0x408800104000080),
+ B(0x8104868204040412, 0x22244202000081),
+ B(0x8104868204040412, 0x22244202000081),
+ B(0x4140001000240440, 0x80209004410004E),
+ B(0x800800000100, 0xB111820100000002),
+ B(0x404240004220, 0x2110402802050080),
+ B(0x284010400004040, 0x100245002502020),
+ B(0x14880A100114010, 0x400208080010024),
+ B(0x4100004040440648, 0x10030D838041A80),
+ B(0x32004000210, 0x4010225C88014000),
+ B(0x2240180C0800002, 0x4151050280000100),
+ B(0x2010A12002000042, 0x189051442010000),
+ B(0x4060050080121883, 0x8250C10001000141),
+ B(0x10000000044100, 0x8401084010261009),
+ B(0xA00028040000, 0x2003224000002000),
+ B(0x2060009001000020, 0x1000432022020228),
+ B(0x404200000883080, 0x1080800848245000),
+ B(0x240000402080, 0xCA0820814210502),
+ B(0x200040200080A02C, 0xB890290400080000),
+ B(0x800000000300482, 0x9203008100100013),
+ B(0x8000210202042000, 0x22642104004C2400),
+ B(0x1040400805000401, 0x2A0300102C80010),
+ B(0x8010A01088020000, 0x122106105A06A030),
+ B(0x8000C00001010494, 0x130A1A20404120),
+ B(0x4B084010844290, 0x10A08008900840),
+ B(0x1180001802460000, 0xB08000034C82004),
+ B(0x4001880060028029, 0x204040002401000),
+ B(0x8021A0001308002A, 0x97001822040040),
+ B(0xC00000009A020AC1, 0x1000080900400),
+ B(0x60010110001990, 0x4000880900400000),
+ B(0x10290402401200, 0x230080402C08),
+ B(0x4220000219012000, 0x140204804100008),
+ B(0x1400200100002, 0x8E62200414128),
+ B(0x402808502004403, 0x20049100C0284520),
+ B(0xB30041004280280, 0x10020464DB200308),
+ B(0x440010800808, 0xA0102E295812100),
+ B(0x10008000B000, 0x2000058583220200),
+ B(0x2000844000000100, 0x2000024C60800800),
+ B(0x110000400100028, 0x24052304508004),
+ B(0x8458000000840004, 0x118006463400001),
+ B(0x804008000040050, 0x41044890228000),
+ B(0x20000050000400, 0x80A101824A00086),
+ B(0x600080404000020, 0x100007322480005),
+ B(0xD082200020020008, 0x642000630120001),
+ B(0x10000100040230, 0x8048114733320002),
+ B(0x20200442002A880A, 0x8200002CB4B8052),
+ B(0x290080000000, 0xA41297838F40D),
+ B(0x800205000080, 0xF221232039874400),
+ B(0x1444002004880C20, 0xC4100049144200),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0x281240881008, 0x204084004C101900),
+ B(0x1444002004880C20, 0xC4100049144200),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0xC0010928430540, 0x92041902180),
+ B(0x1051001208A, 0x4900064800C20640),
+ B(0x882020418C00000, 0x30004040092A821),
+ B(0x224404002004268C, 0x202500204C7D254),
+ B(0x290080000000, 0xA41297838F40D),
+ };
+ constexpr Bitboard JanggiElephantMagicInit[SQUARE_NB] = {
+ B(0xC502282200061400, 0x2D07081241D90200),
+ B(0xC502282200061400, 0x2D07081241D90200),
+ B(0x8084810022440C2, 0x81402202004),
+ B(0x80204010A800500, 0x5000021001740218),
+ B(0x8048100401208000, 0x2001000390000044),
+ B(0x202080020000000, 0x4010800010090424),
+ B(0x4081A0480073200, 0x100000A010406000),
+ B(0x4081A0480073200, 0x100000A010406000),
+ B(0x2040450004000C40, 0x8400000006302),
+ B(0x84010410018201, 0xA00A00000100000),
+ B(0x840091030C00040, 0x1400008200023400),
+ B(0x801058C0A0022, 0xC1920480010034),
+ B(0x80B4004800840800, 0x4080210A42040010),
+ B(0x400402221000445, 0x80321200408040),
+ B(0x4028142401012A00, 0x4005009000104448),
+ B(0x1440102040800220, 0x82800010A082000),
+ B(0x4100040300C00200, 0x800805100120000),
+ B(0x8200080061100, 0x2000101400000),
+ B(0x2000100410070001, 0x40818200B0900410),
+ B(0x400088020080000, 0x4A000402000CA0),
+ B(0x1402040410004000, 0x9840044504040),
+ B(0x20800088A00A0400, 0x1000020100180),
+ B(0x2001820520308201, 0x2008003404349000),
+ B(0x4004808022100, 0x8001000008081080),
+ B(0x102041041100425, 0x840400180B100104),
+ B(0x8806446000800214, 0x404402100010000),
+ B(0x8200141409C04101, 0x209030004A00D00),
+ B(0x8806004800880080, 0x1560004201000A01),
+ B(0x4200050600200090, 0x1CD0000000000421),
+ B(0x4820100022408100, 0x101404080320),
+ B(0x2A000A0A08080080, 0x1C02808000C2C0),
+ B(0x8808425040040014, 0x2021000100020),
+ B(0x5282104044A0020, 0x6B402104200008),
+ B(0x4001091040068120, 0x202000004003031),
+ B(0x4001091040068120, 0x202000004003031),
+ B(0x98040200A0214344, 0xA00300840010),
+ B(0x82508040A40808A, 0x40010000110042),
+ B(0x4400100101023, 0x450C8480040022),
+ B(0x210588880010800, 0x800A000108018102),
+ B(0x9400010144400, 0xC00010100018000),
+ B(0x20A0400100040004, 0x1242000101002040),
+ B(0x8022900040001001, 0x100000014000260),
+ B(0x51004124000A080, 0x40098400000002),
+ B(0x2158040001080022, 0x80009238401222),
+ B(0xA0103A0000802220, 0x20000200400010),
+ B(0x1101001208240, 0x100000800001064),
+ B(0x821020002090081, 0x5840D0010290280),
+ B(0x821020002090081, 0x5840D0010290280),
+ B(0x10400C1042000400, 0x4005000000440200),
+ B(0x844022008804820, 0x1000800100118000),
+ B(0x10802A9800800139, 0x4802840100842200),
+ B(0x4000A008200081, 0x4001100200402000),
+ B(0x200000008108400, 0x1000C00008080020),
+ B(0x120C11500100081, 0x440300308041100),
+ B(0x8080040080060100, 0xC00101B0040028),
+ B(0x901420A00110000, 0x8200010044700280),
+ B(0x140080080410000, 0x808040000C001001),
+ B(0x80210C0200A0008, 0x88088004600201),
+ B(0x8000004202020301, 0x2100142104002000),
+ B(0x1101011210004880, 0x8500840400000000),
+ B(0x40208802004800, 0x8080806009011240),
+ B(0x800000140408880, 0xC001018004060040),
+ B(0xC008080420500, 0x8024A10000000000),
+ B(0x2800000000400010, 0x44001C00400408),
+ B(0xA804008001200408, 0x202000020001000),
+ B(0xC08288805004080, 0x200042000800004),
+ B(0xA40A01000080012, 0x8800080042408),
+ B(0x2200100000100810, 0x800200010000100),
+ B(0x9881800004040001, 0x8058100100884004),
+ B(0x820000044020014, 0x4AA00010245012),
+ B(0x820000044020014, 0x4AA00010245012),
+ B(0x4000080240000808, 0x10100022054000),
+ B(0x5002000840101, 0x202020004000A00),
+ B(0x1188008200008402, 0x8088100020A2204),
+ B(0x304012004044080, 0x8028108818006010),
+ B(0x102210000008400, 0x1008000200380002),
+ B(0x51410E114200, 0x100C00084000000),
+ B(0x5001242320218, 0x800025000040040),
+ B(0x4008000200008190, 0x400020021000000),
+ B(0x10910022F0040, 0x450084400040001),
+ B(0x180010810000040, 0x4004100040040),
+ B(0x1088801424062010, 0x400084010030401),
+ B(0x3000120408000040, 0x10802001080A4051),
+ B(0x200008420, 0x40C0100020008804),
+ B(0x1048C000004000, 0x4220120804004000),
+ B(0x404A180000000E, 0x4C30412008110102),
+ B(0x400000404202005, 0x800808550EC40044),
+ B(0x282000200212010, 0x8001C0C102000210),
+ B(0x9012240000008100, 0x280CA04010040000),
+ B(0x2000C04001020C00, 0x2002010101042000),
+ B(0x1010000204408408, 0x8008004800E0C4A),
+ B(0x800286801000025, 0x8402401040050088),
+ B(0x40002000A0880000, 0x8400300108082086),
+ B(0x2080004404011, 0x20C080400100001),
+ B(0xB0010218100800, 0x8040200482C14103),
+ B(0x8011035000000C20, 0x4200044043200040),
+ B(0x804008000040050, 0x41044890228000),
+ B(0x80000400A0020020, 0x5308022021000000),
+ B(0x2118200000008004, 0x4141014004423D00),
+ B(0x90C0000200008040, 0x41041062000082),
+ B(0x1D000100941204, 0x12402001200420),
+ B(0x8C0040400400065, 0x22300B408100000),
+ B(0x8C0040400400065, 0x22300B408100000),
+ B(0x802802044600000, 0x1210100401030082),
+ B(0x9400488010000000, 0x8005404902040000),
+ B(0x2214020200001, 0x40102100820200),
+ B(0x2022000000800000, 0x6400440108480),
+ B(0x110000400100028, 0x24052304508004),
+ B(0x848820140010000, 0x201012500A000),
+ B(0x848820140010000, 0x201012500A000),
+ B(0x100100000000C4, 0x208004084048201),
+ B(0x100500000000290, 0x10102818208000),
+ B(0x2800414000C000, 0x20004005001301),
+ B(0x698180005101241, 0x10002014800210),
+ B(0x20000080000009, 0x440340C040),
+ B(0x1C0220200290020, 0x42100004004011C0),
+ B(0x200E620018320208, 0x440410402),
+ B(0xD04101010004024, 0x20000121104010A4),
+ B(0x220400000A80040, 0x806080020810010C),
+ B(0xA000200000000080, 0x1040801A0081208),
+ };
+ constexpr Bitboard CannonDiagMagicInit[SQUARE_NB] = {
+ B(0x811801000400, 0x312260280280202),
+ B(0x44A000402022680, 0x1020224880420005),
+ B(0x8000C80800200880, 0x2000810060080C0),
+ B(0x2010300240428040, 0x40240002C004E30),
+ B(0x1018010404010004, 0x1001010018081E0),
+ B(0x2042040010080090, 0x100000008410300),
+ B(0x400080020102000, 0x4500005300000000),
+ B(0x2D00C80420010200, 0x804003280020008),
+ B(0x8038820024420, 0x6010010080012040),
+ B(0x1202028004200088, 0x50018100004000C6),
+ B(0xA02010F0410081, 0x20013001000009A),
+ B(0x4013002041030588, 0x4802004110000004),
+ B(0x110020802000081, 0x202001800908002),
+ B(0x22010404103, 0x2020882080491200),
+ B(0x60000220400580, 0x85902800100100),
+ B(0x100080800050100, 0x200010220021088),
+ B(0x8088840404200080, 0x140011040104000),
+ B(0x4008508080082015, 0x8010100200580048),
+ B(0x4010400420201001, 0x260002080A80808),
+ B(0xC2002004A0008008, 0x8020082000110840),
+ B(0xA000A0820042400, 0x810408082100420),
+ B(0x80231808100004, 0x204002000800400),
+ B(0x8296144044004900, 0x4A1003008001840),
+ B(0x80A0020A0011008, 0x800104846080810),
+ B(0x803800801041000, 0x1030500102000404),
+ B(0x240C00900800850, 0x1804000108810000),
+ B(0x800400000088800, 0x800021801020000),
+ B(0x84800409300082, 0x1002D40680044000),
+ B(0xA110C0000200010, 0x401010001200260),
+ B(0x8200160204100004, 0x8040004004002022),
+ B(0x10001000000100C0, 0x84002811000200),
+ B(0x2000080020014001, 0x42002020000102),
+ B(0x109040044020018, 0x2020400202001000),
+ B(0x620000CD0108, 0x40040201008000),
+ B(0xA1402200A0020, 0x81400400300912),
+ B(0x20020CF100018020, 0x801A14086404000),
+ B(0x800801844001, 0x11621488425000),
+ B(0x10201004A8080, 0x100A000801000010),
+ B(0x2800411001000800, 0x80224084900020),
+ B(0x40400024028100, 0x501000400230060),
+ B(0x404808010080, 0x1201000400100004),
+ B(0x80802005200, 0x2000200008A0000),
+ B(0x20800080000022, 0x80040810002010),
+ B(0x40016004808240, 0x400114000801100),
+ B(0x8410004204240, 0x20011000604050),
+ B(0x8000C1009008268, 0x201004000209000),
+ B(0x10240C000920, 0xE000A5C14003002),
+ B(0x10184024280008, 0x90240802000000),
+ B(0x40889081081000, 0x8010050008800000),
+ B(0x100008C089000019, 0x802032014020010),
+ B(0x401C1804C00, 0x402501002002020),
+ B(0x200022000920D0, 0x8000800081300020),
+ B(0x801000400011, 0x400100044010226),
+ B(0x4A04010100100000, 0x500400080400000),
+ B(0xA000050200080000, 0x8500090001010000),
+ B(0x40400040001812, 0x4403000400100A0),
+ B(0x20C2250203020004, 0x210001C000080000),
+ B(0x21000408C6020001, 0x4200830012D1001),
+ B(0x840082016080A210, 0x2400080801081008),
+ B(0x40001020000, 0x4041240200083120),
+ B(0x2C04030010C0818, 0xA670002000818100),
+ B(0x4704A07085000510, 0x914001000040),
+ B(0x900210304100100, 0x1010004000281840),
+ B(0x8202920002002040, 0x810012000003),
+ B(0x4001400100050, 0x1144000408002000),
+ B(0x5900200020008100, 0x40200020002004),
+ B(0x301020002000480, 0x202000C0004),
+ B(0x20D000201104040, 0x34840100020010),
+ B(0x800004200080408, 0x40184200100240),
+ B(0x8430080100404020, 0x90042100244500),
+ B(0x3800100010220062, 0x50404030200218),
+ B(0x42E20008002020, 0x2000008200200300),
+ B(0xE488008280A004, 0x200001010CC80000),
+ B(0x6018010041109810, 0x800002000242041A),
+ B(0x40A8002438980, 0x8000810008208009),
+ B(0x401000480040100, 0x286800404002212),
+ B(0x821030000100009, 0x2000090200A00000),
+ B(0x20000100C0008028, 0x5000000100400082),
+ B(0x80A000048030080, 0x200000120200008),
+ B(0x6300280800204003, 0x48000105C0040100),
+ B(0x83008802420C0200, 0x2008020200080100),
+ B(0x1050C3102200042, 0x20103900010008),
+ B(0x8040902021408180, 0x12000021806200A4),
+ B(0x3008204008C10004, 0x680110100010401),
+ B(0x204321100421000, 0x400E204820494000),
+ B(0x8000044022404048, 0x4024010090024021),
+ B(0x140201424050, 0x280A000130008000),
+ B(0x900340808004002, 0x21026008000380),
+ B(0x82808000300444, 0x20002000A2001141),
+ B(0x140180100406002, 0x4004480001000004),
+ B(0x4808420800841900, 0x14008C0041000000),
+ B(0x2008600009000480, 0x9008020001400000),
+ B(0x2000100800100002, 0x2004100820210020),
+ B(0x2062010401A8100, 0x12200108420090),
+ B(0x1403188200032, 0x40048166105000),
+ B(0x410020020140041, 0x4400348102940040),
+ B(0x414040209208041, 0x4402400028B004),
+ B(0x8008010100421202, 0x401418002008800),
+ B(0x4000020010062200, 0xA02009148048000),
+ B(0x4443080082008B, 0x104014022801010),
+ B(0x42B440A0C000800, 0x9001009016111020),
+ B(0x400000214002, 0x8008080209020009),
+ B(0x480C414A001900, 0x3400100400210200),
+ B(0x1006008800604, 0x20240004030A050),
+ B(0x4C022401002A8300, 0x405008400000600),
+ B(0x3104000800A1042, 0x2004800204406200),
+ B(0xA09010280008200C, 0x4004000208C4168),
+ B(0x2800401120C20120, 0x4A00450200022030),
+ B(0x88001800304C0200, 0x204288102080000),
+ B(0x8044004201440101, 0x400820080C024022),
+ B(0xA000100C080, 0x4B40341004008081),
+ B(0x94802001300810, 0x140206008000800),
+ B(0x40002020202820, 0x280680404000040),
+ B(0xA820800004200, 0x80E1401012000491),
+ B(0x804000010020C000, 0x9403020200802000),
+ B(0x8C0001284201400, 0xC000100C01620800),
+ B(0x4010004002200414, 0x403080080200000),
+ B(0x140400A100800101, 0x10054C031080400),
+ B(0x20012C2400880082, 0x7000880020C03200),
+ B(0x204040300004, 0x840800041101002),
+ };
+ constexpr Bitboard NightriderMagicInit[SQUARE_NB] = {
+ B(0x8008100800020052, 0x8001440000000000),
+ B(0x24028400210090, 0x4200000000021),
+ B(0x22002020A0200800, 0x100820120000082),
+ B(0x424009020002200, 0x84810D4100002A),
+ B(0x404008020000600, 0x1000202000000081),
+ B(0x21020001000680, 0x2140200000000000),
+ B(0x2060021100000608, 0x8080020040000C8),
+ B(0x910C20408001200, 0x80A4030800000500),
+ B(0x2C40100010400009, 0x40400000200000C),
+ B(0x4090210034400004, 0x2104008000000),
+ B(0x1082008040840180, 0x140082080000000F),
+ B(0x8041001002101002, 0x8C002000000),
+ B(0x102205086800080, 0x84400030020D600),
+ B(0x2120080A32044E0, 0x400A02000016000),
+ B(0x6040003021040A0, 0x8000200206040040),
+ B(0x8001000008104082, 0x4002104202000114),
+ B(0x100400081080000, 0x201020020000020),
+ B(0x203000090010040, 0x140040010000000C),
+ B(0x9140000108100040, 0x4004004C08450010),
+ B(0x4402A12120020000, 0x450C002C000040C),
+ B(0x2028000200120800, 0x100100401001000),
+ B(0x800890081000, 0x10700A080180048),
+ B(0x41040C4008400, 0x8080001000080020),
+ B(0x2404000001060401, 0x10800116C0009110),
+ B(0x2001810020400110, 0xA0100800000480E2),
+ B(0x4081400120200218, 0x8800410800000000),
+ B(0x4C020020300E00, 0x810800000000808),
+ B(0xC8002200100108, 0x4860040380000100),
+ B(0x8000E0000804080, 0x4C800000000000),
+ B(0x211880010934041, 0x31000040000188),
+ B(0x4800801000003140, 0x80000000028),
+ B(0x61240410840004, 0x230100050000002),
+ B(0x61240410840004, 0x230100050000002),
+ B(0x4020008000C04C42, 0x3000021000000804),
+ B(0x829808040040840, 0x288010000002200),
+ B(0xA0420002040084, 0x1008604002000048),
+ B(0x10200004980, 0x2010410028000000),
+ B(0x4900200420100010, 0x1000A00401001898),
+ B(0x20000801600134, 0xC0002400000021C),
+ B(0x200040021010001, 0x8800000400000088),
+ B(0x88000824, 0x8800000040010000),
+ B(0x40000000200000D0, 0x8200004000AA84),
+ B(0x1002000000812002, 0x4000001C1008480),
+ B(0xC00010420003, 0x400002C00000004),
+ B(0x208400008008040, 0x111004280000C0),
+ B(0x60004000E800241, 0x202001000010040),
+ B(0x200004800061, 0x1109100480045800),
+ B(0x52880000200001, 0x608204100012DA0),
+ B(0x1000429008100800, 0x2008001000006018),
+ B(0x8490024200123001, 0x1041400080020),
+ B(0x810001020200014, 0x2C50500220000020),
+ B(0x20234B8000A0080, 0x4091200200000020),
+ B(0x400401010012600, 0xE00200000000020),
+ B(0x8040C00804004000, 0x8208014000000083),
+ B(0x102000410001044, 0x8200000000000),
+ B(0x102000410001044, 0x8200000000000),
+ B(0x242E00508040001, 0x80028000000000),
+ B(0x40810368002080, 0x4008080000224),
+ B(0x48200010208800, 0x1020800C000022),
+ B(0x220C4018008000, 0x282100130000211),
+ B(0x8008004400100, 0xC128208200020400),
+ B(0x44C004100020, 0x4008004000011000),
+ B(0x1008000000600008, 0x41C00180480183),
+ B(0x10080000, 0x404090018800020A),
+ B(0x4500000000004200, 0x45200200A0140),
+ B(0x2040000000240003, 0x8020080288000600),
+ B(0x2040000000240003, 0x8020080288000600),
+ B(0x2040000000240003, 0x8020080288000600),
+ B(0x48000000020A0080, 0x1000020200088),
+ B(0xA00000008404200, 0x4204010101001004),
+ B(0x10008030082, 0x2002800200100104),
+ B(0x44080002460010, 0x8007804100082D00),
+ B(0x98400040001802, 0x913002002020301),
+ B(0xA0400501040028, 0x8040481824002024),
+ B(0x4C0A0600100080A0, 0x8005000080008200),
+ B(0x802124000860008, 0x1009081800100080),
+ B(0x4000110040011000, 0x4080422200001081),
+ B(0x422008008000080, 0x4001020C00000202),
+ B(0x8000202008002101, 0xA008010000040),
+ B(0x804200020000020, 0xE0B0008010000001),
+ B(0x14040200002, 0x2000000006000004),
+ B(0x45000002010020, 0xA4040008400000),
+ B(0x800040020204184A, 0x10000402000006),
+ B(0x908000080400004, 0x5040100038400050),
+ B(0x3100002000040014, 0x804008008120000),
+ B(0x6002200000020008, 0xC011409081008881),
+ B(0xC02004060280001, 0x2003004010220254),
+ B(0x16410101, 0x100040108600000),
+ B(0x100000000200000, 0x201200010800200),
+ B(0xC400000004090002, 0x808808010224000A),
+ B(0xC400000004090002, 0x808808010224000A),
+ B(0x1000000000890001, 0x4050002001040),
+ B(0x618000000008484, 0x40000A0150000D4),
+ B(0x500000400009004, 0x1100200204100A1),
+ B(0x6006000008080480, 0x100254800C00904),
+ B(0x6006000008080480, 0x100254800C00904),
+ B(0x404808040044040, 0x4202020040000200),
+ B(0x2000080440810, 0x9044008844920000),
+ B(0x40204418040, 0x412000204080010),
+ B(0x88400820040248, 0x408040010C040800),
+ B(0x8110084000803, 0x102000000000),
+ B(0x4000040140001100, 0x80808102002),
+ B(0x100480044020008, 0x4601000100084488),
+ B(0x6200204420840, 0x41802043B0000),
+ B(0x540000010091010, 0x409004800A01208),
+ B(0x300040008000008, 0x2030080800010000),
+ B(0x408804010400090, 0x4008100800004000),
+ B(0x401600A0000080, 0x8400200201002200),
+ B(0x44000000100000, 0x1140040210000001),
+ B(0xC6000000D00804, 0x1050020C0081090C),
+ B(0x4098002, 0x900000C045008300),
+ B(0x245190008020, 0x2000C00100041000),
+ B(0x1000000A100046, 0x88000084800004),
+ B(0x1000000220240002, 0x400000401001088),
+ B(0x1000000220240002, 0x400000401001088),
+ B(0x820000118808000, 0x8000048161004A),
+ B(0x1100000000071A02, 0x500146102000048),
+ B(0x610000, 0x10010080040641),
+ B(0x2000010441A0044, 0x500800502020188),
+ B(0xA80000000180000, 0x234402012110080),
+ };
+ constexpr Bitboard GrasshopperMagicHInit[SQUARE_NB] = {
+ B(0x120000880110000, 0x1008000000020020),
+ B(0x24200C080840A052, 0x2004004000010008),
+ B(0xC030024000228800, 0x4000010400000020),
+ B(0x1A0020802008802, 0x206010208000),
+ B(0x12002000D001024, 0x80100800090138),
+ B(0x4220010000241010, 0x3098000602001500),
+ B(0x401010004801040, 0x8000280480100000),
+ B(0x820082024921836, 0x220028000),
+ B(0x100400502411400, 0x220402120240D14),
+ B(0x880202020010404, 0xA80202510000),
+ B(0x140002801000018, 0x1000346490040),
+ B(0x120000880110000, 0x1008000000020020),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x8420060100020000, 0x800280400000120),
+ B(0x4010020018010, 0x40A00001100000),
+ B(0x40006A0004000200, 0x40000000110),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x8908A20028110011, 0x800080000001A114),
+ B(0x200042000080F009, 0x20001000004000),
+ B(0x2820008820100, 0x10002400058000B9),
+ B(0x6083100420008050, 0x4040012600280080),
+ B(0x216020000000446, 0x4080204000000211),
+ B(0x340140003002089, 0x2402008000000911),
+ B(0xD01004008030400, 0x104000408104420),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x8C0488120024214, 0x8414880202291),
+ B(0x1010000060050000, 0x4000004050002602),
+ B(0x4022983A0060000, 0x80000040010400),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x6020101802002840, 0x31000003000004),
+ B(0x9000420008840, 0x4881300000000210),
+ B(0xA200808865, 0x41C0048023000128),
+ B(0x31801100400000, 0x8802DC001221240),
+ B(0x884000080200920, 0x1004002410401001),
+ B(0x2400040000884, 0x421006208040C0),
+ B(0x1404040B20001000, 0x8000824010800011),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x1800040009040200, 0x180000A1004E408A),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x4060402008080, 0xC240080000110000),
+ B(0x20080100920020, 0x2002248010242052),
+ B(0x10001010802050, 0x880000001C98420),
+ B(0x4000800100420022, 0x502022010A00D0),
+ B(0x4C18104500200885, 0x400880800),
+ B(0x8080810081020090, 0x8000000000000),
+ B(0x8000062812080201, 0x8004C8300800),
+ B(0xC010220920198, 0x85000A08000),
+ B(0x24100400060009, 0x112008025042410),
+ B(0x80102204040, 0x1000000900000000),
+ B(0x2080000004202804, 0x120880003461),
+ B(0x102004090A4030, 0x801020589240),
+ B(0x20001100814000A0, 0x420202000820004),
+ B(0x100800000A000120, 0x208000800010000),
+ B(0x1008205000040802, 0x80002000400040),
+ B(0x1480000098008401, 0xA0010000581010),
+ B(0x30C0008200100820, 0x102800080904834),
+ B(0x4810821884000500, 0x4400000200000212),
+ B(0x1811D00128A0180, 0x2500848803000000),
+ B(0x41618A0300040040, 0x21200200A421801),
+ B(0x80102204040, 0x1000000900000000),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x8C890020410000A0, 0xA010000048000400),
+ B(0x40006002210044, 0x600008000408000),
+ B(0x1200447220090042, 0x80001000160012),
+ B(0x48410010AB000000, 0x9200600000000100),
+ B(0x2040000000240003, 0x8020080288000600),
+ B(0x9080000088848088, 0x4010210500000041),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x480100400024, 0x1004800018200000),
+ B(0x808403080080200, 0x802601000000500),
+ B(0x8C890020410000A0, 0xA010000048000400),
+ B(0xA1808E0100108000, 0x2008000505000002),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x400014006000000, 0x10006000810001F5),
+ B(0xC410062001414, 0x820080041B01044),
+ B(0x20000800310, 0x430040000201000),
+ B(0xA40010008000008, 0x4002200028000040),
+ B(0xC00102000008021C, 0x10C2000A010E024),
+ B(0x80004200104008, 0x50A00800C400020),
+ B(0x20200080012542, 0x910F0040000402C0),
+ B(0xB040100504000300, 0x24802002000040),
+ B(0x800001000014008, 0x400031004000),
+ B(0x100A40000004008, 0x2800200400200480),
+ B(0x84008002041081C0, 0x8080500200000000),
+ B(0x440090001012001, 0x4020004010),
+ B(0x100A0028088020, 0x80040E00010020),
+ B(0x2180808000810, 0xB018040A00040000),
+ B(0x40C80920304C4001, 0x42800B200800000),
+ B(0x85000425001000, 0x4810048020001100),
+ B(0x600C000801000004, 0x8015084010200020),
+ B(0x20020050000240C0, 0x100202008600800),
+ B(0x38000050001220, 0x9200010200145900),
+ B(0x1042108040005, 0x1402A0802201001),
+ B(0x824240000C20400, 0x1000000400080010),
+ B(0x84008002041081C0, 0x8080500200000000),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x8000402308483, 0x20006020100100),
+ B(0x80880120000080, 0x8000240100084),
+ B(0x5840020004882001, 0x1004528000A00010),
+ B(0x8001018800300002, 0x84010040804),
+ B(0x180D10004000A008, 0xA001080008020004),
+ B(0x400080B, 0x10A0000004010000),
+ B(0x8080000200000, 0x2001000082004E0),
+ B(0x40040001000C2000, 0x2024800001004008),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x8000402308483, 0x20006020100100),
+ B(0x400804A1000008, 0x1024104A0200010),
+ B(0x2000200000, 0x1201011000802),
+ B(0x100100000000C4, 0x208004084048201),
+ B(0x400084000044, 0x100810140300),
+ B(0x29040C0C01010, 0x300204010820080),
+ B(0x1A808000020200, 0x1000000005210040),
+ B(0x20000400150000, 0x85008020),
+ B(0x40C040008184014, 0x8002AA00024010),
+ B(0x202000081B00804, 0x10001002008),
+ B(0x40011000210060, 0x6080C40000021004),
+ B(0x2000200000, 0x1201011000802),
+ B(0x4100480203840, 0x300080100804),
+ B(0x2000200000, 0x1201011000802),
+ };
+ constexpr Bitboard GrasshopperMagicVInit[SQUARE_NB] = {
+ B(0x202000812104400, 0x24800B01C0000303),
+ B(0x340020400010D, 0x88060150C00400),
+ B(0x400802040609, 0x49010200501A0002),
+ B(0x8002680301000208, 0x628006C0C020200),
+ B(0x20400209001C0804, 0xA044000800143110),
+ B(0xC400082060010202, 0x4000480401014000),
+ B(0x22500200144040, 0x8204820084704C00),
+ B(0x8C1204009030020, 0x328400805000000),
+ B(0x84800800D0001640, 0x200080040060108),
+ B(0x804810208020040, 0x140010108020000),
+ B(0x1102010B008004, 0x300208006220020),
+ B(0x140080404A0A2428, 0x6308010100080),
+ B(0x20444002120408, 0xA080010508010001),
+ B(0x82011044000D02, 0x4112028620110809),
+ B(0x81010831000C02, 0x408802085000000),
+ B(0x81010831000C02, 0x408802085000000),
+ B(0x920008920600040, 0x8053801004000028),
+ B(0x81140283208300, 0x10040C004200420),
+ B(0x103080022201, 0xC01000081312620),
+ B(0x2200221100008, 0x1000408104000A4),
+ B(0x4402088080042008, 0x210401501040340),
+ B(0x898400202170001, 0x80040404208000),
+ B(0x20080004051012, 0x5100048200081800),
+ B(0x2320020000401018, 0x108501021040210),
+ B(0x21080410A422021, 0x83040180008800),
+ B(0x44E8100000408224, 0x20010008040400),
+ B(0x1800240002810405, 0x23004820000020),
+ B(0x80A0100400110, 0x80104020100C4028),
+ B(0x1002050001222C0, 0x5100818004024020),
+ B(0x104000200040, 0xC010A09800102000),
+ B(0x1020003A058120, 0x450900809000302),
+ B(0x40040045008B1, 0x202800400383010),
+ B(0x4640200220034, 0x8800485420304000),
+ B(0x5001042100084288, 0x110820001240080A),
+ B(0x2002C04004010120, 0xA15008020880001),
+ B(0x2800004080C4190, 0x890808280020080),
+ B(0x40C0401000104000, 0x2020880008002580),
+ B(0x40020C002400802, 0x801104010000000),
+ B(0x44842000040080, 0x2050011084000400),
+ B(0x4110040800000401, 0x2023810029008000),
+ B(0x20884000840, 0x8017102004008000),
+ B(0x10411104000480, 0x1414042000201001),
+ B(0x220040000008, 0x800306021000000),
+ B(0x41400A0008080, 0x501000298ACAD10),
+ B(0x800240012831810, 0x80120004468050E),
+ B(0x800005020801008, 0x20102400240000),
+ B(0x20C00040C114C010, 0x88080820200C00),
+ B(0x1044010100820081, 0x20080841004000),
+ B(0x8041048400022, 0x8020836040005002),
+ B(0x2001004010205, 0x8001002884042009),
+ B(0x128088400087, 0x20008002201002),
+ B(0x8084108040402000, 0x80809000A080400),
+ B(0x408081840880, 0x201002088000040),
+ B(0xA40180010280, 0x241004006000010),
+ B(0x4204100080048140, 0x2002C4F104202020),
+ B(0x100140A10204, 0x980200800840060),
+ B(0x1005140010202048, 0x1442280800202815),
+ B(0x2000082025008600, 0x1108400040600003),
+ B(0x1005050648000, 0x200020240008002),
+ B(0x202010208044000, 0x8210404060008),
+ B(0x8011040402000210, 0xC840180408016004),
+ B(0x404098801028, 0x80020A0001000400),
+ B(0x404098801028, 0x80020A0001000400),
+ B(0x80101002180140, 0x40C2080820000C0),
+ B(0x208202081260800, 0x14090E4C04000050),
+ B(0x4221201084004C2, 0x110480A011060),
+ B(0x8000008421090204, 0x1C01010800024),
+ B(0x8000008421090204, 0x1C01010800024),
+ B(0x200180C840088A0, 0x401100400820000),
+ B(0x10084043A021070, 0x202041600080200),
+ B(0x210E6202001040C, 0x10100800080B0),
+ B(0x848008021204002, 0x801004308100BAD),
+ B(0xC082C0390A000601, 0x4040080189008),
+ B(0x431200240210402D, 0x58102820000),
+ B(0x202020100A0019B0, 0x4010C0D018000000),
+ B(0x800800908402203, 0x102948C84C184),
+ B(0x26801100080845, 0x4009702022A00820),
+ B(0x8880520010401040, 0x1060084832052000),
+ B(0x100100022042081, 0x10000600008C121),
+ B(0x46020384100040, 0x800200320882021),
+ B(0xC0002010148, 0x4200800800040003),
+ B(0x2002208020090040, 0x40820210021410),
+ B(0x9000A41160002004, 0x2A09000100080043),
+ B(0x800004010008001, 0x1108002020104600),
+ B(0x800540C000A4E041, 0x18021180000401),
+ B(0x808200900A900202, 0x8364202140012005),
+ B(0x1DBA52000081010, 0x4008000023000010),
+ B(0x4100110204401481, 0x800040091020001C),
+ B(0x4100110204401481, 0x800040091020001C),
+ B(0x4101100020400482, 0x2000402302100120),
+ B(0x100408000A020212, 0xA000400111000020),
+ B(0x2000010488080104, 0x3000404410208100),
+ B(0x2684220180008DD0, 0x422040200004000A),
+ B(0x2021200C0424, 0x1010100000080200),
+ B(0x8908020020801006, 0x3010800020C2000),
+ B(0x4000030008062044, 0x244010202688000),
+ B(0x242101200408009, 0x8150040000200015),
+ B(0x42004C02180204, 0x210208014241040),
+ B(0x4E1A01C208410804, 0x8890041000012004),
+ B(0x2080200401000080, 0x8001098429008004),
+ B(0xA01400121804104, 0x280200C400000500),
+ B(0xD0080408040420, 0x1006040100224000),
+ B(0x28400205000800C9, 0x6021101401040075),
+ B(0x4000900040020104, 0x88129801100D0C),
+ B(0x8000004002180410, 0x400380200400204),
+ B(0x4002A430043008, 0x400200340100020),
+ B(0x401960004140A42, 0x100880710000464),
+ B(0x58014090102, 0xB8D30004010080),
+ B(0xA004C08000244000, 0x11280100E0000040),
+ B(0x2102008089208804, 0x110001004080040),
+ B(0x700010084E003004, 0x8080864112000D40),
+ B(0x4080881000200C20, 0x30324040880E0600),
+ B(0x2024A40401810820, 0x3000888002000000),
+ B(0x8200100400014, 0x4400340800252844),
+ B(0x24A00804288281, 0x410103002201140),
+ B(0x4080005022A08, 0x1000402200100264),
+ B(0x200080032244040, 0x200502189010001),
+ B(0x28108110404001, 0x400600120008412),
+ B(0xA00002102810020, 0xB1080240015408),
+ B(0x810080200806, 0x410440804080046),
+ };
+ constexpr Bitboard GrasshopperMagicDInit[SQUARE_NB] = {
+ B(0x811801000400, 0x312260280280202),
+ B(0x44A000402022680, 0x1020224880420005),
+ B(0x8000C80800200880, 0x2000810060080C0),
+ B(0x2010300240428040, 0x40240002C004E30),
+ B(0x1018010404010004, 0x1001010018081E0),
+ B(0x2042040010080090, 0x100000008410300),
+ B(0x400080020102000, 0x4500005300000000),
+ B(0x2D00C80420010200, 0x804003280020008),
+ B(0x8038820024420, 0x6010010080012040),
+ B(0x1202028004200088, 0x50018100004000C6),
+ B(0xA02010F0410081, 0x20013001000009A),
+ B(0x4013002041030588, 0x4802004110000004),
+ B(0x110020802000081, 0x202001800908002),
+ B(0x22010404103, 0x2020882080491200),
+ B(0x60000220400580, 0x85902800100100),
+ B(0x100080800050100, 0x200010220021088),
+ B(0x8088840404200080, 0x140011040104000),
+ B(0x4008508080082015, 0x8010100200580048),
+ B(0x4010400420201001, 0x260002080A80808),
+ B(0xC2002004A0008008, 0x8020082000110840),
+ B(0xA000A0820042400, 0x810408082100420),
+ B(0x80231808100004, 0x204002000800400),
+ B(0x8296144044004900, 0x4A1003008001840),
+ B(0x80A0020A0011008, 0x800104846080810),
+ B(0x803800801041000, 0x1030500102000404),
+ B(0x240C00900800850, 0x1804000108810000),
+ B(0x800400000088800, 0x800021801020000),
+ B(0x84800409300082, 0x1002D40680044000),
+ B(0xA110C0000200010, 0x401010001200260),
+ B(0x8200160204100004, 0x8040004004002022),
+ B(0x10001000000100C0, 0x84002811000200),
+ B(0x2000080020014001, 0x42002020000102),
+ B(0x109040044020018, 0x2020400202001000),
+ B(0x620000CD0108, 0x40040201008000),
+ B(0xA1402200A0020, 0x81400400300912),
+ B(0x20020CF100018020, 0x801A14086404000),
+ B(0x800801844001, 0x11621488425000),
+ B(0x10201004A8080, 0x100A000801000010),
+ B(0x2800411001000800, 0x80224084900020),
+ B(0x40400024028100, 0x501000400230060),
+ B(0x404808010080, 0x1201000400100004),
+ B(0x80802005200, 0x2000200008A0000),
+ B(0x20800080000022, 0x80040810002010),
+ B(0x40016004808240, 0x400114000801100),
+ B(0x8410004204240, 0x20011000604050),
+ B(0x8000C1009008268, 0x201004000209000),
+ B(0x10240C000920, 0xE000A5C14003002),
+ B(0x10184024280008, 0x90240802000000),
+ B(0x40889081081000, 0x8010050008800000),
+ B(0x100008C089000019, 0x802032014020010),
+ B(0x401C1804C00, 0x402501002002020),
+ B(0x200022000920D0, 0x8000800081300020),
+ B(0x801000400011, 0x400100044010226),
+ B(0x4A04010100100000, 0x500400080400000),
+ B(0xA000050200080000, 0x8500090001010000),
+ B(0x40400040001812, 0x4403000400100A0),
+ B(0x20C2250203020004, 0x210001C000080000),
+ B(0x21000408C6020001, 0x4200830012D1001),
+ B(0x840082016080A210, 0x2400080801081008),
+ B(0x40001020000, 0x4041240200083120),
+ B(0x2C04030010C0818, 0xA670002000818100),
+ B(0x4704A07085000510, 0x914001000040),
+ B(0x900210304100100, 0x1010004000281840),
+ B(0x8202920002002040, 0x810012000003),
+ B(0x4001400100050, 0x1144000408002000),
+ B(0x5900200020008100, 0x40200020002004),
+ B(0x301020002000480, 0x202000C0004),
+ B(0x20D000201104040, 0x34840100020010),
+ B(0x800004200080408, 0x40184200100240),
+ B(0x8430080100404020, 0x90042100244500),
+ B(0x3800100010220062, 0x50404030200218),
+ B(0x42E20008002020, 0x2000008200200300),
+ B(0xE488008280A004, 0x200001010CC80000),
+ B(0x6018010041109810, 0x800002000242041A),
+ B(0x40A8002438980, 0x8000810008208009),
+ B(0x401000480040100, 0x286800404002212),
+ B(0x821030000100009, 0x2000090200A00000),
+ B(0x20000100C0008028, 0x5000000100400082),
+ B(0x80A000048030080, 0x200000120200008),
+ B(0x6300280800204003, 0x48000105C0040100),
+ B(0x83008802420C0200, 0x2008020200080100),
+ B(0x1050C3102200042, 0x20103900010008),
+ B(0x8040902021408180, 0x12000021806200A4),
+ B(0x3008204008C10004, 0x680110100010401),
+ B(0x204321100421000, 0x400E204820494000),
+ B(0x8000044022404048, 0x4024010090024021),
+ B(0x140201424050, 0x280A000130008000),
+ B(0x900340808004002, 0x21026008000380),
+ B(0x82808000300444, 0x20002000A2001141),
+ B(0x140180100406002, 0x4004480001000004),
+ B(0x4808420800841900, 0x14008C0041000000),
+ B(0x2008600009000480, 0x9008020001400000),
+ B(0x2000100800100002, 0x2004100820210020),
+ B(0x2062010401A8100, 0x12200108420090),
+ B(0x1403188200032, 0x40048166105000),
+ B(0x410020020140041, 0x4400348102940040),
+ B(0x414040209208041, 0x4402400028B004),
+ B(0x8008010100421202, 0x401418002008800),
+ B(0x4000020010062200, 0xA02009148048000),
+ B(0x4443080082008B, 0x104014022801010),
+ B(0x42B440A0C000800, 0x9001009016111020),
+ B(0x400000214002, 0x8008080209020009),
+ B(0x480C414A001900, 0x3400100400210200),
+ B(0x1006008800604, 0x20240004030A050),
+ B(0x4C022401002A8300, 0x405008400000600),
+ B(0x3104000800A1042, 0x2004800204406200),
+ B(0xA09010280008200C, 0x4004000208C4168),
+ B(0x2800401120C20120, 0x4A00450200022030),
+ B(0x88001800304C0200, 0x204288102080000),
+ B(0x8044004201440101, 0x400820080C024022),
+ B(0xA000100C080, 0x4B40341004008081),
+ B(0x94802001300810, 0x140206008000800),
+ B(0x40002020202820, 0x280680404000040),
+ B(0xA820800004200, 0x80E1401012000491),
+ B(0x804000010020C000, 0x9403020200802000),
+ B(0x8C0001284201400, 0xC000100C01620800),
+ B(0x4010004002200414, 0x403080080200000),
+ B(0x140400A100800101, 0x10054C031080400),
+ B(0x20012C2400880082, 0x7000880020C03200),
+ B(0x204040300004, 0x840800041101002),
+ };
+#undef B
+#endif
+
++} // namespace Stockfish
++
+#endif // #ifndef MAGIC_H_INCLUDED
#include "tt.h"
#include "uci.h"
+#include "piece.h"
+#include "variant.h"
+
++
+ using namespace Stockfish;
+
int main(int argc, char* argv[]) {
std::cout << engine_info() << std::endl;
#include "types.h"
+ namespace Stockfish {
+
-std::string engine_info(bool to_uci = false);
+std::string engine_info(bool to_uci = false, bool to_xboard = false);
std::string compiler_info();
void prefetch(void* addr);
void start_logger(const std::string& fname);
#include "movegen.h"
#include "position.h"
+ namespace Stockfish {
+
namespace {
- template<GenType Type, Direction D>
- ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
+ template<MoveType T>
+ ExtMove* make_move_and_gating(const Position& pos, ExtMove* moveList, Color us, Square from, Square to) {
- if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
+ // Arrow gating moves
+ if (pos.arrow_gating())
{
- *moveList++ = make<PROMOTION>(to - D, to, QUEEN);
- if (attacks_bb<KNIGHT>(to) & ksq)
- *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
+ for (PieceType pt_gating : pos.piece_types())
+ if (pos.count_in_hand(us, pt_gating) > 0)
+ {
+ Bitboard b = pos.drop_region(us, pt_gating) & moves_bb(us, type_of(pos.piece_on(from)), to, pos.pieces() ^ from) & ~(pos.pieces() ^ from);
+ while (b)
+ *moveList++ = make_gating<T>(from, to, pt_gating, pop_lsb(&b));
+ }
+ return moveList;
}
- if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
+ *moveList++ = make<T>(from, to);
+
+ // Gating moves
+ if (pos.seirawan_gating() && (pos.gates(us) & from))
+ for (PieceType pt_gating : pos.piece_types())
+ if (pos.count_in_hand(us, pt_gating) > 0 && (pos.drop_region(us, pt_gating) & from))
+ *moveList++ = make_gating<T>(from, to, pt_gating, from);
+ if (pos.seirawan_gating() && T == CASTLING && (pos.gates(us) & to))
+ for (PieceType pt_gating : pos.piece_types())
+ if (pos.count_in_hand(us, pt_gating) > 0 && (pos.drop_region(us, pt_gating) & to))
+ *moveList++ = make_gating<T>(from, to, pt_gating, to);
+
+ return moveList;
+ }
+
+ template<Color c, GenType Type, Direction D>
+ ExtMove* make_promotions(const Position& pos, ExtMove* moveList, Square to) {
+
+ if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
- *moveList++ = make<PROMOTION>(to - D, to, ROOK);
- *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
- if (!(attacks_bb<KNIGHT>(to) & ksq))
- *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
+ for (PieceType pt : pos.promotion_piece_types())
+ if (!pos.promotion_limit(pt) || pos.promotion_limit(pt) > pos.count(c, pt))
+ *moveList++ = make<PROMOTION>(to - D, to, pt);
+ PieceType pt = pos.promoted_piece_type(PAWN);
+ if (pt && !(pos.piece_promotion_on_capture() && pos.empty(to)))
+ *moveList++ = make<PIECE_PROMOTION>(to - D, to);
}
return moveList;
#include "movepick.h"
+ namespace Stockfish {
+// Since continuation history grows quadratically with the number of piece types,
+// we need to reserve a limited number of slots and map piece types to these slots
+// in order to reduce memory consumption to a reasonable level.
+int history_slot(Piece pc) {
+ return pc == NO_PIECE ? 0 : (type_of(pc) == KING ? PIECE_SLOTS - 1 : type_of(pc) % (PIECE_SLOTS - 1)) + color_of(pc) * PIECE_SLOTS;
+}
+
-
namespace {
enum Stages {
#include "../layers/affine_transform.h"
#include "../layers/clipped_relu.h"
- namespace Eval::NNUE::Features {
++namespace Stockfish::Eval::NNUE::Features {
+// Alias for compatibility with upstream code
+template <Side AssociatedKing>
+using HalfKP = HalfKPChess<AssociatedKing>;
+}
+
- namespace Eval::NNUE {
+ namespace Stockfish::Eval::NNUE {
// Input features used in evaluation function
using RawFeatures = Features::FeatureSet<
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Definition of input features and network structure used in NNUE evaluation function
+
+#ifndef NNUE_HALFKP_SHOGI_256X2_32_32_H_INCLUDED
+#define NNUE_HALFKP_SHOGI_256X2_32_32_H_INCLUDED
+
+#include "../features/feature_set.h"
+#include "../features/half_kp_shogi.h"
+
+#include "../layers/input_slice.h"
+#include "../layers/affine_transform.h"
+#include "../layers/clipped_relu.h"
+
- namespace Eval::NNUE::Features {
++namespace Stockfish::Eval::NNUE::Features {
+// Alias for compatibility with upstream code
+template <Side AssociatedKing>
+using HalfKP = HalfKPShogi<AssociatedKing>;
+}
+
- namespace Eval::NNUE {
++namespace Stockfish::Eval::NNUE {
+
+// Input features used in evaluation function
+using RawFeatures = Features::FeatureSet<
+ Features::HalfKPShogi<Features::Side::kFriend>>;
+
+// Number of input feature dimensions after conversion
+constexpr IndexType kTransformedFeatureDimensions = 256;
+
+namespace Layers {
+
+// Define network structure
+using InputLayer = InputSlice<kTransformedFeatureDimensions * 2>;
+using HiddenLayer1 = ClippedReLU<AffineTransform<InputLayer, 32>>;
+using HiddenLayer2 = ClippedReLU<AffineTransform<HiddenLayer1, 32>>;
+using OutputLayer = AffineTransform<HiddenLayer2, 1>;
+
+} // namespace Layers
+
+using Network = Layers::OutputLayer;
+
- } // namespace Eval::NNUE
++} // namespace Stockfish::Eval::NNUE
+
+#endif // #ifndef NNUE_HALFKP_SHOGI_256X2_32_32_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Definition of input features and network structure used in NNUE evaluation function
+
+#ifndef NNUE_HALFKP_VARIANTS_256X2_32_32_H_INCLUDED
+#define NNUE_HALFKP_VARIANTS_256X2_32_32_H_INCLUDED
+
+#include "../features/feature_set.h"
+#include "../features/half_kp_variants.h"
+
+#include "../layers/input_slice.h"
+#include "../layers/affine_transform.h"
+#include "../layers/clipped_relu.h"
+
- namespace Eval::NNUE::Features {
++namespace Stockfish::Eval::NNUE::Features {
+// Alias for compatibility with upstream code
+template <Side AssociatedKing>
+using HalfKP = HalfKPVariants<AssociatedKing>;
+}
+
- namespace Eval::NNUE {
++namespace Stockfish::Eval::NNUE {
+
+// Input features used in evaluation function
+using RawFeatures = Features::FeatureSet<
+ Features::HalfKPVariants<Features::Side::kFriend>>;
+
+// Number of input feature dimensions after conversion
+constexpr IndexType kTransformedFeatureDimensions = 256;
+
+namespace Layers {
+
+// Define network structure
+using InputLayer = InputSlice<kTransformedFeatureDimensions * 2>;
+using HiddenLayer1 = ClippedReLU<AffineTransform<InputLayer, 32>>;
+using HiddenLayer2 = ClippedReLU<AffineTransform<HiddenLayer1, 32>>;
+using OutputLayer = AffineTransform<HiddenLayer2, 1>;
+
+} // namespace Layers
+
+using Network = Layers::OutputLayer;
+
- } // namespace Eval::NNUE
++} // namespace Stockfish::Eval::NNUE
+
+#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
#include "half_kp.h"
#include "index_list.h"
- namespace Eval::NNUE::Features {
+ namespace Stockfish::Eval::NNUE::Features {
+ // Map square to numbering on 8x8 board
+ constexpr Square to_chess_square(Square s) {
+ return Square(s - rank_of(s) * (FILE_MAX - FILE_H));
+ }
+
// Orient a square according to perspective (rotates by 180 for black)
inline Square orient(Color perspective, Square s) {
- return Square(int(s) ^ (bool(perspective) * 63));
+ return Square(int(to_chess_square(s)) ^ (bool(perspective) * 63));
}
// Index of a feature for a given king position and another piece on some square
}
}
- template class HalfKP<Side::kFriend>;
+ template class HalfKPChess<Side::kFriend>;
- } // namespace Eval::NNUE::Features
+ } // namespace Stockfish::Eval::NNUE::Features
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+//Definition of input features HalfKP of NNUE evaluation function
+
+#include "half_kp_shogi.h"
+#include "index_list.h"
+
- namespace Eval::NNUE::Features {
++namespace Stockfish::Eval::NNUE::Features {
+
+ constexpr Square rotate(Square s) {
+ return Square(SQUARE_NB_SHOGI - 1 - int(s));
+ }
+
+ constexpr Square to_shogi_square(Square s) {
+ return Square((8 - s % 12) * 9 + 8 - s / 12);
+ }
+
+ // Orient a square according to perspective (rotates by 180 for black)
+ inline Square orient(Color perspective, Square s) {
+ return perspective == WHITE ? to_shogi_square(s) : rotate(to_shogi_square(s));
+ }
+
+ // Index of a feature for a given king position and another piece on some square
+ inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) {
+ return IndexType(orient(perspective, s) + shogi_kpp_board_index[perspective][pc] + SHOGI_PS_END * ksq);
+ }
+
+ // Index of a feature for a given king position and hand piece
+ inline IndexType make_index(Color perspective, Color c, int hand_index, PieceType pt, Square ksq) {
+ Color color = (c == perspective) ? WHITE : BLACK;
+ return IndexType(hand_index + shogi_kpp_hand_index[color][pt] + SHOGI_PS_END * ksq);
+ }
+
+ // Get a list of indices for active features
+ template <Side AssociatedKing>
+ void HalfKPShogi<AssociatedKing>::AppendActiveIndices(
+ const Position& pos, Color perspective, IndexList* active) {
+
+ Square ksq = orient(perspective, pos.square<KING>(perspective));
+ Bitboard bb = pos.pieces() & ~pos.pieces(KING);
+ while (bb) {
+ Square s = pop_lsb(&bb);
+ active->push_back(make_index(perspective, s, pos.piece_on(s), ksq));
+ }
+
+ // Indices for pieces in hand
+ for (Color c : {WHITE, BLACK})
+ for (PieceType pt : pos.piece_types())
+ for (int i = 0; i < pos.count_in_hand(c, pt); i++)
+ active->push_back(make_index(perspective, c, i, pt, ksq));
+ }
+
+ // Get a list of indices for recently changed features
+ template <Side AssociatedKing>
+ void HalfKPShogi<AssociatedKing>::AppendChangedIndices(
+ const Position& pos, const DirtyPiece& dp, Color perspective,
+ IndexList* removed, IndexList* added) {
+
+ Square ksq = orient(perspective, pos.square<KING>(perspective));
+ for (int i = 0; i < dp.dirty_num; ++i) {
+ Piece pc = dp.piece[i];
+ if (type_of(pc) == KING) continue;
+ if (dp.from[i] != SQ_NONE)
+ removed->push_back(make_index(perspective, dp.from[i], pc, ksq));
+ else if (dp.dirty_num == 1)
+ {
+ Piece handPc = dp.handPiece[i];
+ removed->push_back(make_index(perspective, color_of(handPc), pos.count_in_hand(color_of(handPc), type_of(handPc)), type_of(handPc), ksq));
+ }
+ if (dp.to[i] != SQ_NONE)
+ added->push_back(make_index(perspective, dp.to[i], pc, ksq));
+ else if (i == 1)
+ {
+ Piece handPc = dp.handPiece[i];
+ added->push_back(make_index(perspective, color_of(handPc), pos.count_in_hand(color_of(handPc), type_of(handPc)) - 1, type_of(handPc), ksq));
+ }
+ }
+ }
+
+ template class HalfKPShogi<Side::kFriend>;
+
- } // namespace Eval::NNUE::Features
++} // namespace Stockfish::Eval::NNUE::Features
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+//Definition of input features HalfKP of NNUE evaluation function
+
+#ifndef NNUE_FEATURES_HALF_KP_SHOGI_H_INCLUDED
+#define NNUE_FEATURES_HALF_KP_SHOGI_H_INCLUDED
+
+#include "../../evaluate.h"
+#include "features_common.h"
+
- namespace Eval::NNUE::Features {
++namespace Stockfish::Eval::NNUE::Features {
+
+ // Feature HalfKP: Combination of the position of own king
+ // and the position of pieces other than kings
+ template <Side AssociatedKing>
+ class HalfKPShogi {
+
+ public:
+ // Feature name
+ static constexpr const char* kName = "HalfKP(Friend)";
+ // Hash value embedded in the evaluation file
+ static constexpr std::uint32_t kHashValue =
+ 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
+ // Number of feature dimensions
+ static constexpr IndexType kDimensions =
+ static_cast<IndexType>(SQUARE_NB_SHOGI) * static_cast<IndexType>(SHOGI_PS_END);
+ // Maximum number of simultaneously active features
+ static constexpr IndexType kMaxActiveDimensions = 38; // Kings don't count
+ // Trigger for full calculation instead of difference calculation
+ static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
+
+ // Get a list of indices for active features
+ static void AppendActiveIndices(const Position& pos, Color perspective,
+ IndexList* active);
+
+ // Get a list of indices for recently changed features
+ static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective,
+ IndexList* removed, IndexList* added);
+ };
+
- } // namespace Eval::NNUE::Features
++} // namespace Stockfish::Eval::NNUE::Features
+
+#endif // #ifndef NNUE_FEATURES_HALF_KP_SHOGI_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+//Definition of input features HalfKP of NNUE evaluation function
+
+#include "half_kp_variants.h"
+#include "index_list.h"
+
+#ifdef LARGEBOARDS
+#include "half_kp_shogi.h"
+#endif
+
- namespace Eval::NNUE::Features {
++namespace Stockfish::Eval::NNUE::Features {
+
+ // Map square to numbering on 8x8 board
+ constexpr Square to_chess_square(Square s) {
+ return Square(s - rank_of(s) * (FILE_MAX - FILE_H));
+ }
+
+ // Orient a square according to perspective (rotates by 180 for black)
+ inline Square orient(const Position& pos, Color perspective, Square s) {
+ return to_chess_square( perspective == WHITE || (pos.capture_the_flag(BLACK) & Rank8BB) ? s
+ : flip_rank(flip_file(s, pos.max_file()), pos.max_rank()));
+ }
+
+ // Index of a feature for a given king position and another piece on some square
+ inline IndexType make_index(const Position& pos, Color perspective, Square s, Piece pc, Square ksq) {
+ return IndexType(orient(pos, perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq);
+ }
+
+ // Get a list of indices for active features
+ template <Side AssociatedKing>
+ void HalfKPVariants<AssociatedKing>::AppendActiveIndices(
+ const Position& pos, Color perspective, IndexList* active) {
+
+ // Re-route to shogi features
+#ifdef LARGEBOARDS
+ if (currentNnueFeatures == NNUE_SHOGI)
+ {
+ assert(HalfKPShogi<AssociatedKing>::kDimensions <= kDimensions);
+ return HalfKPShogi<AssociatedKing>::AppendActiveIndices(pos, perspective, active);
+ }
+#endif
+
+ Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king()));
+ Bitboard bb = pos.pieces() & ~pos.pieces(pos.nnue_king());
+ while (bb) {
+ Square s = pop_lsb(&bb);
+ active->push_back(make_index(pos, perspective, s, pos.piece_on(s), ksq));
+ }
+ }
+
+ // Get a list of indices for recently changed features
+ template <Side AssociatedKing>
+ void HalfKPVariants<AssociatedKing>::AppendChangedIndices(
+ const Position& pos, const DirtyPiece& dp, Color perspective,
+ IndexList* removed, IndexList* added) {
+
+ // Re-route to shogi features
+#ifdef LARGEBOARDS
+ if (currentNnueFeatures == NNUE_SHOGI)
+ {
+ assert(HalfKPShogi<AssociatedKing>::kDimensions <= kDimensions);
+ return HalfKPShogi<AssociatedKing>::AppendChangedIndices(pos, dp, perspective, removed, added);
+ }
+#endif
+
+ Square ksq = orient(pos, perspective, pos.square(perspective, pos.nnue_king()));
+ for (int i = 0; i < dp.dirty_num; ++i) {
+ Piece pc = dp.piece[i];
+ if (type_of(pc) == pos.nnue_king()) continue;
+ if (dp.from[i] != SQ_NONE)
+ removed->push_back(make_index(pos, perspective, dp.from[i], pc, ksq));
+ if (dp.to[i] != SQ_NONE)
+ added->push_back(make_index(pos, perspective, dp.to[i], pc, ksq));
+ }
+ }
+
+ template class HalfKPVariants<Side::kFriend>;
+
- } // namespace Eval::NNUE::Features
++} // namespace Stockfish::Eval::NNUE::Features
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+//Definition of input features HalfKP of NNUE evaluation function
+
+#ifndef NNUE_FEATURES_HALF_KP_VARIANTS_H_INCLUDED
+#define NNUE_FEATURES_HALF_KP_VARIANTS_H_INCLUDED
+
+#include "../../evaluate.h"
+#include "features_common.h"
+
- namespace Eval::NNUE::Features {
++namespace Stockfish::Eval::NNUE::Features {
+
+ // Feature HalfKP: Combination of the position of own king
+ // and the position of pieces other than kings
+ template <Side AssociatedKing>
+ class HalfKPVariants {
+
+ public:
+ // Feature name
+ static constexpr const char* kName = "HalfKP(Friend)";
+ // Hash value embedded in the evaluation file
+ static constexpr std::uint32_t kHashValue =
+ 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
+ // Number of feature dimensions
+ static constexpr IndexType kDimensions =
+#ifdef LARGEBOARDS
+ static_cast<IndexType>(SQUARE_NB_SHOGI) * static_cast<IndexType>(SHOGI_PS_END);
+#else
+ static_cast<IndexType>(SQUARE_NB_CHESS) * static_cast<IndexType>(PS_END);
+#endif
+ // Maximum number of simultaneously active features
+ static constexpr IndexType kMaxActiveDimensions = 64; // Kings don't count
+ // Trigger for full calculation instead of difference calculation
+ static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
+
+ // Get a list of indices for active features
+ static void AppendActiveIndices(const Position& pos, Color perspective,
+ IndexList* active);
+
+ // Get a list of indices for recently changed features
+ static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective,
+ IndexList* removed, IndexList* added);
+ };
+
- } // namespace Eval::NNUE::Features
++} // namespace Stockfish::Eval::NNUE::Features
+
+#endif // #ifndef NNUE_FEATURES_HALF_KP_VARIANTS_H_INCLUDED
#define NNUE_ARCHITECTURE_H_INCLUDED
// Defines the network structure
-#include "architectures/halfkp_256x2-32-32.h"
+// #include "architectures/halfkp_256x2-32-32.h"
+// #include "architectures/halfkp_shogi_256x2-32-32.h"
+#include "architectures/halfkp_variants_256x2-32-32.h"
- namespace Eval::NNUE {
+ namespace Stockfish::Eval::NNUE {
static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, "");
static_assert(Network::kOutputDimensions == 1, "");
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string>
+#include <sstream>
+
+#include "apiutil.h"
+#include "parser.h"
+#include "piece.h"
+#include "types.h"
+
++namespace Stockfish {
++
+namespace {
+
+ template <typename T> bool set(const std::string& value, T& target)
+ {
+ std::stringstream ss(value);
+ ss >> target;
+ return !ss.fail();
+ }
+
+ template <> bool set(const std::string& value, Rank& target) {
+ std::stringstream ss(value);
+ int i;
+ ss >> i;
+ target = Rank(i - 1);
+ return !ss.fail() && target >= RANK_1 && target <= RANK_MAX;
+ }
+
+ template <> bool set(const std::string& value, File& target) {
+ std::stringstream ss(value);
+ if (isdigit(ss.peek()))
+ {
+ int i;
+ ss >> i;
+ target = File(i - 1);
+ }
+ else
+ {
+ char c;
+ ss >> c;
+ target = File(c - 'a');
+ }
+ return !ss.fail() && target >= FILE_A && target <= FILE_MAX;
+ }
+
+ template <> bool set(const std::string& value, std::string& target) {
+ target = value;
+ return true;
+ }
+
+ template <> bool set(const std::string& value, bool& target) {
+ target = value == "true";
+ return value == "true" || value == "false";
+ }
+
+ template <> bool set(const std::string& value, Value& target) {
+ target = value == "win" ? VALUE_MATE
+ : value == "loss" ? -VALUE_MATE
+ : value == "draw" ? VALUE_DRAW
+ : VALUE_NONE;
+ return value == "win" || value == "loss" || value == "draw" || value == "none";
+ }
+
+ template <> bool set(const std::string& value, MaterialCounting& target) {
+ target = value == "janggi" ? JANGGI_MATERIAL
+ : value == "unweighted" ? UNWEIGHTED_MATERIAL
+ : value == "whitedrawodds" ? WHITE_DRAW_ODDS
+ : value == "blackdrawodds" ? BLACK_DRAW_ODDS
+ : NO_MATERIAL_COUNTING;
+ return value == "janggi" || value == "unweighted"
+ || value == "whitedrawodds" || value == "blackdrawodds" || value == "none";
+ }
+
+ template <> bool set(const std::string& value, CountingRule& target) {
+ target = value == "makruk" ? MAKRUK_COUNTING
+ : value == "asean" ? ASEAN_COUNTING
+ : NO_COUNTING;
+ return value == "makruk" || value == "asean" || value == "none";
+ }
+
+ template <> bool set(const std::string& value, EnclosingRule& target) {
+ target = value == "reversi" ? REVERSI
+ : value == "ataxx" ? ATAXX
+ : NO_ENCLOSING;
+ return value == "reversi" || value == "ataxx" || value == "none";
+ }
+
+ template <> bool set(const std::string& value, Bitboard& target) {
+ char file;
+ int rank;
+ std::stringstream ss(value);
+ target = 0;
+ while (!ss.eof() && ss >> file && ss >> rank)
+ target |= file == '*' ? rank_bb(Rank(rank - 1)) : square_bb(make_square(File(tolower(file) - 'a'), Rank(rank - 1)));
+ return !ss.fail();
+ }
+
+} // namespace
+
+template <bool DoCheck>
+template <class T> void VariantParser<DoCheck>::parse_attribute(const std::string& key, T& target) {
+ const auto& it = config.find(key);
+ if (it != config.end())
+ {
+ bool valid = set(it->second, target);
+ if (DoCheck && !valid)
+ {
+ std::string typeName = std::is_same<T, int>() ? "int"
+ : std::is_same<T, Rank>() ? "Rank"
+ : std::is_same<T, File>() ? "File"
+ : std::is_same<T, bool>() ? "bool"
+ : std::is_same<T, Value>() ? "Value"
+ : std::is_same<T, MaterialCounting>() ? "MaterialCounting"
+ : std::is_same<T, CountingRule>() ? "CountingRule"
+ : std::is_same<T, Bitboard>() ? "Bitboard"
+ : typeid(T).name();
+ std::cerr << key << " - Invalid value " << it->second << " for type " << typeName << std::endl;
+ }
+ }
+}
+
+template <bool DoCheck>
+void VariantParser<DoCheck>::parse_attribute(const std::string& key, PieceType& target, std::string pieceToChar) {
+ const auto& it = config.find(key);
+ if (it != config.end())
+ {
+ char token;
+ size_t idx;
+ std::stringstream ss(it->second);
+ if (ss >> token && (idx = token == '-' ? 0 : pieceToChar.find(toupper(token))) != std::string::npos)
+ target = PieceType(idx);
+ else if (DoCheck)
+ std::cerr << key << " - Invalid piece type: " << token << std::endl;
+ }
+}
+
+template <bool DoCheck>
+Variant* VariantParser<DoCheck>::parse() {
+ Variant* v = new Variant();
+ v->reset_pieces();
+ v->promotionPieceTypes = {};
+ return parse(v);
+}
+
+template <bool DoCheck>
+Variant* VariantParser<DoCheck>::parse(Variant* v) {
+ // piece types
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
+ {
+ // piece char
+ std::string name = piece_name(pt);
+
+ const auto& keyValue = config.find(name);
+ if (keyValue != config.end() && !keyValue->second.empty())
+ {
+ if (isalpha(keyValue->second.at(0)))
+ v->add_piece(pt, keyValue->second.at(0));
+ else
+ {
+ if (DoCheck && keyValue->second.at(0) != '-')
+ std::cerr << name << " - Invalid letter: " << keyValue->second.at(0) << std::endl;
+ v->remove_piece(pt);
+ }
+ // betza
+ if (is_custom(pt))
+ {
+ if (keyValue->second.size() > 1)
+ v->customPiece[pt - CUSTOM_PIECES] = keyValue->second.substr(2);
+ else if (DoCheck)
+ std::cerr << name << " - Missing Betza move notation" << std::endl;
+ }
+ }
+ // mobility region
+ std::string capitalizedPiece = name;
+ capitalizedPiece[0] = toupper(capitalizedPiece[0]);
+ for (Color c : {WHITE, BLACK})
+ {
+ std::string color = c == WHITE ? "White" : "Black";
+ parse_attribute("mobilityRegion" + color + capitalizedPiece, v->mobilityRegion[c][pt]);
+ }
+ }
+ // 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);
+ parse_attribute("maxRank", v->maxRank);
+ parse_attribute("maxFile", v->maxFile);
+ parse_attribute("chess960", v->chess960);
+ parse_attribute("twoBoards", v->twoBoards);
+ parse_attribute("startFen", v->startFen);
+ parse_attribute("promotionRank", v->promotionRank);
+ // promotion piece types
+ const auto& it_prom = config.find("promotionPieceTypes");
+ if (it_prom != config.end())
+ {
+ v->promotionPieceTypes = {};
+ char token;
+ size_t idx = 0;
+ std::stringstream ss(it_prom->second);
+ while (ss >> token && ((idx = v->pieceToChar.find(toupper(token))) != std::string::npos))
+ v->promotionPieceTypes.insert(PieceType(idx));
+ if (DoCheck && idx == std::string::npos && token != '-')
+ std::cerr << "promotionPieceTypes - Invalid piece type: " << token << std::endl;
+ }
+ parse_attribute("sittuyinPromotion", v->sittuyinPromotion);
+ // promotion limit
+ const auto& it_prom_limit = config.find("promotionLimit");
+ if (it_prom_limit != config.end())
+ {
+ char token;
+ size_t idx = 0;
+ std::stringstream ss(it_prom_limit->second);
+ while (!ss.eof() && ss >> token && (idx = v->pieceToChar.find(toupper(token))) != std::string::npos
+ && ss >> token && ss >> v->promotionLimit[idx]) {}
+ if (DoCheck && idx == std::string::npos)
+ std::cerr << "promotionLimit - Invalid piece type: " << token << std::endl;
+ else if (DoCheck && !ss.eof())
+ std::cerr << "promotionLimit - Invalid piece count for type: " << v->pieceToChar[idx] << std::endl;
+ }
+ // promoted piece types
+ const auto& it_prom_pt = config.find("promotedPieceType");
+ if (it_prom_pt != config.end())
+ {
+ char token;
+ size_t idx = 0, idx2 = 0;
+ std::stringstream ss(it_prom_pt->second);
+ while ( ss >> token && (idx = v->pieceToChar.find(toupper(token))) != std::string::npos && ss >> token
+ && ss >> token && (idx2 = (token == '-' ? 0 : v->pieceToChar.find(toupper(token)))) != std::string::npos)
+ v->promotedPieceType[idx] = PieceType(idx2);
+ if (DoCheck && (idx == std::string::npos || idx2 == std::string::npos))
+ std::cerr << "promotedPieceType - Invalid piece type: " << token << std::endl;
+ }
+ parse_attribute("piecePromotionOnCapture", v->piecePromotionOnCapture);
+ parse_attribute("mandatoryPawnPromotion", v->mandatoryPawnPromotion);
+ parse_attribute("mandatoryPiecePromotion", v->mandatoryPiecePromotion);
+ parse_attribute("pieceDemotion", v->pieceDemotion);
+ parse_attribute("blastOnCapture", v->blastOnCapture);
+ parse_attribute("doubleStep", v->doubleStep);
+ parse_attribute("doubleStepRank", v->doubleStepRank);
+ parse_attribute("doubleStepRankMin", v->doubleStepRankMin);
+ parse_attribute("enPassantRegion", v->enPassantRegion);
+ parse_attribute("castling", v->castling);
+ parse_attribute("castlingDroppedPiece", v->castlingDroppedPiece);
+ parse_attribute("castlingKingsideFile", v->castlingKingsideFile);
+ parse_attribute("castlingQueensideFile", v->castlingQueensideFile);
+ parse_attribute("castlingRank", v->castlingRank);
+ parse_attribute("castlingKingFile", v->castlingKingFile);
+ parse_attribute("castlingKingPiece", v->castlingKingPiece, v->pieceToChar);
+ parse_attribute("castlingRookPiece", v->castlingRookPiece, v->pieceToChar);
+ parse_attribute("kingType", v->kingType, v->pieceToChar);
+ parse_attribute("checking", v->checking);
+ parse_attribute("dropChecks", v->dropChecks);
+ parse_attribute("mustCapture", v->mustCapture);
+ parse_attribute("mustDrop", v->mustDrop);
+ parse_attribute("mustDropType", v->mustDropType, v->pieceToChar);
+ parse_attribute("pieceDrops", v->pieceDrops);
+ parse_attribute("dropLoop", v->dropLoop);
+ parse_attribute("capturesToHand", v->capturesToHand);
+ parse_attribute("firstRankPawnDrops", v->firstRankPawnDrops);
+ parse_attribute("promotionZonePawnDrops", v->promotionZonePawnDrops);
+ parse_attribute("dropOnTop", v->dropOnTop);
+ parse_attribute("enclosingDrop", v->enclosingDrop);
+ parse_attribute("enclosingDropStart", v->enclosingDropStart);
+ parse_attribute("whiteDropRegion", v->whiteDropRegion);
+ parse_attribute("blackDropRegion", v->blackDropRegion);
+ parse_attribute("sittuyinRookDrop", v->sittuyinRookDrop);
+ parse_attribute("dropOppositeColoredBishop", v->dropOppositeColoredBishop);
+ parse_attribute("dropPromoted", v->dropPromoted);
+ parse_attribute("dropNoDoubled", v->dropNoDoubled, v->pieceToChar);
+ parse_attribute("dropNoDoubledCount", v->dropNoDoubledCount);
+ parse_attribute("immobilityIllegal", v->immobilityIllegal);
+ parse_attribute("gating", v->gating);
+ parse_attribute("arrowGating", v->arrowGating);
+ parse_attribute("seirawanGating", v->seirawanGating);
+ parse_attribute("cambodianMoves", v->cambodianMoves);
+ parse_attribute("diagonalLines", v->diagonalLines);
+ parse_attribute("pass", v->pass);
+ parse_attribute("passOnStalemate", v->passOnStalemate);
+ parse_attribute("makpongRule", v->makpongRule);
+ parse_attribute("flyingGeneral", v->flyingGeneral);
+ parse_attribute("soldierPromotionRank", v->soldierPromotionRank);
+ parse_attribute("flipEnclosedPieces", v->flipEnclosedPieces);
+ // game end
+ parse_attribute("nMoveRule", v->nMoveRule);
+ parse_attribute("nFoldRule", v->nFoldRule);
+ parse_attribute("nFoldValue", v->nFoldValue);
+ parse_attribute("nFoldValueAbsolute", v->nFoldValueAbsolute);
+ parse_attribute("perpetualCheckIllegal", v->perpetualCheckIllegal);
+ parse_attribute("moveRepetitionIllegal", v->moveRepetitionIllegal);
+ parse_attribute("stalemateValue", v->stalemateValue);
+ parse_attribute("stalematePieceCount", v->stalematePieceCount);
+ parse_attribute("checkmateValue", v->checkmateValue);
+ parse_attribute("shogiPawnDropMateIllegal", v->shogiPawnDropMateIllegal);
+ parse_attribute("shatarMateRule", v->shatarMateRule);
+ parse_attribute("bikjangRule", v->bikjangRule);
+ parse_attribute("extinctionValue", v->extinctionValue);
+ parse_attribute("extinctionClaim", v->extinctionClaim);
+ parse_attribute("extinctionPseudoRoyal", v->extinctionPseudoRoyal);
+ // extinction piece types
+ const auto& it_ext = config.find("extinctionPieceTypes");
+ if (it_ext != config.end())
+ {
+ v->extinctionPieceTypes = {};
+ char token;
+ size_t idx = 0;
+ std::stringstream ss(it_ext->second);
+ while (ss >> token && (idx = token == '*' ? size_t(ALL_PIECES) : v->pieceToChar.find(toupper(token))) != std::string::npos)
+ v->extinctionPieceTypes.insert(PieceType(idx));
+ if (DoCheck && idx == std::string::npos)
+ std::cerr << "extinctionPieceTypes - Invalid piece type: " << token << std::endl;
+ }
+ parse_attribute("extinctionPieceCount", v->extinctionPieceCount);
+ parse_attribute("extinctionOpponentPieceCount", v->extinctionOpponentPieceCount);
+ parse_attribute("flagPiece", v->flagPiece, v->pieceToChar);
+ parse_attribute("whiteFlag", v->whiteFlag);
+ parse_attribute("blackFlag", v->blackFlag);
+ parse_attribute("flagMove", v->flagMove);
+ parse_attribute("checkCounting", v->checkCounting);
+ parse_attribute("connectN", v->connectN);
+ parse_attribute("materialCounting", v->materialCounting);
+ parse_attribute("countingRule", v->countingRule);
+ // Report invalid options
+ if (DoCheck)
+ {
+ const std::set<std::string>& parsedKeys = config.get_comsumed_keys();
+ for (const auto& it : config)
+ if (parsedKeys.find(it.first) == parsedKeys.end())
+ std::cerr << "Invalid option: " << it.first << std::endl;
+ }
+ // Check consistency
+ if (DoCheck)
+ {
+ // pieces
+ for (PieceType pt : v->pieceTypes)
+ {
+ for (Color c : {WHITE, BLACK})
+ if (std::count(v->pieceToChar.begin(), v->pieceToChar.end(), v->pieceToChar[make_piece(c, pt)]) != 1)
+ std::cerr << piece_name(pt) << " - Ambiguous piece character: " << v->pieceToChar[make_piece(c, pt)] << std::endl;
+ }
+
+ // startFen
+ 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 != "-")
+ {
+ 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;
+ for (PieceType pt : v->pieceTypes)
+ {
+ char ptl = tolower(v->pieceToChar[pt]);
+ if (v->pieceToCharTable.find(ptl) == std::string::npos && fenBoard.find(ptl) != std::string::npos)
+ std::cerr << "pieceToCharTable - Missing piece type: " << ptl << std::endl;
+ char ptu = toupper(v->pieceToChar[pt]);
+ if (v->pieceToCharTable.find(ptu) == std::string::npos && fenBoard.find(ptu) != std::string::npos)
+ std::cerr << "pieceToCharTable - Missing piece type: " << ptu << std::endl;
+ }
+ }
+
+ // Contradictory options
+ if (!v->checking && v->checkCounting)
+ std::cerr << "checkCounting=true requires checking=true." << std::endl;
+ if (v->doubleStep && v->doubleStepRankMin > v->doubleStepRank)
+ std::cerr << "Inconsistent settings: doubleStepRankMin > doubleStepRank." << std::endl;
+ if (v->castling && v->castlingRank > v->maxRank)
+ std::cerr << "Inconsistent settings: castlingRank > maxRank." << std::endl;
+ if (v->castling && v->castlingQueensideFile > v->castlingKingsideFile)
+ std::cerr << "Inconsistent settings: castlingQueensideFile > castlingKingsideFile." << std::endl;
+
+ // Check for limitations
+
+ // Options incompatible with royal kings
+ if (v->pieceTypes.find(KING) != v->pieceTypes.end())
+ {
+ if (v->blastOnCapture)
+ std::cerr << "Can not use kings with blastOnCapture." << std::endl;
+ if (v->flipEnclosedPieces)
+ std::cerr << "Can not use kings with flipEnclosedPieces." << std::endl;
+ // We can not fully check custom king movements at this point
+ if (!is_custom(v->kingType))
+ {
+ const PieceInfo* pi = pieceMap.find(v->kingType)->second;
+ if ( pi->hopper[MODALITY_QUIET].size()
+ || pi->hopper[MODALITY_CAPTURE].size()
+ || std::any_of(pi->steps[MODALITY_CAPTURE].begin(),
+ pi->steps[MODALITY_CAPTURE].end(),
+ [](const std::pair<const Direction, int>& d) { return d.second; }))
+ std::cerr << piece_name(v->kingType) << " is not supported as kingType." << std::endl;
+ }
+ }
+ }
+ return v;
+}
+
+template Variant* VariantParser<true>::parse();
+template Variant* VariantParser<false>::parse();
+template Variant* VariantParser<true>::parse(Variant* v);
+template Variant* VariantParser<false>::parse(Variant* v);
++
++} // namespace Stockfish
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PARSER_H_INCLUDED
+#define PARSER_H_INCLUDED
+
+#include <iostream>
+
+#include "variant.h"
+
++namespace Stockfish {
++
+class Config : public std::map<std::string, std::string> {
+public:
+ Config::iterator find (const std::string& s) {
+ constexpr bool PrintOptions = false; // print config options?
+ if (PrintOptions)
+ std::cout << s << std::endl;
+ consumedKeys.insert(s);
+ return std::map<std::string, std::string>::find(s);
+ }
+ const std::set<std::string>& get_comsumed_keys() {
+ return consumedKeys;
+ }
+private:
+ std::set<std::string> consumedKeys = {};
+};
+
+template <bool DoCheck>
+class VariantParser {
+public:
+ VariantParser(const Config& c) : config (c) {};
+ Variant* parse();
+ Variant* parse(Variant* v);
+
+private:
+ Config config;
+ template <class T> void parse_attribute(const std::string& key, T& target);
+ void parse_attribute(const std::string& key, PieceType& target, std::string pieceToChar);
+};
+
++} // namespace Stockfish
++
+#endif // #ifndef PARSER_H_INCLUDED
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "partner.h"
+#include "thread.h"
+#include "uci.h"
+
++namespace Stockfish {
++
+PartnerHandler Partner; // Global object
+
+void PartnerHandler::reset() {
+ fast = sitRequested = partnerDead = weDead = weWin = weVirtualWin = weVirtualLoss = false;
+ time = opptime = 0;
+}
+
+template <PartnerType p>
+void PartnerHandler::ptell(const std::string& message) {
+ if (p == ALL_PARTNERS || (p == FAIRY && isFairy) || (p == HUMAN && !isFairy))
+ sync_cout << "tellics ptell " << message << sync_endl;
+}
+
+void PartnerHandler::parse_partner(std::istringstream& is) {
+ std::string token;
+ if (is >> token)
+ // handshake to identify Fairy-Stockfish
+ ptell("partner Fairy-Stockfish is an engine. Ask it 'help' for supported commands.");
+ else
+ isFairy = false;
+}
+
+void PartnerHandler::parse_ptell(std::istringstream& is, const Position& pos) {
+ std::string token;
+ is >> token;
+ if (token == "partner")
+ {
+ // handshake to identify Fairy-Stockfish
+ if (is >> token && token == "Fairy-Stockfish")
+ isFairy = true;
+ }
+ else if (token == "help")
+ {
+ if (!(is >> token))
+ {
+ ptell<HUMAN>("I listen to the commands help, sit, go, move, fast, slow, dead, x, time, and otim.");
+ ptell<HUMAN>("Tell 'help sit', etc. for details.");
+ }
+ else if (token == "sit")
+ ptell<HUMAN>("After receiving 'sit', I stop moving. Also see 'go'.");
+ else if (token == "go")
+ ptell<HUMAN>("After receiving 'go', I will no longer sit.");
+ else if (token == "move")
+ {
+ ptell<HUMAN>("After receiving 'move', I will move immediately." );
+ ptell<HUMAN>("If you specify a valid move, e.g., 'move e2e4', I will play it.");
+ }
+ else if (token == "fast")
+ ptell<HUMAN>("After receiving 'go', I will play fast.");
+ else if (token == "slow")
+ ptell<HUMAN>("After receiving 'slow', I will play at normal speed.");
+ else if (token == "dead")
+ ptell<HUMAN>("After receiving 'dead', I assume you are dead and I play fast.");
+ else if (token == "x")
+ ptell<HUMAN>("After receiving 'x', I assume I can play normally again.");
+ else if (token == "time")
+ {
+ ptell<HUMAN>("'time' together with your time in centiseconds allows me to consider your time.");
+ ptell<HUMAN>("E.g., 'time 1000' for 10 seconds.");
+ }
+ else if (token == "otim")
+ ptell<HUMAN>("'otim' together with your opponent's time in centiseconds allows me to consider his time.");
+ }
+ else if (!pos.two_boards())
+ return;
+ else if (token == "sit")
+ {
+ // Avoid deadlocking sit
+ if (!isFairy || !weWin)
+ sitRequested = true;
+ ptell<HUMAN>("I sit, tell me 'go' to continue");
+ }
+ else if (token == "go")
+ {
+ sitRequested = false;
+ Threads.stop = true;
+ }
+ else if (token == "move")
+ {
+ if (is >> token)
+ {
+ // if the given move is valid and we can still abort the search, play it
+ Move move = UCI::to_move(pos, token);
+ if (move && !Threads.abort.exchange(true))
+ moveRequested = move;
+ else
+ ptell<HUMAN>("sorry, not possible");
+ }
+ else
+ // Move immediately on request
+ Threads.stop = true;
+ }
+ else if (token == "fast")
+ {
+ fast = true;
+ ptell<HUMAN>("I play fast, tell me 'slow' to play normally again");
+ }
+ else if (token == "slow")
+ {
+ fast = false;
+ ptell<HUMAN>("I play at normal speed again.");
+ }
+ else if (token == "dead")
+ {
+ partnerDead = true;
+ ptell<HUMAN>("I play fast, tell me 'x' if you are no longer dead.");
+ }
+ else if (token == "x")
+ {
+ partnerDead = false;
+ sitRequested = false;
+ ptell<HUMAN>("I play normally again");
+ }
+ else if (token == "time")
+ {
+ int value;
+ time = (is >> value) ? value * 10 : 0;
+ }
+ else if (token == "otim")
+ {
+ int value;
+ opptime = (is >> value) ? value * 10 : 0;
+ }
+}
+
+template void PartnerHandler::ptell<HUMAN>(const std::string&);
+template void PartnerHandler::ptell<FAIRY>(const std::string&);
+template void PartnerHandler::ptell<ALL_PARTNERS>(const std::string&);
++
++} // namespace Stockfish
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PARTNER_H_INCLUDED
+#define PARTNER_H_INCLUDED
+
+#include <atomic>
+#include <sstream>
+
+#include "misc.h"
+#include "position.h"
+
++namespace Stockfish {
++
+/// PartnerHandler manages the communication with the partner
+/// in games played on two boards, such as bughouse.
+
+enum PartnerType {
+ HUMAN,
+ FAIRY,
+ ALL_PARTNERS
+};
+
+struct PartnerHandler {
+ void reset();
+ template <PartnerType p = ALL_PARTNERS>
+ void ptell(const std::string& message);
+ void parse_partner(std::istringstream& is);
+ void parse_ptell(std::istringstream& is, const Position& pos);
+
+ std::atomic<bool> isFairy;
+ std::atomic<bool> fast, sitRequested, partnerDead, weDead, weWin, weVirtualWin, weVirtualLoss;
+ std::atomic<TimePoint> time, opptime;
+ Move moveRequested;
+};
+
+extern PartnerHandler Partner;
+
++} // namespace Stockfish
++
+#endif // #ifndef PARTNER_H_INCLUDED
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "types.h"
+#include "piece.h"
+
++namespace Stockfish {
++
+PieceMap pieceMap; // Global object
+
+
+namespace {
+ std::map<char, std::vector<std::pair<int, int>>> leaperAtoms = {
+ {'W', {std::make_pair(1, 0)}},
+ {'F', {std::make_pair(1, 1)}},
+ {'D', {std::make_pair(2, 0)}},
+ {'N', {std::make_pair(2, 1)}},
+ {'A', {std::make_pair(2, 2)}},
+ {'H', {std::make_pair(3, 0)}},
+ {'L', {std::make_pair(3, 1)}},
+ {'C', {std::make_pair(3, 1)}},
+ {'J', {std::make_pair(3, 2)}},
+ {'Z', {std::make_pair(3, 2)}},
+ {'G', {std::make_pair(3, 3)}},
+ {'K', {std::make_pair(1, 0), std::make_pair(1, 1)}},
+ };
+ std::map<char, std::vector<std::pair<int, int>>> riderAtoms = {
+ {'R', {std::make_pair(1, 0)}},
+ {'B', {std::make_pair(1, 1)}},
+ {'Q', {std::make_pair(1, 0), std::make_pair(1, 1)}},
+ };
+ const std::string verticals = "fbvh";
+ const std::string horizontals = "rlsh";
+ // from_betza creates a piece by parsing Betza notation
+ // https://en.wikipedia.org/wiki/Betza%27s_funny_notation
+ PieceInfo* from_betza(const std::string& betza, const std::string& name) {
+ PieceInfo* p = new PieceInfo();
+ p->name = name;
+ p->betza = betza;
+ std::vector<MoveModality> moveModalities = {};
+ bool hopper = false;
+ bool rider = false;
+ bool lame = false;
+ int distance = 0;
+ std::vector<std::string> prelimDirections = {};
+ for (std::string::size_type i = 0; i < betza.size(); i++)
+ {
+ char c = betza[i];
+ // Modality
+ if (c == 'm' || c == 'c')
+ moveModalities.push_back(c == 'c' ? MODALITY_CAPTURE : MODALITY_QUIET);
+ // Hopper
+ else if (c == 'p' || c == 'g')
+ {
+ hopper = true;
+ // Grasshopper
+ if (c == 'g')
+ distance = 1;
+ }
+ // Lame leaper
+ else if (c == 'n')
+ lame = true;
+ // Directional modifiers
+ else if (verticals.find(c) != std::string::npos || horizontals.find(c) != std::string::npos)
+ {
+ if (i + 1 < betza.size())
+ {
+ char c2 = betza[i+1];
+ // Can modifiers be combined?
+ if ( c2 == c
+ || (verticals.find(c) != std::string::npos && horizontals.find(c2) != std::string::npos)
+ || (horizontals.find(c) != std::string::npos && verticals.find(c2) != std::string::npos))
+ {
+ prelimDirections.push_back(std::string(1, c) + c2);
+ i++;
+ continue;
+ }
+ }
+ prelimDirections.push_back(std::string(2, c));
+ }
+ // Move atom
+ else if (leaperAtoms.find(c) != leaperAtoms.end() || riderAtoms.find(c) != riderAtoms.end())
+ {
+ const auto& atoms = riderAtoms.find(c) != riderAtoms.end() ? riderAtoms.find(c)->second
+ : leaperAtoms.find(c)->second;
+ // Check for rider
+ if (riderAtoms.find(c) != riderAtoms.end())
+ rider = true;
+ if (i + 1 < betza.size() && (isdigit(betza[i+1]) || betza[i+1] == c))
+ {
+ rider = true;
+ // limited distance riders
+ if (isdigit(betza[i+1]))
+ distance = betza[i+1] - '0';
+ i++;
+ }
+ if (!rider && lame)
+ distance = -1;
+ // No modality qualifier means m+c
+ if (moveModalities.size() == 0)
+ {
+ moveModalities.push_back(MODALITY_QUIET);
+ moveModalities.push_back(MODALITY_CAPTURE);
+ }
+ // Define moves
+ for (const auto& atom : atoms)
+ {
+ std::vector<std::string> directions = {};
+ // Split directions for orthogonal pieces
+ // This is required e.g. to correctly interpret fsW for soldiers
+ for (auto s : prelimDirections)
+ if (atoms.size() == 1 && atom.second == 0 && s[0] != s[1])
+ {
+ directions.push_back(std::string(2, s[0]));
+ directions.push_back(std::string(2, s[1]));
+ }
+ else
+ directions.push_back(s);
+ // Add moves
+ for (auto modality : moveModalities)
+ {
+ auto& v = hopper ? p->hopper[modality]
+ : rider ? p->slider[modality]
+ : p->steps[modality];
+ 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[Direction(atom.first * FILE_NB + atom.second)] = distance;
+ 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[Direction(-atom.first * FILE_NB - atom.second)] = distance;
+ 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[Direction(-atom.second * FILE_NB + atom.first)] = distance;
+ 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[Direction(atom.second * FILE_NB - atom.first)] = distance;
+ 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[Direction(atom.second * FILE_NB + atom.first)] = distance;
+ 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[Direction(-atom.second * FILE_NB - atom.first)] = distance;
+ 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[Direction(-atom.first * FILE_NB + atom.second)] = distance;
+ 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[Direction(atom.first * FILE_NB - atom.second)] = distance;
+ }
+ }
+ // Reset state
+ moveModalities.clear();
+ prelimDirections.clear();
+ hopper = false;
+ rider = false;
+ }
+ }
+ return p;
+ }
+ // Special multi-leg betza description for Janggi elephant
+ PieceInfo* janggi_elephant_piece() {
+ PieceInfo* p = from_betza("nZ", "");
+ p->betza = "mafsmafW"; // for compatiblity with XBoard/Winboard
+ return p;
+ }
+}
+
+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"));
+ add(ROOK, from_betza("R", "rook"));
+ add(QUEEN, from_betza("Q", "queen"));
+ add(FERS, from_betza("F", "fers"));
+ add(ALFIL, from_betza("A", "alfil"));
+ add(FERS_ALFIL, from_betza("FA", "fersAlfil"));
+ add(SILVER, from_betza("FfW", "silver"));
+ add(AIWOK, from_betza("RNF", "aiwok"));
+ add(BERS, from_betza("RF", "bers"));
+ add(ARCHBISHOP, from_betza("BN", "archbishop"));
+ add(CHANCELLOR, from_betza("RN", "chancellor"));
+ add(AMAZON, from_betza("QN", "amazon"));
+ add(KNIBIS, from_betza("mNcB", "knibis"));
+ add(BISKNI, from_betza("mBcN", "biskni"));
+ add(KNIROO, from_betza("mNcR", "kniroo"));
+ add(ROOKNI, from_betza("mRcN", "rookni"));
+ add(SHOGI_PAWN, from_betza("fW", "shogiPawn"));
+ add(LANCE, from_betza("fR", "lance"));
+ add(SHOGI_KNIGHT, from_betza("fN", "shogiKnight"));
+ add(GOLD, from_betza("WfF", "gold"));
+ add(DRAGON_HORSE, from_betza("BW", "dragonHorse"));
+ add(CLOBBER_PIECE, from_betza("cW", "clobber"));
+ add(BREAKTHROUGH_PIECE, from_betza("fWfFcF", "breakthrough"));
+ add(IMMOBILE_PIECE, from_betza("", "immobile"));
+ add(CANNON, from_betza("mRcpR", "cannon"));
+ add(JANGGI_CANNON, from_betza("pR", "janggiCannon"));
+ add(SOLDIER, from_betza("fsW", "soldier"));
+ add(HORSE, from_betza("nN", "horse"));
+ add(ELEPHANT, from_betza("nA", "elephant"));
+ add(JANGGI_ELEPHANT, janggi_elephant_piece());
+ add(BANNER, from_betza("RcpRnN", "banner"));
+ add(WAZIR, from_betza("W", "wazir"));
+ 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) {
+ insert(std::pair<PieceType, const PieceInfo*>(pt, p));
+}
+
+void PieceMap::clear_all() {
+ for (auto const& element : *this)
+ delete element.second;
+ clear();
+}
++
++} // namespace Stockfish
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PIECE_H_INCLUDED
+#define PIECE_H_INCLUDED
+
+#include <string>
+#include <map>
+
+#include "types.h"
+#include "variant.h"
+
++namespace Stockfish {
+
+enum MoveModality {MODALITY_QUIET, MODALITY_CAPTURE, MOVE_MODALITY_NB};
+
+/// PieceInfo struct stores information about the piece movements.
+
+struct PieceInfo {
+ std::string name = "";
+ std::string betza = "";
+ std::map<Direction, int> steps[MOVE_MODALITY_NB] = {};
+ std::map<Direction, int> slider[MOVE_MODALITY_NB] = {};
+ std::map<Direction, int> hopper[MOVE_MODALITY_NB] = {};
+};
+
+struct PieceMap : public std::map<PieceType, const PieceInfo*> {
+ void init(const Variant* v = nullptr);
+ void add(PieceType pt, const PieceInfo* v);
+ void clear_all();
+};
+
+extern PieceMap pieceMap;
+
+inline std::string piece_name(PieceType pt) {
+ return is_custom(pt) ? "customPiece" + std::to_string(pt - CUSTOM_PIECES + 1)
+ : pieceMap.find(pt)->second->name;
+}
+
++} // namespace Stockfish
++
+#endif // #ifndef PIECE_H_INCLUDED
return st;
}
+// Variant-specific
+
+inline int Position::count_in_hand(PieceType pt) const {
+ return pieceCountInHand[WHITE][pt] + pieceCountInHand[BLACK][pt];
+}
+
+inline int Position::count_in_hand(Color c, PieceType pt) const {
+ return pieceCountInHand[c][pt];
+}
+
+inline int Position::count_with_hand(Color c, PieceType pt) const {
+ return pieceCount[make_piece(c, pt)] + pieceCountInHand[c][pt];
+}
+
+inline bool Position::bikjang() const {
+ return st->bikjang;
+}
+
+inline bool Position::allow_virtual_drop(Color c, PieceType pt) const {
+ assert(two_boards());
+ // Do we allow a virtual drop?
+ return pt != KING && ( count_in_hand(c, PAWN) >= -(pt == PAWN)
+ && count_in_hand(c, KNIGHT) >= -(pt == PAWN)
+ && count_in_hand(c, BISHOP) >= -(pt == PAWN)
+ && count_in_hand(c, ROOK) >= 0
+ && count_in_hand(c, QUEEN) >= 0);
+}
+
+inline Value Position::material_counting_result() const {
+ auto weigth_count = [this](PieceType pt, int v){ return v * (count(WHITE, pt) - count(BLACK, pt)); };
+ int materialCount;
+ Value result;
+ switch (var->materialCounting)
+ {
+ case JANGGI_MATERIAL:
+ materialCount = weigth_count(ROOK, 13)
+ + weigth_count(JANGGI_CANNON, 7)
+ + weigth_count(HORSE, 5)
+ + weigth_count(JANGGI_ELEPHANT, 3)
+ + weigth_count(WAZIR, 3)
+ + weigth_count(SOLDIER, 2)
+ - 1;
+ result = materialCount > 0 ? VALUE_MATE : -VALUE_MATE;
+ break;
+ case UNWEIGHTED_MATERIAL:
+ result = count(WHITE, ALL_PIECES) > count(BLACK, ALL_PIECES) ? VALUE_MATE
+ : count(WHITE, ALL_PIECES) < count(BLACK, ALL_PIECES) ? -VALUE_MATE
+ : VALUE_DRAW;
+ break;
+ case WHITE_DRAW_ODDS:
+ result = VALUE_MATE;
+ break;
+ case BLACK_DRAW_ODDS:
+ result = -VALUE_MATE;
+ break;
+ default:
+ assert(false);
+ result = VALUE_DRAW;
+ }
+ return sideToMove == WHITE ? result : -result;
+}
+
+inline void Position::add_to_hand(Piece pc) {
+ pieceCountInHand[color_of(pc)][type_of(pc)]++;
+ pieceCountInHand[color_of(pc)][ALL_PIECES]++;
+ psq += PSQT::psq[pc][SQ_NONE];
+}
+
+inline void Position::remove_from_hand(Piece pc) {
+ pieceCountInHand[color_of(pc)][type_of(pc)]--;
+ pieceCountInHand[color_of(pc)][ALL_PIECES]--;
+ psq -= PSQT::psq[pc][SQ_NONE];
+}
+
+inline void Position::drop_piece(Piece pc_hand, Piece pc_drop, Square s) {
+ assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)] > 0 || var->twoBoards);
+ put_piece(pc_drop, s, pc_drop != pc_hand, pc_drop != pc_hand ? pc_hand : NO_PIECE);
+ remove_from_hand(pc_hand);
+}
+
+inline void Position::undrop_piece(Piece pc_hand, Square s) {
+ remove_piece(s);
+ board[s] = NO_PIECE;
+ add_to_hand(pc_hand);
+ assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)] > 0 || var->twoBoards);
+}
+
+ } // namespace Stockfish
+
#endif // #ifndef POSITION_H_INCLUDED
#include "bitboard.h"
#include "types.h"
+#include "piece.h"
+#include "variant.h"
+#include "misc.h"
+
++
+ namespace Stockfish {
+
+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, GoldValueMg, DragonHorseValueMg,
+ ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, 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, 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, GoldValueMg, DragonHorseValueMg,
+ ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, 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, 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, GoldValueEg, DragonHorseValueEg,
+ ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, 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, 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, GoldValueEg, DragonHorseValueEg,
+ ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, 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, VALUE_ZERO, VALUE_ZERO,
+ },
+};
+
+
namespace
{
#include "types.h"
+#include "variant.h"
- namespace PSQT
+ namespace Stockfish::PSQT
{
-extern Score psq[PIECE_NB][SQUARE_NB];
+extern Score psq[PIECE_NB][SQUARE_NB + 1];
// Fill psqt array from a set of internally linked parameters
-extern void init();
+extern void init(const Variant*);
- } // namespace PSQT
+ } // namespace Stockfish::PSQT
#endif // PSQT_H_INCLUDED
--- /dev/null
+/*
+ Based on Jean-Francois Romang work
+ https://github.com/jromang/Stockfish/blob/pyfish/src/pyfish.cpp
+*/
+
+#include <Python.h>
+#include <sstream>
+
+#include "misc.h"
+#include "types.h"
+#include "bitboard.h"
+#include "evaluate.h"
+#include "position.h"
+#include "search.h"
+#include "syzygy/tbprobe.h"
+#include "thread.h"
+#include "tt.h"
+#include "uci.h"
+#include "piece.h"
+#include "variant.h"
+#include "apiutil.h"
+
++using namespace Stockfish;
+
+static PyObject* PyFFishError;
+
+void buildPosition(Position& pos, StateListPtr& states, const char *variant, const char *fen, PyObject *moveList, const bool chess960) {
+ states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
+
+ const Variant* v = variants.find(std::string(variant))->second;
+ if (strcmp(fen, "startpos") == 0)
+ fen = v->startFen.c_str();
+ Options["UCI_Chess960"] = chess960;
+ pos.set(v, std::string(fen), chess960, &states->back(), Threads.main());
+
+ // parse move list
+ int numMoves = PyList_Size(moveList);
+ for (int i = 0; i < numMoves ; i++)
+ {
+ PyObject *MoveStr = PyUnicode_AsEncodedString( PyList_GetItem(moveList, i), "UTF-8", "strict");
+ std::string moveStr(PyBytes_AS_STRING(MoveStr));
+ Py_XDECREF(MoveStr);
+ Move m;
+ if ((m = UCI::to_move(pos, moveStr)) != MOVE_NONE)
+ {
+ // do the move
+ states->emplace_back();
+ pos.do_move(m, states->back());
+ }
+ else
+ PyErr_SetString(PyExc_ValueError, (std::string("Invalid move '") + moveStr + "'").c_str());
+ }
+ return;
+}
+
+extern "C" PyObject* pyffish_version(PyObject* self) {
+ return Py_BuildValue("(iii)", 0, 0, 57);
+}
+
+extern "C" PyObject* pyffish_info(PyObject* self) {
+ return Py_BuildValue("s", engine_info().c_str());
+}
+
+extern "C" PyObject* pyffish_variants(PyObject* self, PyObject *args) {
+ PyObject* varList = PyList_New(0);
+
+ for (std::string v : variants.get_keys())
+ {
+ PyObject* variant = Py_BuildValue("s", v.c_str());
+ PyList_Append(varList, variant);
+ Py_XDECREF(variant);
+ }
+
+ PyObject* Result = Py_BuildValue("O", varList);
+ Py_XDECREF(varList);
+ return Result;
+}
+
+// INPUT option name, option value
+extern "C" PyObject* pyffish_setOption(PyObject* self, PyObject *args) {
+ const char *name;
+ PyObject *valueObj;
+ if (!PyArg_ParseTuple(args, "sO", &name, &valueObj)) return NULL;
+
+ if (Options.count(name))
+ {
+ PyObject *Value = PyUnicode_AsEncodedString( PyObject_Str(valueObj), "UTF-8", "strict");
+ Options[name] = std::string(PyBytes_AS_STRING(Value));
+ Py_XDECREF(Value);
+ }
+ else
+ {
+ PyErr_SetString(PyExc_ValueError, (std::string("No such option ") + name + "'").c_str());
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+// INPUT variant config
+extern "C" PyObject* pyffish_loadVariantConfig(PyObject* self, PyObject *args) {
+ const char *config;
+ if (!PyArg_ParseTuple(args, "s", &config))
+ return NULL;
+ std::stringstream ss(config);
+ variants.parse_istream<false>(ss);
+ Options["UCI_Variant"].set_combo(variants.get_keys());
+ Py_RETURN_NONE;
+}
+
+// INPUT variant
+extern "C" PyObject* pyffish_startFen(PyObject* self, PyObject *args) {
+ const char *variant;
+
+ if (!PyArg_ParseTuple(args, "s", &variant)) {
+ return NULL;
+ }
+
+ return Py_BuildValue("s", variants.find(std::string(variant))->second->startFen.c_str());
+}
+
+// INPUT variant
+extern "C" PyObject* pyffish_twoBoards(PyObject* self, PyObject *args) {
+ const char *variant;
+
+ if (!PyArg_ParseTuple(args, "s", &variant)) {
+ return NULL;
+ }
+
+ return Py_BuildValue("O", variants.find(std::string(variant))->second->twoBoards ? Py_True : Py_False);
+}
+
+// INPUT variant, fen, move
+extern "C" PyObject* pyffish_getSAN(PyObject* self, PyObject *args) {
+ PyObject* moveList = PyList_New(0);
+ Position pos;
+ const char *fen, *variant, *move;
+
+ int chess960 = false;
+ Notation notation = NOTATION_DEFAULT;
+ if (!PyArg_ParseTuple(args, "sss|pi", &variant, &fen, &move, &chess960, ¬ation)) {
+ return NULL;
+ }
+ if (notation == NOTATION_DEFAULT)
+ notation = default_notation(variants.find(std::string(variant))->second);
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+ std::string moveStr = move;
+
+ Py_XDECREF(moveList);
+ return Py_BuildValue("s", SAN::move_to_san(pos, UCI::to_move(pos, moveStr), notation).c_str());
+}
+
+// INPUT variant, fen, movelist
+extern "C" PyObject* pyffish_getSANmoves(PyObject* self, PyObject *args) {
+ PyObject* sanMoves = PyList_New(0), *moveList;
+ Position pos;
+ const char *fen, *variant;
+
+ int chess960 = false;
+ Notation notation = NOTATION_DEFAULT;
+ if (!PyArg_ParseTuple(args, "ssO!|pi", &variant, &fen, &PyList_Type, &moveList, &chess960, ¬ation)) {
+ return NULL;
+ }
+ if (notation == NOTATION_DEFAULT)
+ notation = default_notation(variants.find(std::string(variant))->second);
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, sanMoves, chess960);
+
+ int numMoves = PyList_Size(moveList);
+ for (int i=0; i<numMoves ; i++) {
+ PyObject *MoveStr = PyUnicode_AsEncodedString( PyList_GetItem(moveList, i), "UTF-8", "strict");
+ std::string moveStr(PyBytes_AS_STRING(MoveStr));
+ Py_XDECREF(MoveStr);
+ Move m;
+ if ((m = UCI::to_move(pos, moveStr)) != MOVE_NONE)
+ {
+ //add to the san move list
+ PyObject *move = Py_BuildValue("s", SAN::move_to_san(pos, m, notation).c_str());
+ PyList_Append(sanMoves, move);
+ Py_XDECREF(move);
+
+ //do the move
+ states->emplace_back();
+ pos.do_move(m, states->back());
+ }
+ else
+ {
+ PyErr_SetString(PyExc_ValueError, (std::string("Invalid move '") + moveStr + "'").c_str());
+ return NULL;
+ }
+ }
+ PyObject *Result = Py_BuildValue("O", sanMoves);
+ Py_XDECREF(sanMoves);
+ return Result;
+}
+
+// INPUT variant, fen, move list
+extern "C" PyObject* pyffish_legalMoves(PyObject* self, PyObject *args) {
+ PyObject* legalMoves = PyList_New(0), *moveList;
+ Position pos;
+ const char *fen, *variant;
+
+ int chess960 = false;
+ if (!PyArg_ParseTuple(args, "ssO!|p", &variant, &fen, &PyList_Type, &moveList, &chess960)) {
+ return NULL;
+ }
+
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+ for (const auto& m : MoveList<LEGAL>(pos))
+ {
+ PyObject *moveStr;
+ moveStr = Py_BuildValue("s", UCI::move(pos, m).c_str());
+ PyList_Append(legalMoves, moveStr);
+ Py_XDECREF(moveStr);
+ }
+
+ PyObject *Result = Py_BuildValue("O", legalMoves);
+ Py_XDECREF(legalMoves);
+ return Result;
+}
+
+// INPUT variant, fen, move list
+extern "C" PyObject* pyffish_getFEN(PyObject* self, PyObject *args) {
+ PyObject *moveList;
+ Position pos;
+ const char *fen, *variant;
+
+ int chess960 = false, sfen = false, showPromoted = false, countStarted = 0;
+ if (!PyArg_ParseTuple(args, "ssO!|pppi", &variant, &fen, &PyList_Type, &moveList, &chess960, &sfen, &showPromoted, &countStarted)) {
+ return NULL;
+ }
+ countStarted = std::min<unsigned int>(countStarted, INT_MAX); // pseudo-unsigned
+
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+ return Py_BuildValue("s", pos.fen(sfen, showPromoted, countStarted).c_str());
+}
+
+// INPUT variant, fen, move list
+extern "C" PyObject* pyffish_givesCheck(PyObject* self, PyObject *args) {
+ PyObject *moveList;
+ Position pos;
+ const char *fen, *variant;
+ int chess960 = false;
+ if (!PyArg_ParseTuple(args, "ssO!|p", &variant, &fen, &PyList_Type, &moveList, &chess960)) {
+ return NULL;
+ }
+
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+ return Py_BuildValue("O", pos.checkers() ? Py_True : Py_False);
+}
+
+// INPUT variant, fen, move list
+// should only be called when the move list is empty
+extern "C" PyObject* pyffish_gameResult(PyObject* self, PyObject *args) {
+ PyObject *moveList;
+ Position pos;
+ const char *fen, *variant;
+ bool gameEnd;
+ Value result;
+ int chess960 = false;
+ if (!PyArg_ParseTuple(args, "ssO!|p", &variant, &fen, &PyList_Type, &moveList, &chess960)) {
+ return NULL;
+ }
+
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+ assert(!MoveList<LEGAL>(pos).size());
+ gameEnd = pos.is_immediate_game_end(result);
+ if (!gameEnd)
+ result = pos.checkers() ? pos.checkmate_value() : pos.stalemate_value();
+
+ return Py_BuildValue("i", result);
+}
+
+// INPUT variant, fen, move list
+extern "C" PyObject* pyffish_isImmediateGameEnd(PyObject* self, PyObject *args) {
+ PyObject *moveList;
+ Position pos;
+ const char *fen, *variant;
+ bool gameEnd;
+ Value result;
+ int chess960 = false;
+ if (!PyArg_ParseTuple(args, "ssO!|p", &variant, &fen, &PyList_Type, &moveList, &chess960)) {
+ return NULL;
+ }
+
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+ gameEnd = pos.is_immediate_game_end(result);
+ return Py_BuildValue("(Oi)", gameEnd ? Py_True : Py_False, result);
+}
+
+// INPUT variant, fen, move list
+extern "C" PyObject* pyffish_isOptionalGameEnd(PyObject* self, PyObject *args) {
+ PyObject *moveList;
+ Position pos;
+ const char *fen, *variant;
+ bool gameEnd;
+ Value result;
+ int chess960 = false, countStarted = 0;
+ if (!PyArg_ParseTuple(args, "ssO!|pi", &variant, &fen, &PyList_Type, &moveList, &chess960, &countStarted)) {
+ return NULL;
+ }
+ countStarted = std::min<unsigned int>(countStarted, INT_MAX); // pseudo-unsigned
+
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+ gameEnd = pos.is_optional_game_end(result, 0, countStarted);
+ return Py_BuildValue("(Oi)", gameEnd ? Py_True : Py_False, result);
+}
+
+// INPUT variant, fen, move list
+extern "C" PyObject* pyffish_hasInsufficientMaterial(PyObject* self, PyObject *args) {
+ PyObject *moveList;
+ Position pos;
+ const char *fen, *variant;
+ int chess960 = false;
+ if (!PyArg_ParseTuple(args, "ssO!|p", &variant, &fen, &PyList_Type, &moveList, &chess960)) {
+ return NULL;
+ }
+
+ StateListPtr states(new std::deque<StateInfo>(1));
+ buildPosition(pos, states, variant, fen, moveList, chess960);
+
+ bool wInsufficient = has_insufficient_material(WHITE, pos);
+ bool bInsufficient = has_insufficient_material(BLACK, pos);
+
+ return Py_BuildValue("(OO)", wInsufficient ? Py_True : Py_False, bInsufficient ? Py_True : Py_False);
+}
+
+// INPUT variant, fen
+extern "C" PyObject* pyffish_validateFen(PyObject* self, PyObject *args) {
+ const char *fen, *variant;
+ int chess960 = false;
+ if (!PyArg_ParseTuple(args, "ss|p", &fen, &variant, &chess960)) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", FEN::validate_fen(std::string(fen), variants.find(std::string(variant))->second, chess960));
+}
+
+
+static PyMethodDef PyFFishMethods[] = {
+ {"version", (PyCFunction)pyffish_version, METH_NOARGS, "Get package version."},
+ {"info", (PyCFunction)pyffish_info, METH_NOARGS, "Get Stockfish version info."},
+ {"variants", (PyCFunction)pyffish_variants, METH_NOARGS, "Get supported variants."},
+ {"set_option", (PyCFunction)pyffish_setOption, METH_VARARGS, "Set UCI option."},
+ {"load_variant_config", (PyCFunction)pyffish_loadVariantConfig, METH_VARARGS, "Load variant configuration."},
+ {"start_fen", (PyCFunction)pyffish_startFen, METH_VARARGS, "Get starting position FEN."},
+ {"two_boards", (PyCFunction)pyffish_twoBoards, METH_VARARGS, "Checks whether the variant is played on two boards."},
+ {"get_san", (PyCFunction)pyffish_getSAN, METH_VARARGS, "Get SAN move from given FEN and UCI move."},
+ {"get_san_moves", (PyCFunction)pyffish_getSANmoves, METH_VARARGS, "Get SAN movelist from given FEN and UCI movelist."},
+ {"legal_moves", (PyCFunction)pyffish_legalMoves, METH_VARARGS, "Get legal moves from given FEN and movelist."},
+ {"get_fen", (PyCFunction)pyffish_getFEN, METH_VARARGS, "Get resulting FEN from given FEN and movelist."},
+ {"gives_check", (PyCFunction)pyffish_givesCheck, METH_VARARGS, "Get check status from given FEN and movelist."},
+ {"game_result", (PyCFunction)pyffish_gameResult, METH_VARARGS, "Get result from given FEN, considering variant end, checkmate, and stalemate."},
+ {"is_immediate_game_end", (PyCFunction)pyffish_isImmediateGameEnd, METH_VARARGS, "Get result from given FEN if variant rules ends the game."},
+ {"is_optional_game_end", (PyCFunction)pyffish_isOptionalGameEnd, METH_VARARGS, "Get result from given FEN it rules enable game end by player."},
+ {"has_insufficient_material", (PyCFunction)pyffish_hasInsufficientMaterial, METH_VARARGS, "Checks for insufficient material."},
+ {"validate_fen", (PyCFunction)pyffish_validateFen, METH_VARARGS, "Validate an input FEN."},
+ {NULL, NULL, 0, NULL}, // sentinel
+};
+
+static PyModuleDef pyffishmodule = {
+ PyModuleDef_HEAD_INIT,
+ "pyffish",
+ "Fairy-Stockfish extension module.",
+ -1,
+ PyFFishMethods,
+};
+
+PyMODINIT_FUNC PyInit_pyffish() {
+ PyObject* module;
+
+ module = PyModule_Create(&pyffishmodule);
+ if (module == NULL) {
+ return NULL;
+ }
+ PyFFishError = PyErr_NewException("pyffish.error", NULL, NULL);
+ Py_INCREF(PyFFishError);
+ PyModule_AddObject(module, "error", PyFFishError);
+
+ // values
+ PyModule_AddObject(module, "VALUE_MATE", PyLong_FromLong(VALUE_MATE));
+ PyModule_AddObject(module, "VALUE_DRAW", PyLong_FromLong(VALUE_DRAW));
+
+ // notations
+ PyModule_AddObject(module, "NOTATION_DEFAULT", PyLong_FromLong(NOTATION_DEFAULT));
+ PyModule_AddObject(module, "NOTATION_SAN", PyLong_FromLong(NOTATION_SAN));
+ PyModule_AddObject(module, "NOTATION_LAN", PyLong_FromLong(NOTATION_LAN));
+ PyModule_AddObject(module, "NOTATION_SHOGI_HOSKING", PyLong_FromLong(NOTATION_SHOGI_HOSKING));
+ PyModule_AddObject(module, "NOTATION_SHOGI_HODGES", PyLong_FromLong(NOTATION_SHOGI_HODGES));
+ PyModule_AddObject(module, "NOTATION_SHOGI_HODGES_NUMBER", PyLong_FromLong(NOTATION_SHOGI_HODGES_NUMBER));
+ PyModule_AddObject(module, "NOTATION_JANGGI", PyLong_FromLong(NOTATION_JANGGI));
+ PyModule_AddObject(module, "NOTATION_XIANGQI_WXF", PyLong_FromLong(NOTATION_XIANGQI_WXF));
+
+ // validation
+ PyModule_AddObject(module, "FEN_OK", PyLong_FromLong(FEN::FEN_OK));
+
+ // initialize stockfish
+ pieceMap.init();
+ variants.init();
+ UCI::init(Options);
+ PSQT::init(variants.find(Options["UCI_Variant"])->second);
+ Bitboards::init();
+ Position::init();
+ Bitbases::init();
+ Search::init();
+ Threads.set(Options["Threads"]);
+ Search::clear(); // After threads are up
+
+ return module;
+};
#include "timeman.h"
#include "tt.h"
#include "uci.h"
+#include "xboard.h"
#include "syzygy/tbprobe.h"
+ namespace Stockfish {
+
namespace Search {
LimitsType Limits;
#include "uci.h"
#include "syzygy/tbprobe.h"
#include "tt.h"
+#include "xboard.h"
+ namespace Stockfish {
+
ThreadPool Threads; // Global object
pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
}
void join() { pthread_join(thread, NULL); }
+ void detach() { pthread_detach(thread); }
};
+ } // namespace Stockfish
+
#else // Default case: use STL classes
+ namespace Stockfish {
+
typedef std::thread NativeThread;
+ } // namespace Stockfish
+
#endif
#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED
#include "misc.h"
#include "types.h"
+ namespace Stockfish {
+
-/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
+/// TTEntry struct is the 12 bytes transposition table entry, defined as below:
///
/// key 16 bit
/// depth 8 bit
using std::string;
+ namespace Stockfish {
+
UCI::OptionsMap Options; // Global object
+namespace PSQT {
+ void init(const Variant* v);
+}
+
namespace UCI {
+// standard variants of XBoard/WinBoard
+std::set<string> standard_variants = {
+ "normal", "nocastle", "fischerandom", "knightmate", "3check", "makruk", "shatranj",
+ "asean", "seirawan", "crazyhouse", "bughouse", "suicide", "giveaway", "losers", "atomic",
+ "capablanca", "gothic", "janus", "caparandom", "grand", "shogi", "xiangqi"
+};
+
/// 'On change' actions, triggered by an option's value change
void on_clear_hash(const Option&) { Search::clear(); }
void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
return *this;
}
+void Option::set_combo(std::vector<std::string> newComboValues) {
+ comboValues = newComboValues;
+}
+
+void Option::set_default(std::string newDefault) {
+ defaultValue = currentValue = newDefault;
+
+ // When changing the variant default, suppress variant definition output,
+ // but still do the essential re-initialization of the variant
+ if (on_change)
+ (on_change == on_variant_change ? on_variant_set : on_change)(*this);
+}
+
+const std::string Option::get_type() const {
+ return type;
+}
+
} // namespace UCI
+
+ } // namespace Stockfish
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include "parser.h"
+#include "piece.h"
+#include "variant.h"
+
+using std::string;
+
++namespace Stockfish {
++
+VariantMap variants; // Global object
+
+namespace {
+ // Base variant
+ Variant* variant_base() {
+ Variant* v = new Variant();
+ return v;
+ }
+ // Base for all fairy variants
+ Variant* chess_variant_base() {
+ Variant* v = variant_base();
+ v->pieceToCharTable = "PNBRQ................Kpnbrq................k";
+ return v;
+ }
+ // Standard chess
+ // https://en.wikipedia.org/wiki/Chess
+ Variant* chess_variant() {
+ Variant* v = chess_variant_base();
+ v->nnueFeatures = NNUE_CHESS;
+ return v;
+ }
+ // Chess960 aka Fischer random chess
+ // https://en.wikipedia.org/wiki/Fischer_random_chess
+ Variant* chess960_variant() {
+ Variant* v = chess_variant();
+ v->chess960 = true;
+ return v;
+ }
+ // Standard chess without castling
+ Variant* nocastle_variant() {
+ Variant* v = chess_variant();
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1";
+ v->castling = false;
+ return v;
+ }
+ // Armageddon Chess
+ // https://en.wikipedia.org/wiki/Fast_chess#Armageddon
+ Variant* armageddon_variant() {
+ Variant* v = chess_variant();
+ v->materialCounting = BLACK_DRAW_ODDS;
+ return v;
+ }
+ // Pseudo-variant only used for endgame initialization
+ Variant* fairy_variant() {
+ Variant* v = chess_variant_base();
+ v->add_piece(SILVER, 's');
+ v->add_piece(FERS, 'f');
+ return v;
+ }
+ // Makruk (Thai Chess)
+ // https://en.wikipedia.org/wiki/Makruk
+ Variant* makruk_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "makruk";
+ v->pieceToCharTable = "PN.R.M....SKpn.r.m....sk";
+ v->remove_piece(BISHOP);
+ v->remove_piece(QUEEN);
+ v->add_piece(KHON, 's');
+ v->add_piece(MET, 'm');
+ v->startFen = "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w - - 0 1";
+ v->promotionRank = RANK_6;
+ v->promotionPieceTypes = {MET};
+ v->doubleStep = false;
+ v->castling = false;
+ v->nMoveRule = 0;
+ v->countingRule = MAKRUK_COUNTING;
+ return v;
+ }
+ // Makpong (Defensive Chess)
+ // A Makruk variant used for tie-breaks
+ // https://www.mayhematics.com/v/vol8/vc64b.pdf, p. 177
+ Variant* makpong_variant() {
+ Variant* v = makruk_variant();
+ v->makpongRule = true;
+ return v;
+ }
+ // Ouk Chatrang, Cambodian chess
+ // https://en.wikipedia.org/wiki/Makruk#Cambodian_chess
+ Variant* cambodian_variant() {
+ Variant* v = makruk_variant();
+ v->startFen = "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w DEde - 0 1";
+ v->gating = true;
+ v->cambodianMoves = true;
+ return v;
+ }
+ // Kar Ouk
+ // A variant of Cambodian chess where the first check wins
+ // https://en.wikipedia.org/wiki/Makruk#Ka_Ouk
+ Variant* karouk_variant() {
+ Variant* v = cambodian_variant();
+ v->checkCounting = true;
+ return v;
+ }
+ // ASEAN chess
+ // A simplified version of south-east asian variants
+ // https://aseanchess.org/laws-of-asean-chess/
+ Variant* asean_variant() {
+ Variant* v = chess_variant_base();
+ v->remove_piece(BISHOP);
+ v->remove_piece(QUEEN);
+ v->add_piece(KHON, 'b');
+ v->add_piece(MET, 'q');
+ v->startFen = "rnbqkbnr/8/pppppppp/8/8/PPPPPPPP/8/RNBQKBNR w - - 0 1";
+ v->promotionPieceTypes = {ROOK, KNIGHT, KHON, MET};
+ v->doubleStep = false;
+ v->castling = false;
+ v->countingRule = ASEAN_COUNTING;
+ return v;
+ }
+ // Ai-wok
+ // A makruk variant where the met is replaced by a super-piece moving as rook, knight, or met
+ Variant* aiwok_variant() {
+ Variant* v = makruk_variant();
+ v->pieceToCharTable = "PN.R...A..SKpn.r...a..sk";
+ v->remove_piece(MET);
+ v->add_piece(AIWOK, 'a');
+ v->startFen = "rnsaksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKASNR w - - 0 1";
+ v->promotionPieceTypes = {AIWOK};
+ return v;
+ }
+ // Shatranj
+ // The medieval form of chess, originating from chaturanga
+ // https://en.wikipedia.org/wiki/Shatranj
+ Variant* shatranj_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "shatranj";
+ v->pieceToCharTable = "PN.R.QB....Kpn.r.qb....k";
+ v->remove_piece(BISHOP);
+ v->remove_piece(QUEEN);
+ v->add_piece(ALFIL, 'b');
+ v->add_piece(FERS, 'q');
+ v->startFen = "rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR w - - 0 1";
+ v->promotionPieceTypes = {FERS};
+ v->doubleStep = false;
+ v->castling = false;
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionClaim = true;
+ v->extinctionPieceTypes = {ALL_PIECES};
+ v->extinctionPieceCount = 1;
+ v->extinctionOpponentPieceCount = 2;
+ v->stalemateValue = -VALUE_MATE;
+ v->nMoveRule = 70;
+ return v;
+ }
+ // Chaturanga
+ // The actual rules of the game are not known. This reflects the rules as used on chess.com.
+ // https://en.wikipedia.org/wiki/Chaturanga
+ Variant* chaturanga_variant() {
+ Variant* v = shatranj_variant();
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1";
+ v->extinctionValue = VALUE_NONE;
+ return v;
+ }
+ // Amazon chess
+ // The queen has the additional power of moving like a knight.
+ // https://www.chessvariants.com/diffmove.dir/amazone.html
+ Variant* amazon_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBR..............AKpnbr..............ak";
+ v->remove_piece(QUEEN);
+ v->add_piece(AMAZON, 'a');
+ v->startFen = "rnbakbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBAKBNR w KQkq - 0 1";
+ v->promotionPieceTypes = {AMAZON, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Nightrider chess
+ // Knights are replaced by nightriders.
+ // https://en.wikipedia.org/wiki/Nightrider_(chess)
+ Variant* nightrider_variant() {
+ Variant* v = chess_variant_base();
+ v->remove_piece(KNIGHT);
+ v->add_piece(CUSTOM_PIECES, 'n', "NN");
+ v->promotionPieceTypes = {QUEEN, ROOK, BISHOP, CUSTOM_PIECES};
+ return v;
+ }
+ // Grasshopper chess
+ // https://en.wikipedia.org/wiki/Grasshopper_chess
+ Variant* grasshopper_variant() {
+ Variant* v = chess_variant_base();
+ v->add_piece(CUSTOM_PIECES, 'g', "gQ");
+ v->promotionPieceTypes.insert(CUSTOM_PIECES);
+ v->startFen = "rnbqkbnr/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/RNBQKBNR w KQkq - 0 1";
+ v->doubleStep = false;
+ return v;
+ }
+ // Hoppel-Poppel
+ // A variant from Germany where knights capture like bishops and vice versa
+ // https://www.chessvariants.com/diffmove.dir/hoppel-poppel.html
+ Variant* hoppelpoppel_variant() {
+ Variant* v = chess_variant_base();
+ v->remove_piece(KNIGHT);
+ v->remove_piece(BISHOP);
+ v->add_piece(KNIBIS, 'n');
+ v->add_piece(BISKNI, 'b');
+ v->promotionPieceTypes = {QUEEN, ROOK, BISKNI, KNIBIS};
+ return v;
+ }
+ // New Zealand
+ // Knights capture like rooks and vice versa.
+ Variant* newzealand_variant() {
+ Variant* v = chess_variant_base();
+ v->remove_piece(ROOK);
+ v->remove_piece(KNIGHT);
+ v->add_piece(ROOKNI, 'r');
+ v->add_piece(KNIROO, 'n');
+ v->castlingRookPiece = ROOKNI;
+ v->promotionPieceTypes = {QUEEN, ROOKNI, BISHOP, KNIROO};
+ return v;
+ }
+ // King of the Hill
+ // https://lichess.org/variant/kingOfTheHill
+ Variant* kingofthehill_variant() {
+ Variant* v = chess_variant_base();
+ v->flagPiece = KING;
+ v->whiteFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
+ v->blackFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
+ v->flagMove = false;
+ return v;
+ }
+ // Racing Kings
+ // https://lichess.org/variant/racingKings
+ Variant* racingkings_variant() {
+ Variant* v = chess_variant_base();
+ v->startFen = "8/8/8/8/8/8/krbnNBRK/qrbnNBRQ w - - 0 1";
+ v->flagPiece = KING;
+ v->whiteFlag = Rank8BB;
+ v->blackFlag = Rank8BB;
+ v->flagMove = true;
+ v->castling = false;
+ v->checking = false;
+ return v;
+ }
+ // Knightmate
+ // https://www.chessvariants.com/diffobjective.dir/knightmate.html
+ Variant* knightmate_variant() {
+ Variant* v = chess_variant_base();
+ v->add_piece(COMMONER, 'm');
+ v->remove_piece(KNIGHT);
+ v->startFen = "rmbqkbmr/pppppppp/8/8/8/8/PPPPPPPP/RMBQKBMR w KQkq - 0 1";
+ v->kingType = KNIGHT;
+ v->castlingKingPiece = KING;
+ v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP};
+ return v;
+ }
+ // Losers chess
+ // https://www.chessclub.com/help/Wild17
+ Variant* losers_variant() {
+ Variant* v = chess_variant_base();
+ v->checkmateValue = VALUE_MATE;
+ v->stalemateValue = VALUE_MATE;
+ v->extinctionValue = VALUE_MATE;
+ v->extinctionPieceTypes = {ALL_PIECES};
+ v->extinctionPieceCount = 1;
+ v->mustCapture = true;
+ return v;
+ }
+ // Giveaway chess
+ // Antichess with castling.
+ // https://www.chessvariants.com/diffobjective.dir/giveaway.old.html
+ Variant* giveaway_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "giveaway";
+ v->remove_piece(KING);
+ v->add_piece(COMMONER, 'k');
+ v->castlingKingPiece = COMMONER;
+ v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->stalemateValue = VALUE_MATE;
+ v->extinctionValue = VALUE_MATE;
+ v->extinctionPieceTypes = {ALL_PIECES};
+ v->mustCapture = true;
+ return v;
+ }
+ // Antichess
+ // https://lichess.org/variant/antichess
+ Variant* antichess_variant() {
+ Variant* v = giveaway_variant();
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1";
+ v->castling = false;
+ return v;
+ }
+ // Suicide chess
+ // Antichess with modified stalemate adjudication.
+ // https://www.freechess.org/Help/HelpFiles/suicide_chess.html
+ Variant* suicide_variant() {
+ Variant* v = antichess_variant();
+ v->stalematePieceCount = true;
+ return v;
+ }
+ // Codrus
+ // Lose the king to win. Captures are mandatory.
+ // http://www.binnewirtz.com/Schlagschach1.htm
+ Variant* codrus_variant() {
+ Variant* v = giveaway_variant();
+ v->promotionPieceTypes = {QUEEN, ROOK, BISHOP, KNIGHT};
+ v->extinctionPieceTypes = {COMMONER};
+ return v;
+ }
+ // Extinction chess
+ // https://en.wikipedia.org/wiki/Extinction_chess
+ Variant* extinction_variant() {
+ Variant* v = chess_variant_base();
+ v->remove_piece(KING);
+ v->add_piece(COMMONER, 'k');
+ v->castlingKingPiece = COMMONER;
+ v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT, PAWN};
+ return v;
+ }
+ // Kinglet
+ // https://en.wikipedia.org/wiki/V._R._Parton#Kinglet_chess
+ Variant* kinglet_variant() {
+ Variant* v = extinction_variant();
+ v->promotionPieceTypes = {COMMONER};
+ v->extinctionPieceTypes = {PAWN};
+ return v;
+ }
+ // Three Kings Chess
+ // https://github.com/cutechess/cutechess/blob/master/projects/lib/src/board/threekingsboard.h
+ Variant* threekings_variant() {
+ Variant* v = chess_variant_base();
+ v->remove_piece(KING);
+ v->add_piece(COMMONER, 'k');
+ v->castlingKingPiece = COMMONER;
+ v->startFen = "knbqkbnk/pppppppp/8/8/8/8/PPPPPPPP/KNBQKBNK w - - 0 1";
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionPieceTypes = {COMMONER};
+ v->extinctionPieceCount = 2;
+ return v;
+ }
+ // Horde chess
+ // https://en.wikipedia.org/wiki/Dunsany%27s_chess#Horde_chess
+ Variant* horde_variant() {
+ Variant* v = chess_variant_base();
+ v->startFen = "rnbqkbnr/pppppppp/8/1PP2PP1/PPPPPPPP/PPPPPPPP/PPPPPPPP/PPPPPPPP w kq - 0 1";
+ v->doubleStepRankMin = RANK_1;
+ v->enPassantRegion = Rank3BB | Rank6BB; // exclude en passant on second rank
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionPieceTypes = {ALL_PIECES};
+ return v;
+ }
+ // Atomic chess without checks (ICC rules)
+ // https://www.chessclub.com/help/atomic
+ Variant* nocheckatomic_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "atomic";
+ v->remove_piece(KING);
+ v->add_piece(COMMONER, 'k');
+ v->castlingKingPiece = COMMONER;
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionPieceTypes = {COMMONER};
+ v->blastOnCapture = true;
+ return v;
+ }
+ // Atomic chess
+ // https://en.wikipedia.org/wiki/Atomic_chess
+ Variant* atomic_variant() {
+ Variant* v = nocheckatomic_variant();
+ v->extinctionPseudoRoyal = true;
+ return v;
+ }
+ // Three-check chess
+ // Check the king three times to win
+ // https://lichess.org/variant/threeCheck
+ Variant* threecheck_variant() {
+ Variant* v = chess_variant_base();
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 3+3 0 1";
+ v->checkCounting = true;
+ return v;
+ }
+ // Five-check chess
+ // Check the king five times to win
+ Variant* fivecheck_variant() {
+ Variant* v = threecheck_variant();
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 5+5 0 1";
+ return v;
+ }
+ // Crazyhouse
+ // Chess with piece drops
+ // https://en.wikipedia.org/wiki/Crazyhouse
+ Variant* crazyhouse_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "crazyhouse";
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[] w KQkq - 0 1";
+ v->pieceDrops = true;
+ v->capturesToHand = true;
+ return v;
+ }
+ // Loop chess
+ // Variant of crazyhouse where promoted pawns are not demoted when captured
+ // https://en.wikipedia.org/wiki/Crazyhouse#Variations
+ Variant* loop_variant() {
+ Variant* v = crazyhouse_variant();
+ v->dropLoop = true;
+ return v;
+ }
+ // Chessgi
+ // Variant of loop chess where pawns can be dropped to the first rank
+ // https://en.wikipedia.org/wiki/Crazyhouse#Variations
+ Variant* chessgi_variant() {
+ Variant* v = loop_variant();
+ v->firstRankPawnDrops = true;
+ return v;
+ }
+ // Bughouse
+ // A four player variant where captured pieces are introduced on the other board
+ // https://en.wikipedia.org/wiki/Bughouse_chess
+ Variant* bughouse_variant() {
+ Variant* v = crazyhouse_variant();
+ v->variantTemplate = "bughouse";
+ v->twoBoards = true;
+ v->capturesToHand = false;
+ v->stalemateValue = -VALUE_MATE;
+ return v;
+ }
+ // Koedem (Bughouse variant)
+ // http://schachclub-oetigheim.de/wp-content/uploads/2016/04/Koedem-rules.pdf
+ Variant* koedem_variant() {
+ Variant* v = bughouse_variant();
+ v->remove_piece(KING);
+ v->add_piece(COMMONER, 'k');
+ v->castlingKingPiece = COMMONER;
+ v->mustDrop = true;
+ v->mustDropType = COMMONER;
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionPieceTypes = {COMMONER};
+ v->extinctionOpponentPieceCount = 2; // own all kings/commoners
+ return v;
+ }
+ // Pocket Knight chess
+ // Each player has an additional knight in hand which can be dropped at any move
+ // https://www.chessvariants.com/other.dir/pocket.html
+ Variant* pocketknight_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "bughouse";
+ v->pocketSize = 2;
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[Nn] w KQkq - 0 1";
+ v->pieceDrops = true;
+ v->capturesToHand = false;
+ return v;
+ }
+ // Placement/Pre-chess
+ // A shuffle variant where the players determine the placing of the back rank pieces
+ // https://www.chessvariants.com/link/placement-chess
+ Variant* placement_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "bughouse";
+ v->startFen = "8/pppppppp/8/8/8/8/PPPPPPPP/8[KQRRBBNNkqrrbbnn] w - - 0 1";
+ v->mustDrop = true;
+ v->pieceDrops = true;
+ v->capturesToHand = false;
+ v->whiteDropRegion = Rank1BB;
+ v->blackDropRegion = Rank8BB;
+ v->dropOppositeColoredBishop = true;
+ v->castlingDroppedPiece = true;
+ return v;
+ }
+ // Sittuyin (Burmese chess)
+ // Regional chess variant from Myanmar, similar to Makruk but with a setup phase.
+ // https://en.wikipedia.org/wiki/Sittuyin
+ Variant* sittuyin_variant() {
+ Variant* v = makruk_variant();
+ v->variantTemplate = "bughouse";
+ v->pieceToCharTable = "PN.R.F....SKpn.r.f....sk";
+ v->startFen = "8/8/4pppp/pppp4/4PPPP/PPPP4/8/8[KFRRSSNNkfrrssnn] w - - 0 1";
+ v->remove_piece(MET);
+ v->add_piece(MET, 'f');
+ v->mustDrop = true;
+ v->pieceDrops = true;
+ v->capturesToHand = false;
+ v->whiteDropRegion = Rank1BB | Rank2BB | Rank3BB;
+ v->blackDropRegion = Rank8BB | Rank7BB | Rank6BB;
+ v->sittuyinRookDrop = true;
+ v->promotionRank = RANK_1; // no regular promotions
+ v->sittuyinPromotion = true;
+ v->promotionLimit[FERS] = 1;
+ v->immobilityIllegal = false;
+ v->countingRule = ASEAN_COUNTING;
+ v->nMoveRule = 50;
+ return v;
+ }
+ // S-Chess (aka Seirawan-, or SHarper chess)
+ // 8x8 variant introducing the knighted pieces from capablanca chess
+ // via gating when a piece first moves from its initial square.
+ Variant* seirawan_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "seirawan";
+ v->pieceToCharTable = "PNBRQ.E..........H...Kpnbrq.e..........h...k";
+ v->add_piece(ARCHBISHOP, 'h');
+ v->add_piece(CHANCELLOR, 'e');
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[HEhe] w KQBCDFGkqbcdfg - 0 1";
+ v->gating = true;
+ v->seirawanGating = true;
+ v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // S-House
+ // A hybrid variant of S-Chess and Crazyhouse.
+ // Pieces in the pocket can either be gated or dropped.
+ Variant* shouse_variant() {
+ Variant* v = seirawan_variant();
+ v->variantTemplate = "crazyhouse";
+ v->pieceDrops = true;
+ v->capturesToHand = true;
+ return v;
+ }
+ // Base used for most shogi variants
+ Variant* minishogi_variant_base() {
+ Variant* v = variant_base();
+ v->variantTemplate = "shogi";
+ v->maxRank = RANK_5;
+ v->maxFile = FILE_E;
+ v->reset_pieces();
+ v->add_piece(SHOGI_PAWN, 'p');
+ v->add_piece(SILVER, 's');
+ v->add_piece(GOLD, 'g');
+ v->add_piece(BISHOP, 'b');
+ v->add_piece(DRAGON_HORSE, 'h');
+ v->add_piece(ROOK, 'r');
+ v->add_piece(DRAGON, 'd');
+ v->add_piece(KING, 'k');
+ v->startFen = "rbsgk/4p/5/P4/KGSBR[-] w 0 1";
+ v->pieceDrops = true;
+ v->capturesToHand = true;
+ v->promotionRank = RANK_5;
+ v->promotionPieceTypes = {};
+ v->doubleStep = false;
+ v->castling = false;
+ v->promotedPieceType[SHOGI_PAWN] = GOLD;
+ v->promotedPieceType[SILVER] = GOLD;
+ v->promotedPieceType[BISHOP] = DRAGON_HORSE;
+ v->promotedPieceType[ROOK] = DRAGON;
+ v->dropNoDoubled = SHOGI_PAWN;
+ v->immobilityIllegal = true;
+ v->shogiPawnDropMateIllegal = true;
+ v->stalemateValue = -VALUE_MATE;
+ v->nFoldRule = 4;
+ v->nMoveRule = 0;
+ v->perpetualCheckIllegal = true;
+ return v;
+ }
+ // Minishogi
+ // 5x5 variant of shogi
+ // https://en.wikipedia.org/wiki/Minishogi
+ Variant* minishogi_variant() {
+ Variant* v = minishogi_variant_base();
+ v->pieceToCharTable = "P.BR.S...G.+.++.+Kp.br.s...g.+.++.+k";
+ v->pocketSize = 5;
+ v->nFoldValue = -VALUE_MATE;
+ v->nFoldValueAbsolute = true;
+ return v;
+ }
+ // Kyoto shogi
+ // 5x5 variant of shogi with pieces alternating between promotion and demotion
+ // https://en.wikipedia.org/wiki/Kyoto_shogi
+ Variant* kyotoshogi_variant() {
+ Variant* v = minishogi_variant_base();
+ v->add_piece(LANCE, 'l');
+ v->add_piece(SHOGI_KNIGHT, 'n');
+ v->startFen = "p+nks+l/5/5/5/+LSK+NP[-] w 0 1";
+ v->promotionRank = RANK_1;
+ v->mandatoryPiecePromotion = true;
+ v->pieceDemotion = true;
+ v->dropPromoted = true;
+ v->promotedPieceType[LANCE] = GOLD;
+ v->promotedPieceType[SILVER] = BISHOP;
+ v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
+ v->promotedPieceType[SHOGI_PAWN] = ROOK;
+ v->promotedPieceType[GOLD] = NO_PIECE_TYPE;
+ v->promotedPieceType[BISHOP] = NO_PIECE_TYPE;
+ v->promotedPieceType[ROOK] = NO_PIECE_TYPE;
+ v->immobilityIllegal = false;
+ v->shogiPawnDropMateIllegal = false;
+ v->dropNoDoubled = NO_PIECE_TYPE;
+ return v;
+ }
+ // Micro shogi
+ // 4x5 shogi variant where pieces promoted and demote when capturing
+ // https://en.wikipedia.org/wiki/Micro_shogi
+ Variant* microshogi_variant() {
+ Variant* v = kyotoshogi_variant();
+ v->maxFile = FILE_D;
+ v->startFen = "kb+r+l/p3/4/3P/+L+RBK[-] w 0 1";
+ v->promotionRank = RANK_1;
+ v->piecePromotionOnCapture = true;
+ v->promotedPieceType[LANCE] = SILVER;
+ v->promotedPieceType[BISHOP] = GOLD;
+ v->promotedPieceType[ROOK] = GOLD;
+ v->promotedPieceType[SHOGI_PAWN] = SHOGI_KNIGHT;
+ v->promotedPieceType[SILVER] = NO_PIECE_TYPE;
+ v->promotedPieceType[GOLD] = NO_PIECE_TYPE;
+ v->promotedPieceType[SHOGI_KNIGHT] = NO_PIECE_TYPE;
+ return v;
+ }
+ // Dobutsu
+ // Educational shogi variant on a 3x4 board
+ // https://en.wikipedia.org/wiki/D%C5%8Dbutsu_sh%C5%8Dgi
+ Variant* dobutsu_variant() {
+ Variant* v = minishogi_variant_base();
+ v->pieceToCharTable = "C....E...G.+.....Lc....e...g.+.....l";
+ v->pocketSize = 3;
+ v->maxRank = RANK_4;
+ v->maxFile = FILE_C;
+ v->reset_pieces();
+ v->add_piece(SHOGI_PAWN, 'c');
+ v->add_piece(GOLD, 'h');
+ v->add_piece(FERS, 'e');
+ v->add_piece(WAZIR, 'g');
+ v->add_piece(KING, 'l');
+ v->startFen = "gle/1c1/1C1/ELG[-] w 0 1";
+ v->promotionRank = RANK_4;
+ v->immobilityIllegal = false;
+ v->shogiPawnDropMateIllegal = false;
+ v->flagPiece = KING;
+ v->whiteFlag = Rank4BB;
+ v->blackFlag = Rank1BB;
+ v->dropNoDoubled = NO_PIECE_TYPE;
+ return v;
+ }
+ // Goro goro shogi
+ // https://en.wikipedia.org/wiki/D%C5%8Dbutsu_sh%C5%8Dgi#Variation
+ Variant* gorogoroshogi_variant() {
+ Variant* v = minishogi_variant_base();
+ v->pieceToCharTable = "P....S...G.+....+Kp....s...g.+....+k";
+ v->pocketSize = 3;
+ v->maxRank = RANK_6;
+ v->maxFile = FILE_E;
+ v->startFen = "sgkgs/5/1ppp1/1PPP1/5/SGKGS[-] w 0 1";
+ v->promotionRank = RANK_5;
+ return v;
+ }
+ // Judkins shogi
+ // https://en.wikipedia.org/wiki/Judkins_shogi
+ Variant* judkinsshogi_variant() {
+ Variant* v = minishogi_variant_base();
+ v->pieceToCharTable = "PNBR.S...G.++++.+Kpnbr.s...g.++++.+k";
+ v->maxRank = RANK_6;
+ v->maxFile = FILE_F;
+ v->add_piece(SHOGI_KNIGHT, 'n');
+ v->startFen = "rbnsgk/5p/6/6/P5/KGSNBR[-] w 0 1";
+ v->promotionRank = RANK_5;
+ v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
+ return v;
+ }
+ // Tori shogi
+ // https://en.wikipedia.org/wiki/Tori_shogi
+ Variant* torishogi_variant() {
+ Variant* v = variant_base();
+ v->variantTemplate = "shogi";
+ v->pieceToCharTable = "S.....FLR.C+.....+.PKs.....flr.c+.....+.pk";
+ v->maxRank = RANK_7;
+ v->maxFile = FILE_G;
+ v->reset_pieces();
+ v->add_piece(SHOGI_PAWN, 's');
+ v->add_piece(KING, 'k');
+ v->add_piece(CUSTOM_PIECES, 'f', "FsfW"); // falcon
+ v->add_piece(CUSTOM_PIECES + 1, 'c', "FvW"); // crane
+ v->add_piece(CUSTOM_PIECES + 2, 'l', "fRrbBlbF"); // left quail
+ v->add_piece(CUSTOM_PIECES + 3, 'r', "fRlbBrbF"); // right quail
+ v->add_piece(CUSTOM_PIECES + 4, 'p', "bFfD"); // pheasant
+ v->add_piece(CUSTOM_PIECES + 5, 'g', "fAbD"); // goose
+ v->add_piece(CUSTOM_PIECES + 6, 'e', "KbRfBbF2"); // eagle
+ v->startFen = "rpckcpl/3f3/sssssss/2s1S2/SSSSSSS/3F3/LPCKCPR[-] w 0 1";
+ v->pieceDrops = true;
+ v->capturesToHand = true;
+ v->promotionRank = RANK_6;
+ v->promotionPieceTypes = {};
+ v->doubleStep = false;
+ v->castling = false;
+ v->promotedPieceType[SHOGI_PAWN] = CUSTOM_PIECES + 5; // swallow promotes to goose
+ v->promotedPieceType[CUSTOM_PIECES] = CUSTOM_PIECES + 6; // falcon promotes to eagle
+ v->mandatoryPiecePromotion = true;
+ v->dropNoDoubled = SHOGI_PAWN;
+ v->dropNoDoubledCount = 2;
+ v->immobilityIllegal = true;
+ v->shogiPawnDropMateIllegal = true;
+ v->stalemateValue = -VALUE_MATE;
+ v->nFoldValue = VALUE_MATE;
+ v->nFoldRule = 3;
+ v->nMoveRule = 0;
+ v->perpetualCheckIllegal = true;
+ return v;
+ }
+ // EuroShogi
+ // https://en.wikipedia.org/wiki/EuroShogi
+ Variant* euroshogi_variant() {
+ Variant* v = minishogi_variant_base();
+ v->pieceToCharTable = "PNBR.....G.++++Kpnbr.....g.++++k";
+ v->maxRank = RANK_8;
+ v->maxFile = FILE_H;
+ v->add_piece(CUSTOM_PIECES, 'n', "fNsW");
+ v->startFen = "1nbgkgn1/1r4b1/pppppppp/8/8/PPPPPPPP/1B4R1/1NGKGBN1[-] w 0 1";
+ v->promotionRank = RANK_6;
+ v->promotedPieceType[CUSTOM_PIECES] = GOLD;
+ v->mandatoryPiecePromotion = true;
+ return v;
+ }
+ // Los Alamos chess
+ // https://en.wikipedia.org/wiki/Los_Alamos_chess
+ Variant* losalamos_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PN.RQ................Kpn.rq................k";
+ v->maxRank = RANK_6;
+ v->maxFile = FILE_F;
+ v->remove_piece(BISHOP);
+ v->startFen = "rnqknr/pppppp/6/6/PPPPPP/RNQKNR w - - 0 1";
+ v->promotionRank = RANK_6;
+ v->promotionPieceTypes = {QUEEN, ROOK, KNIGHT};
+ v->doubleStep = false;
+ v->castling = false;
+ return v;
+ }
+ // Gardner's minichess
+ // https://en.wikipedia.org/wiki/Minichess#5%C3%975_chess
+ Variant* gardner_variant() {
+ Variant* v = chess_variant_base();
+ v->maxRank = RANK_5;
+ v->maxFile = FILE_E;
+ v->startFen = "rnbqk/ppppp/5/PPPPP/RNBQK w - - 0 1";
+ v->promotionRank = RANK_5;
+ v->doubleStep = false;
+ v->castling = false;
+ return v;
+ }
+ // Almost chess
+ // Queens are replaced by chancellors
+ // https://en.wikipedia.org/wiki/Almost_chess
+ Variant* almost_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBR............CKpnbr............ck";
+ v->remove_piece(QUEEN);
+ v->add_piece(CHANCELLOR, 'c');
+ v->startFen = "rnbckbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBCKBNR w KQkq - 0 1";
+ v->promotionPieceTypes = {CHANCELLOR, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Chigorin chess
+ // Asymmetric variant with knight vs. bishop movements
+ // https://www.chessvariants.com/diffsetup.dir/chigorin.html
+ Variant* chigorin_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBR............CKpnbrq............k";
+ v->add_piece(CHANCELLOR, 'c');
+ v->startFen = "rbbqkbbr/pppppppp/8/8/8/8/PPPPPPPP/RNNCKNNR w KQkq - 0 1";
+ v->promotionPieceTypes = {QUEEN, CHANCELLOR, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Shatar (Mongolian chess)
+ // https://en.wikipedia.org/wiki/Shatar
+ Variant* shatar_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBR..........J......Kpnbr..........j......k";
+ v->remove_piece(QUEEN);
+ v->add_piece(BERS, 'j');
+ v->startFen = "rnbjkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBJKBNR w - - 0 1";
+ v->promotionPieceTypes = {BERS};
+ v->doubleStep = false;
+ v->castling = false;
+ v->extinctionValue = VALUE_DRAW; // Robado
+ v->extinctionPieceTypes = {ALL_PIECES};
+ v->extinctionPieceCount = 1;
+ v->shatarMateRule = true;
+ return v;
+ }
+ // Coregal chess
+ // Queens are also subject to check and checkmate
+ // https://www.chessvariants.com/winning.dir/coregal.html
+ Variant* coregal_variant() {
+ Variant* v = chess_variant_base();
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionPieceTypes = {QUEEN};
+ v->extinctionPseudoRoyal = true;
+ v->extinctionPieceCount = 64; // no matter how many queens, all are royal
+ return v;
+ }
+ // Clobber
+ // https://en.wikipedia.org/wiki/Clobber
+ Variant* clobber_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "P.................p.................";
+ v->maxRank = RANK_6;
+ v->maxFile = FILE_E;
+ v->reset_pieces();
+ v->add_piece(CLOBBER_PIECE, 'p');
+ v->startFen = "PpPpP/pPpPp/PpPpP/pPpPp/PpPpP/pPpPp w 0 1";
+ v->promotionPieceTypes = {};
+ v->doubleStep = false;
+ v->castling = false;
+ v->stalemateValue = -VALUE_MATE;
+ v->immobilityIllegal = false;
+ return v;
+ }
+ // Breakthrough
+ // https://en.wikipedia.org/wiki/Breakthrough_(board_game)
+ Variant* breakthrough_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "P.................p.................";
+ v->reset_pieces();
+ v->add_piece(BREAKTHROUGH_PIECE, 'p');
+ v->startFen = "pppppppp/pppppppp/8/8/8/8/PPPPPPPP/PPPPPPPP w 0 1";
+ v->promotionPieceTypes = {};
+ v->doubleStep = false;
+ v->castling = false;
+ v->stalemateValue = -VALUE_MATE;
+ v->flagPiece = BREAKTHROUGH_PIECE;
+ v->whiteFlag = Rank8BB;
+ v->blackFlag = Rank1BB;
+ return v;
+ }
+ // Ataxx
+ // https://en.wikipedia.org/wiki/Ataxx
+ Variant* ataxx_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "P.................p.................";
+ v->maxRank = RANK_7;
+ v->maxFile = FILE_G;
+ v->reset_pieces();
+ v->add_piece(CUSTOM_PIECES, 'p', "mDmNmA");
+ v->startFen = "P5p/7/7/7/7/7/p5P[PPPPPPPPPPPPPPPPPPPPPPPPPppppppppppppppppppppppppp] w 0 1";
+ v->promotionPieceTypes = {};
+ v->pieceDrops = true;
+ v->doubleStep = false;
+ v->castling = false;
+ v->immobilityIllegal = false;
+ v->stalemateValue = -VALUE_MATE;
+ v->stalematePieceCount = true;
+ v->passOnStalemate = true;
+ v->enclosingDrop = ATAXX;
+ v->flipEnclosedPieces = ATAXX;
+ v->materialCounting = UNWEIGHTED_MATERIAL;
+ return v;
+ }
+ // Minixiangqi
+ // http://mlwi.magix.net/bg/minixiangqi.htm
+ Variant* minixiangqi_variant() {
+ Variant* v = chess_variant_base();
+ v->variantTemplate = "xiangqi";
+ v->pieceToCharTable = "PN.R.....K.C.pn.r.....k.c.";
+ v->maxRank = RANK_7;
+ v->maxFile = FILE_G;
+ v->reset_pieces();
+ v->add_piece(ROOK, 'r');
+ v->add_piece(HORSE, 'n', 'h');
+ v->add_piece(KING, 'k');
+ v->add_piece(CANNON, 'c');
+ v->add_piece(SOLDIER, 'p');
+ v->startFen = "rcnkncr/p1ppp1p/7/7/7/P1PPP1P/RCNKNCR w - - 0 1";
+ v->mobilityRegion[WHITE][KING] = (Rank1BB | Rank2BB | Rank3BB) & (FileCBB | FileDBB | FileEBB);
+ v->mobilityRegion[BLACK][KING] = (Rank5BB | Rank6BB | Rank7BB) & (FileCBB | FileDBB | FileEBB);
+ v->kingType = WAZIR;
+ v->promotionPieceTypes = {};
+ v->doubleStep = false;
+ v->castling = false;
+ v->stalemateValue = -VALUE_MATE;
+ //v->nFoldValue = VALUE_MATE;
+ v->perpetualCheckIllegal = true;
+ v->flyingGeneral = true;
+ return v;
+ }
+#ifdef LARGEBOARDS
+ // Shogi (Japanese chess)
+ // https://en.wikipedia.org/wiki/Shogi
+ Variant* shogi_variant() {
+ Variant* v = minishogi_variant_base();
+ v->maxRank = RANK_9;
+ v->maxFile = FILE_I;
+ v->add_piece(LANCE, 'l');
+ v->add_piece(SHOGI_KNIGHT, 'n');
+ v->startFen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL[-] w 0 1";
+ v->promotionRank = RANK_7;
+ v->promotedPieceType[LANCE] = GOLD;
+ v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
+ 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->add_piece(CUSTOM_PIECES, 'n', "fRffN"); // Yari knight
+ v->add_piece(CUSTOM_PIECES + 1, 'b', "fFfR"); // Yari bishop
+ v->add_piece(CUSTOM_PIECES + 2, 'r', "frlR"); // Yari rook
+ v->add_piece(CUSTOM_PIECES + 3, 'g', "WfFbR"); // Yari gold
+ v->add_piece(CUSTOM_PIECES + 4, 's', "fKbR"); // Yari silver
+ 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->add_piece(CUSTOM_PIECES, 'l', "vR"); // Vertical slider
+ 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";
+ v->maxRank = RANK_8;
+ v->maxFile = FILE_J;
+ v->castlingKingsideFile = FILE_I;
+ v->castlingQueensideFile = FILE_C;
+ v->add_piece(ARCHBISHOP, 'a');
+ v->add_piece(CHANCELLOR, 'c');
+ v->startFen = "rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR w KQkq - 0 1";
+ v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Capahouse
+ // Capablanca chess with crazyhouse-style piece drops
+ // https://www.pychess.org/variant/capahouse
+ Variant* capahouse_variant() {
+ Variant* v = capablanca_variant();
+ v->startFen = "rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR[] w KQkq - 0 1";
+ v->pieceDrops = true;
+ v->capturesToHand = true;
+ return v;
+ }
+ // Capablanca random chess (CRC)
+ // Shuffle variant of capablanca chess
+ // https://en.wikipedia.org/wiki/Capablanca_random_chess
+ Variant* caparandom_variant() {
+ Variant* v = capablanca_variant();
+ v->chess960 = true;
+ return v;
+ }
+ // Gothic chess
+ // Capablanca chess with changed starting position
+ // https://www.chessvariants.com/large.dir/gothicchess.html
+ Variant* gothic_variant() {
+ Variant* v = capablanca_variant();
+ v->startFen = "rnbqckabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBQCKABNR w KQkq - 0 1";
+ return v;
+ }
+ // Janus chess
+ // 10x8 variant with two archbishops per side
+ // https://en.wikipedia.org/wiki/Janus_Chess
+ Variant* janus_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBRQ............J...Kpnbrq............j...k";
+ v->maxRank = RANK_8;
+ v->maxFile = FILE_J;
+ v->castlingKingsideFile = FILE_I;
+ v->castlingQueensideFile = FILE_B;
+ v->add_piece(ARCHBISHOP, 'j');
+ v->startFen = "rjnbkqbnjr/pppppppppp/10/10/10/10/PPPPPPPPPP/RJNBKQBNJR w KQkq - 0 1";
+ v->promotionPieceTypes = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Modern chess
+ // 9x9 variant with archbishops
+ // https://en.wikipedia.org/wiki/Modern_chess
+ Variant* modern_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBRQ..M.............Kpnbrq..m.............k";
+ v->maxRank = RANK_9;
+ v->maxFile = FILE_I;
+ v->promotionRank = RANK_9;
+ v->castlingKingsideFile = FILE_G;
+ v->castlingQueensideFile = FILE_C;
+ v->add_piece(ARCHBISHOP, 'm');
+ v->startFen = "rnbqkmbnr/ppppppppp/9/9/9/9/9/PPPPPPPPP/RNBMKQBNR w KQkq - 0 1";
+ v->promotionPieceTypes = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Chancellor chess
+ // 9x9 variant with chancellors
+ // https://en.wikipedia.org/wiki/Chancellor_chess
+ Variant* chancellor_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBRQ...........CKpnbrq...........ck";
+ v->maxRank = RANK_9;
+ v->maxFile = FILE_I;
+ v->promotionRank = RANK_9;
+ v->castlingKingsideFile = FILE_G;
+ v->castlingQueensideFile = FILE_C;
+ v->add_piece(CHANCELLOR, 'c');
+ v->startFen = "rnbqkcnbr/ppppppppp/9/9/9/9/9/PPPPPPPPP/RNBQKCNBR w KQkq - 0 1";
+ v->promotionPieceTypes = {CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Embassy chess
+ // Capablanca chess with different starting position
+ // https://en.wikipedia.org/wiki/Embassy_chess
+ Variant* embassy_variant() {
+ Variant* v = capablanca_variant();
+ v->castlingKingsideFile = FILE_H;
+ v->castlingQueensideFile = FILE_B;
+ v->startFen = "rnbqkcabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBQKCABNR w KQkq - 0 1";
+ return v;
+ }
+ // Centaur chess (aka Royal Court)
+ // 10x8 variant with a knight+commoner compound
+ // https://www.chessvariants.com/large.dir/contest/royalcourt.html
+ Variant* centaur_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBRQ...............CKpnbrq...............ck";
+ v->maxRank = RANK_8;
+ v->maxFile = FILE_J;
+ v->castlingKingsideFile = FILE_I;
+ v->castlingQueensideFile = FILE_C;
+ v->add_piece(CENTAUR, 'c');
+ v->startFen = "rcnbqkbncr/pppppppppp/10/10/10/10/PPPPPPPPPP/RCNBQKBNCR w KQkq - 0 1";
+ v->promotionPieceTypes = {CENTAUR, QUEEN, ROOK, BISHOP, KNIGHT};
+ return v;
+ }
+ // Jeson mor
+ // Mongolian chess variant with knights only and a king of the hill like goal
+ // https://en.wikipedia.org/wiki/Jeson_Mor
+ Variant* jesonmor_variant() {
+ Variant* v = chess_variant_base();
+ v->maxRank = RANK_9;
+ v->maxFile = FILE_I;
+ v->reset_pieces();
+ v->add_piece(KNIGHT, 'n');
+ v->startFen = "nnnnnnnnn/9/9/9/9/9/9/9/NNNNNNNNN w - - 0 1";
+ v->promotionPieceTypes = {};
+ v->doubleStep = false;
+ v->castling = false;
+ v->stalemateValue = -VALUE_MATE;
+ v->flagPiece = KNIGHT;
+ v->whiteFlag = make_bitboard(SQ_E5);
+ v->blackFlag = make_bitboard(SQ_E5);
+ v->flagMove = true;
+ return v;
+ }
+ // Courier chess
+ // Medieval variant of Shatranj on a 12x8 board
+ // https://en.wikipedia.org/wiki/Courier_chess
+ Variant* courier_variant() {
+ Variant* v = chess_variant_base();
+ v->maxRank = RANK_8;
+ v->maxFile = FILE_L;
+ v->remove_piece(QUEEN);
+ v->add_piece(ALFIL, 'e');
+ v->add_piece(FERS, 'f');
+ v->add_piece(COMMONER, 'm');
+ v->add_piece(WAZIR, 'w');
+ v->startFen = "rnebmk1wbenr/1ppppp1pppp1/6f5/p5p4p/P5P4P/6F5/1PPPPP1PPPP1/RNEBMK1WBENR w - - 0 1";
+ v->promotionPieceTypes = {FERS};
+ v->doubleStep = false;
+ v->castling = false;
+ v->extinctionValue = -VALUE_MATE;
+ v->extinctionClaim = true;
+ v->extinctionPieceTypes = {ALL_PIECES};
+ v->extinctionPieceCount = 1;
+ v->extinctionOpponentPieceCount = 2;
+ v->stalemateValue = -VALUE_MATE;
+ return v;
+ }
+ // Grand chess
+ // 10x10 variant with chancellors and archbishops
+ // https://en.wikipedia.org/wiki/Grand_chess
+ Variant* grand_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBRQ..AC............Kpnbrq..ac............k";
+ v->maxRank = RANK_10;
+ v->maxFile = FILE_J;
+ v->add_piece(ARCHBISHOP, 'a');
+ v->add_piece(CHANCELLOR, 'c');
+ v->startFen = "r8r/1nbqkcabn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKCABN1/R8R w - - 0 1";
+ v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionRank = RANK_8;
+ v->promotionLimit[ARCHBISHOP] = 1;
+ v->promotionLimit[CHANCELLOR] = 1;
+ v->promotionLimit[QUEEN] = 1;
+ v->promotionLimit[ROOK] = 2;
+ v->promotionLimit[BISHOP] = 2;
+ v->promotionLimit[KNIGHT] = 2;
+ v->mandatoryPawnPromotion = false;
+ v->immobilityIllegal = true;
+ v->doubleStepRank = RANK_3;
+ v->doubleStepRankMin = RANK_3;
+ 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->add_piece(CUSTOM_PIECES, 'c', "DAW"); // Champion
+ v->add_piece(CUSTOM_PIECES + 1, 'w', "CF"); // Wizard
+ v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN};
+ v->promotionRank = RANK_10;
+ v->doubleStepRank = RANK_3;
+ v->doubleStepRankMin = RANK_3;
+ v->castling = false;
+ return v;
+ }
+ // Shako
+ // 10x10 variant with cannons by Jean-Louis Cazaux
+ // https://www.chessvariants.com/large.dir/shako.html
+ Variant* shako_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "PNBRQ.E....C.........Kpnbrq.e....c.........k";
+ v->maxRank = RANK_10;
+ v->maxFile = FILE_J;
+ v->add_piece(FERS_ALFIL, 'e');
+ v->add_piece(CANNON, 'c');
+ v->startFen = "c8c/ernbqkbnre/pppppppppp/10/10/10/10/PPPPPPPPPP/ERNBQKBNRE/C8C w KQkq - 0 1";
+ v->promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT, CANNON, FERS_ALFIL };
+ v->promotionRank = RANK_10;
+ v->castlingKingsideFile = FILE_H;
+ v->castlingQueensideFile = FILE_D;
+ v->castlingRank = RANK_2;
+ v->doubleStepRank = RANK_3;
+ v->doubleStepRankMin = RANK_3;
+ return v;
+ }
+ // Clobber 10x10
+ // Clobber on a 10x10, mainly played by computers
+ // https://en.wikipedia.org/wiki/Clobber
+ Variant* clobber10_variant() {
+ Variant* v = clobber_variant();
+ v->maxRank = RANK_10;
+ v->maxFile = FILE_J;
+ v->startFen = "PpPpPpPpPp/pPpPpPpPpP/PpPpPpPpPp/pPpPpPpPpP/PpPpPpPpPp/"
+ "pPpPpPpPpP/PpPpPpPpPp/pPpPpPpPpP/PpPpPpPpPp/pPpPpPpPpP w 0 1";
+ return v;
+ }
+#ifdef ALLVARS
+ // Game of the Amazons
+ // https://en.wikipedia.org/wiki/Game_of_the_Amazons
+ Variant* amazons_variant() {
+ Variant* v = chess_variant_base();
+ v->pieceToCharTable = "P...Q.................p...q.................";
+ v->maxRank = RANK_10;
+ v->maxFile = FILE_J;
+ v->reset_pieces();
+ v->add_piece(CUSTOM_PIECES, 'q', "mQ");
+ v->add_piece(IMMOBILE_PIECE, 'p');
+ v->startFen = "3q2q3/10/10/q8q/10/10/Q8Q/10/10/3Q2Q3[PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPpppppppppppppppppppppppppppppppppppppppppppppp] w - - 0 1";
+ v->stalemateValue = -VALUE_MATE;
+ v->arrowGating = true;
+ return v;
+ }
+#endif
+ // Xiangqi (Chinese chess)
+ // 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..........";
+ v->maxRank = RANK_10;
+ v->maxFile = FILE_I;
+ v->add_piece(ELEPHANT, 'b', 'e');
+ v->add_piece(FERS, 'a');
+ v->startFen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1";
+ v->mobilityRegion[WHITE][KING] = (Rank1BB | Rank2BB | Rank3BB) & (FileDBB | FileEBB | FileFBB);
+ v->mobilityRegion[BLACK][KING] = (Rank8BB | Rank9BB | Rank10BB) & (FileDBB | FileEBB | FileFBB);
+ v->mobilityRegion[WHITE][FERS] = v->mobilityRegion[WHITE][KING];
+ v->mobilityRegion[BLACK][FERS] = v->mobilityRegion[BLACK][KING];
+ v->mobilityRegion[WHITE][ELEPHANT] = Rank1BB | Rank2BB | Rank3BB | Rank4BB | Rank5BB;
+ v->mobilityRegion[BLACK][ELEPHANT] = Rank6BB | Rank7BB | Rank8BB | Rank9BB | Rank10BB;
+ v->soldierPromotionRank = RANK_6;
+ return v;
+ }
+ // Manchu/Yitong chess
+ // Asymmetric Xiangqi variant with a super-piece
+ // https://en.wikipedia.org/wiki/Manchu_chess
+ Variant* manchu_variant() {
+ Variant* v = xiangqi_variant();
+ v->pieceToCharTable = "PN.R.AB..K.C....M.....pn.r.ab..k.c..........";
+ v->add_piece(BANNER, 'm');
+ v->startFen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/9/9/M1BAKAB2 w - - 0 1";
+ return v;
+ }
+ // Supply chess
+ // https://en.wikipedia.org/wiki/Xiangqi#Variations
+ Variant* supply_variant() {
+ Variant* v = xiangqi_variant();
+ v->variantTemplate = "bughouse";
+ v->startFen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR[] w - - 0 1";
+ v->twoBoards = true;
+ v->pieceDrops = true;
+ v->dropChecks = false;
+ v->whiteDropRegion = v->mobilityRegion[WHITE][ELEPHANT];
+ v->blackDropRegion = v->mobilityRegion[BLACK][ELEPHANT];
+ return v;
+ }
+ // Janggi (Korean chess)
+ // https://en.wikipedia.org/wiki/Janggi
+ // Official tournament rules with bikjang and material counting.
+ Variant* janggi_variant() {
+ Variant* v = xiangqi_variant();
+ v->variantTemplate = "janggi";
+ v->pieceToCharTable = ".N.R.AB.P..C.........K.n.r.ab.p..c.........k";
+ v->remove_piece(FERS);
+ v->remove_piece(CANNON);
+ v->remove_piece(ELEPHANT);
+ v->add_piece(WAZIR, 'a');
+ v->add_piece(JANGGI_CANNON, 'c');
+ v->add_piece(JANGGI_ELEPHANT, 'b', 'e');
+ v->startFen = "rnba1abnr/4k4/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/4K4/RNBA1ABNR w - - 0 1";
+ v->mobilityRegion[WHITE][WAZIR] = v->mobilityRegion[WHITE][KING];
+ v->mobilityRegion[BLACK][WAZIR] = v->mobilityRegion[BLACK][KING];
+ v->soldierPromotionRank = RANK_1;
+ v->flyingGeneral = false;
+ v->bikjangRule = true;
+ v->materialCounting = JANGGI_MATERIAL;
+ v->diagonalLines = make_bitboard(SQ_D1, SQ_F1, SQ_E2, SQ_D3, SQ_F3,
+ SQ_D8, SQ_F8, SQ_E9, SQ_D10, SQ_F10);
+ v->pass = true;
+ v->nFoldValue = VALUE_DRAW;
+ v->perpetualCheckIllegal = true;
+ return v;
+ }
+ // Traditional rules of Janggi, where bikjang is a draw
+ Variant* janggi_traditional_variant() {
+ Variant* v = janggi_variant();
+ v->bikjangRule = true;
+ v->materialCounting = NO_MATERIAL_COUNTING;
+ return v;
+ }
+ // Modern rules of Janggi, where bikjang is not considered, but material counting is.
+ // The repetition rules are also adjusted for better compatibility with Kakao Janggi.
+ Variant* janggi_modern_variant() {
+ Variant* v = janggi_variant();
+ v->bikjangRule = false;
+ v->materialCounting = JANGGI_MATERIAL;
+ v->moveRepetitionIllegal = true;
+ v->nFoldRule = 4; // avoid nFold being triggered before move repetition
+ v->nMoveRule = 100; // avoid adjudication before reaching 200 half-moves
+ return v;
+ }
+ // Casual rules of Janggi, where bikjang and material counting are not considered
+ Variant* janggi_casual_variant() {
+ Variant* v = janggi_variant();
+ v->bikjangRule = false;
+ v->materialCounting = NO_MATERIAL_COUNTING;
+ return v;
+ }
+#endif
+
+} // namespace
+
+
+/// VariantMap::init() is called at startup to initialize all predefined variants
+
+void VariantMap::init() {
+ // Add to UCI_Variant option
+ add("chess", chess_variant()->conclude());
+ add("normal", chess_variant()->conclude());
+ add("fischerandom", chess960_variant()->conclude());
+ add("nocastle", nocastle_variant()->conclude());
+ add("armageddon", armageddon_variant()->conclude());
+ add("fairy", fairy_variant()->conclude()); // fairy variant used for endgame code initialization
+ add("makruk", makruk_variant()->conclude());
+ add("makpong", makpong_variant()->conclude());
+ add("cambodian", cambodian_variant()->conclude());
+ add("karouk", karouk_variant()->conclude());
+ add("asean", asean_variant()->conclude());
+ add("ai-wok", aiwok_variant()->conclude());
+ add("shatranj", shatranj_variant()->conclude());
+ add("chaturanga", chaturanga_variant()->conclude());
+ add("amazon", amazon_variant()->conclude());
+ add("nightrider", nightrider_variant()->conclude());
+ add("grasshopper", grasshopper_variant()->conclude());
+ add("hoppelpoppel", hoppelpoppel_variant()->conclude());
+ add("newzealand", newzealand_variant()->conclude());
+ add("kingofthehill", kingofthehill_variant()->conclude());
+ add("racingkings", racingkings_variant()->conclude());
+ add("knightmate", knightmate_variant()->conclude());
+ add("losers", losers_variant()->conclude());
+ add("giveaway", giveaway_variant()->conclude());
+ add("antichess", antichess_variant()->conclude());
+ add("suicide", suicide_variant()->conclude());
+ add("codrus", codrus_variant()->conclude());
+ add("extinction", extinction_variant()->conclude());
+ add("kinglet", kinglet_variant()->conclude());
+ add("threekings", threekings_variant()->conclude());
+ add("horde", horde_variant()->conclude());
+ add("nocheckatomic", nocheckatomic_variant()->conclude());
+ add("atomic", atomic_variant()->conclude());
+ add("3check", threecheck_variant()->conclude());
+ add("5check", fivecheck_variant()->conclude());
+ add("crazyhouse", crazyhouse_variant()->conclude());
+ add("loop", loop_variant()->conclude());
+ add("chessgi", chessgi_variant()->conclude());
+ add("bughouse", bughouse_variant()->conclude());
+ add("koedem", koedem_variant()->conclude());
+ add("pocketknight", pocketknight_variant()->conclude());
+ add("placement", placement_variant()->conclude());
+ add("sittuyin", sittuyin_variant()->conclude());
+ add("seirawan", seirawan_variant()->conclude());
+ add("shouse", shouse_variant()->conclude());
+ add("minishogi", minishogi_variant()->conclude());
+ add("mini", minishogi_variant()->conclude());
+ add("kyotoshogi", kyotoshogi_variant()->conclude());
+ add("micro", microshogi_variant()->conclude());
+ add("dobutsu", dobutsu_variant()->conclude());
+ add("gorogoro", gorogoroshogi_variant()->conclude());
+ add("judkins", judkinsshogi_variant()->conclude());
+ add("torishogi", torishogi_variant()->conclude());
+ add("euroshogi", euroshogi_variant()->conclude());
+ add("losalamos", losalamos_variant()->conclude());
+ add("gardner", gardner_variant()->conclude());
+ add("almost", almost_variant()->conclude());
+ add("chigorin", chigorin_variant()->conclude());
+ add("shatar", shatar_variant()->conclude());
+ add("coregal", coregal_variant()->conclude());
+ add("clobber", clobber_variant()->conclude());
+ add("breakthrough", breakthrough_variant()->conclude());
+ add("ataxx", ataxx_variant()->conclude());
+ 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());
+ add("gothic", gothic_variant()->conclude());
+ add("janus", janus_variant()->conclude());
+ add("modern", modern_variant()->conclude());
+ add("chancellor", chancellor_variant()->conclude());
+ add("embassy", embassy_variant()->conclude());
+ add("centaur", centaur_variant()->conclude());
+ 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
+ add("amazons", amazons_variant()->conclude());
+#endif
+ add("xiangqi", xiangqi_variant()->conclude());
+ add("manchu", manchu_variant()->conclude());
+ add("supply", supply_variant()->conclude());
+ add("janggi", janggi_variant()->conclude());
+ add("janggitraditional", janggi_traditional_variant()->conclude());
+ add("janggimodern", janggi_modern_variant()->conclude());
+ add("janggicasual", janggi_casual_variant()->conclude());
+#endif
+}
+
+
+/// VariantMap::parse_istream reads variants from an INI-style configuration input stream.
+
+template <bool DoCheck>
+void VariantMap::parse_istream(std::istream& file) {
+ std::string variant, variant_template, key, value, input;
+ while (file.peek() != '[' && std::getline(file, input)) {}
+
+ std::vector<std::string> varsToErase = {};
+ while (file.get() && std::getline(std::getline(file, variant, ']'), input))
+ {
+ // Extract variant template, if specified
+ if (!std::getline(std::getline(std::stringstream(variant), variant, ':'), variant_template))
+ variant_template = "";
+
+ // Read variant rules
+ Config attribs = {};
+ while (file.peek() != '[' && std::getline(file, input))
+ {
+ std::stringstream ss(input);
+ if (ss.peek() != '#' && std::getline(std::getline(ss, key, '=') >> std::ws, value) && !key.empty())
+ attribs[key.erase(key.find_last_not_of(" ") + 1)] = value;
+ }
+
+ // Create variant
+ if (variants.find(variant) != variants.end())
+ std::cerr << "Variant '" << variant << "' already exists." << std::endl;
+ else if (!variant_template.empty() && variants.find(variant_template) == variants.end())
+ std::cerr << "Variant template '" << variant_template << "' does not exist." << std::endl;
+ else
+ {
+ if (DoCheck)
+ std::cerr << "Parsing variant: " << variant << std::endl;
+ Variant* v = !variant_template.empty() ? VariantParser<DoCheck>(attribs).parse(new Variant(*variants.find(variant_template)->second))
+ : VariantParser<DoCheck>(attribs).parse();
+ if (v->maxFile <= FILE_MAX && v->maxRank <= RANK_MAX)
+ {
+ add(variant, v->conclude());
+ // In order to allow inheritance, we need to temporarily add configured variants
+ // even when only checking them, but we remove them later after parsing is finished.
+ if (DoCheck)
+ varsToErase.push_back(variant);
+ }
+ else
+ delete v;
+ }
+ }
+ // Clean up temporary variants
+ for (std::string tempVar : varsToErase)
+ {
+ delete variants[tempVar];
+ variants.erase(tempVar);
+ }
+}
+
+/// VariantMap::parse reads variants from an INI-style configuration file.
+
+template <bool DoCheck>
+void VariantMap::parse(std::string path) {
+ if (path.empty() || path == "<empty>")
+ return;
+ std::ifstream file(path);
+ if (!file.is_open())
+ {
+ std::cerr << "Unable to open file " << path << std::endl;
+ return;
+ }
+ parse_istream<DoCheck>(file);
+ file.close();
+}
+
+template void VariantMap::parse<true>(std::string path);
+template void VariantMap::parse<false>(std::string path);
+
+void VariantMap::add(std::string s, const Variant* v) {
+ insert(std::pair<std::string, const Variant*>(s, v));
+}
+
+void VariantMap::clear_all() {
+ for (auto const& element : *this)
+ delete element.second;
+ clear();
+}
+
+std::vector<std::string> VariantMap::get_keys() {
+ std::vector<std::string> keys;
+ for (auto const& element : *this)
+ keys.push_back(element.first);
+ return keys;
+}
++
++} // namespace Stockfish
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef VARIANT_H_INCLUDED
+#define VARIANT_H_INCLUDED
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#include <functional>
+
+#include "types.h"
+#include "bitboard.h"
+
++namespace Stockfish {
+
+/// Variant struct stores information needed to determine the rules of a variant.
+
+struct Variant {
+ std::string variantTemplate = "fairy";
+ std::string pieceToCharTable = "-";
+ int pocketSize = 0;
+ Rank maxRank = RANK_8;
+ 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, ' ');
+ std::string pieceToCharSynonyms = std::string(PIECE_NB, ' ');
+ std::string startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+ Bitboard mobilityRegion[COLOR_NB][PIECE_TYPE_NB] = {};
+ Rank promotionRank = RANK_8;
+ std::set<PieceType, std::greater<PieceType> > promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT };
+ bool sittuyinPromotion = false;
+ int promotionLimit[PIECE_TYPE_NB] = {}; // 0 means unlimited
+ PieceType promotedPieceType[PIECE_TYPE_NB] = {};
+ bool piecePromotionOnCapture = false;
+ bool mandatoryPawnPromotion = true;
+ bool mandatoryPiecePromotion = false;
+ bool pieceDemotion = false;
+ bool blastOnCapture = false;
+ bool doubleStep = true;
+ Rank doubleStepRank = RANK_2;
+ Rank doubleStepRankMin = RANK_2;
+ Bitboard enPassantRegion = AllSquares;
+ bool castling = true;
+ bool castlingDroppedPiece = false;
+ File castlingKingsideFile = FILE_G;
+ File castlingQueensideFile = FILE_C;
+ Rank castlingRank = RANK_1;
+ File castlingKingFile = FILE_E;
+ PieceType castlingKingPiece = KING;
+ PieceType castlingRookPiece = ROOK;
+ PieceType kingType = KING;
+ bool checking = true;
+ bool dropChecks = true;
+ bool mustCapture = false;
+ bool mustDrop = false;
+ PieceType mustDropType = ALL_PIECES;
+ bool pieceDrops = false;
+ bool dropLoop = false;
+ bool capturesToHand = false;
+ bool firstRankPawnDrops = false;
+ bool promotionZonePawnDrops = false;
+ bool dropOnTop = false;
+ EnclosingRule enclosingDrop = NO_ENCLOSING;
+ Bitboard enclosingDropStart = 0;
+ Bitboard whiteDropRegion = AllSquares;
+ Bitboard blackDropRegion = AllSquares;
+ bool sittuyinRookDrop = false;
+ bool dropOppositeColoredBishop = false;
+ bool dropPromoted = false;
+ PieceType dropNoDoubled = NO_PIECE_TYPE;
+ int dropNoDoubledCount = 1;
+ bool immobilityIllegal = false;
+ bool gating = false;
+ bool arrowGating = false;
+ bool seirawanGating = false;
+ bool cambodianMoves = false;
+ Bitboard diagonalLines = 0;
+ bool pass = false;
+ bool passOnStalemate = false;
+ bool makpongRule = false;
+ bool flyingGeneral = false;
+ Rank soldierPromotionRank = RANK_1;
+ EnclosingRule flipEnclosedPieces = NO_ENCLOSING;
+
+ // game end
+ int nMoveRule = 50;
+ int nFoldRule = 3;
+ Value nFoldValue = VALUE_DRAW;
+ bool nFoldValueAbsolute = false;
+ bool perpetualCheckIllegal = false;
+ bool moveRepetitionIllegal = false;
+ Value stalemateValue = VALUE_DRAW;
+ bool stalematePieceCount = false; // multiply stalemate value by sign(count(~stm) - count(stm))
+ Value checkmateValue = -VALUE_MATE;
+ bool shogiPawnDropMateIllegal = false;
+ bool shatarMateRule = false;
+ bool bikjangRule = false;
+ Value extinctionValue = VALUE_NONE;
+ bool extinctionClaim = false;
+ bool extinctionPseudoRoyal = false;
+ std::set<PieceType> extinctionPieceTypes = {};
+ int extinctionPieceCount = 0;
+ int extinctionOpponentPieceCount = 0;
+ PieceType flagPiece = NO_PIECE_TYPE;
+ Bitboard whiteFlag = 0;
+ Bitboard blackFlag = 0;
+ bool flagMove = false;
+ bool checkCounting = false;
+ int connectN = 0;
+ MaterialCounting materialCounting = NO_MATERIAL_COUNTING;
+ CountingRule countingRule = NO_COUNTING;
+
+ NnueFeatures nnueFeatures = NNUE_VARIANT;
+
+ // Derived properties
+ bool fastAttacks = true;
+ bool fastAttacks2 = true;
+ PieceType nnueKing = KING;
+ bool endgameEval = false;
+
+ void add_piece(PieceType pt, char c, std::string betza = "", char c2 = ' ') {
+ pieceToChar[make_piece(WHITE, pt)] = toupper(c);
+ pieceToChar[make_piece(BLACK, pt)] = tolower(c);
+ pieceToCharSynonyms[make_piece(WHITE, pt)] = toupper(c2);
+ pieceToCharSynonyms[make_piece(BLACK, pt)] = tolower(c2);
+ pieceTypes.insert(pt);
+ // Add betza notation for custom piece
+ if (is_custom(pt))
+ customPiece[pt - CUSTOM_PIECES] = betza;
+ }
+
+ void add_piece(PieceType pt, char c, char c2) {
+ add_piece(pt, c, "", c2);
+ }
+
+ void remove_piece(PieceType pt) {
+ pieceToChar[make_piece(WHITE, pt)] = ' ';
+ pieceToChar[make_piece(BLACK, pt)] = ' ';
+ pieceToCharSynonyms[make_piece(WHITE, pt)] = ' ';
+ pieceToCharSynonyms[make_piece(BLACK, pt)] = ' ';
+ pieceTypes.erase(pt);
+ }
+
+ void reset_pieces() {
+ pieceToChar = std::string(PIECE_NB, ' ');
+ pieceToCharSynonyms = std::string(PIECE_NB, ' ');
+ pieceTypes.clear();
+ }
+
+ // Pre-calculate derived properties
+ Variant* conclude() {
+ fastAttacks = std::all_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {
+ return ( pt < FAIRY_PIECES
+ || pt == COMMONER || pt == IMMOBILE_PIECE
+ || pt == ARCHBISHOP || pt == CHANCELLOR
+ || (pt == KING && kingType == KING))
+ && !(mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]);
+ })
+ && !cambodianMoves
+ && !diagonalLines;
+ fastAttacks2 = std::all_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {
+ return ( pt < FAIRY_PIECES
+ || pt == COMMONER || pt == FERS || pt == WAZIR || pt == BREAKTHROUGH_PIECE
+ || pt == SHOGI_PAWN || pt == GOLD || pt == SILVER || pt == SHOGI_KNIGHT
+ || pt == DRAGON || pt == DRAGON_HORSE || pt == LANCE
+ || (pt == KING && kingType == KING))
+ && !(mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]);
+ })
+ && !cambodianMoves
+ && !diagonalLines;
+ nnueKing = pieceTypes.find(KING) != pieceTypes.end() ? KING
+ : extinctionPieceTypes.find(COMMONER) != extinctionPieceTypes.end() ? COMMONER
+ : NO_PIECE_TYPE;
+ // For endgame evaluation to be applicable, no special win rules must apply.
+ // Furthermore, rules significantly changing game mechanics also invalidate it.
+ endgameEval = std::none_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {
+ return mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt];
+ })
+ && extinctionValue == VALUE_NONE
+ && checkmateValue == -VALUE_MATE
+ && stalemateValue == VALUE_DRAW
+ && !materialCounting
+ && !flagPiece
+ && !mustCapture
+ && !checkCounting
+ && !makpongRule
+ && !connectN
+ && !blastOnCapture
+ && !capturesToHand
+ && !twoBoards
+ && kingType == KING;
+ return this;
+ }
+};
+
+class VariantMap : public std::map<std::string, const Variant*> {
+public:
+ void init();
+ template <bool DoCheck> void parse(std::string path);
+ template <bool DoCheck> void parse_istream(std::istream& file);
+ void clear_all();
+ std::vector<std::string> get_keys();
+
+private:
+ void add(std::string s, const Variant* v);
+};
+
+extern VariantMap variants;
+
++} // namespace Stockfish
++
+#endif // #ifndef VARIANT_H_INCLUDED
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <string>
+
+#include "evaluate.h"
+#include "misc.h"
+#include "partner.h"
+#include "search.h"
+#include "thread.h"
+#include "types.h"
+#include "uci.h"
+#include "xboard.h"
+
++namespace Stockfish {
++
+namespace {
+
+ const Search::LimitsType analysisLimits = []{
+ Search::LimitsType limits;
+ limits.infinite = 1;
+ return limits;
+ }();
+
+} // namespace
+
+namespace XBoard {
+
+ StateMachine* stateMachine = nullptr;
+
+ // go() starts the search for game play, analysis, or perft.
+
+ void StateMachine::go(Search::LimitsType searchLimits, bool ponder) {
+
+ searchLimits.startTime = now(); // As early as possible!
+
+ Threads.start_thinking(pos, states, searchLimits, ponder);
+ }
+
+ // ponder() starts a ponder search
+
+ void StateMachine::ponder() {
+
+ sync_cout << "Hint: " << UCI::move(pos, ponderMove) << sync_endl;
+ ponderHighlight = highlight(UCI::square(pos, from_sq(ponderMove)));
+ do_move(ponderMove);
+ ponderMove = MOVE_NONE;
+ go(limits, true);
+ }
+
+ // stop() stops an ongoing search (if any)
+ // and does not print/apply a move if aborted
+
+ void StateMachine::stop(bool abort) {
+
+ if (abort)
+ Threads.abort = true;
+ Threads.stop = true;
+ Threads.main()->wait_for_search_finished();
+ // Ensure that current position does not get out of sync with GUI
+ if (Threads.main()->ponder)
+ {
+ assert(moveList.size());
+ undo_move();
+ Threads.main()->ponder = false;
+ }
+ }
+
+ // setboard() is called when engine receives the "setboard" XBoard command.
+
+ void StateMachine::setboard(std::string fen) {
+
+ if (fen.empty())
+ fen = variants.find(Options["UCI_Variant"])->second->startFen;
+
+ states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
+ moveList.clear();
+ pos.set(variants.find(Options["UCI_Variant"])->second, fen, Options["UCI_Chess960"], &states->back(), Threads.main());
+ }
+
+ // do_move() is called when engine needs to apply a move when using XBoard protocol.
+
+ void StateMachine::do_move(Move m) {
+
+ // transfer states back
+ if (Threads.setupStates.get())
+ states = std::move(Threads.setupStates);
+
+ if (m == MOVE_NONE)
+ return;
+ moveList.push_back(m);
+ states->emplace_back();
+ pos.do_move(m, states->back());
+ }
+
+ // undo_move() is called when the engine receives the undo command in XBoard protocol.
+
+ void StateMachine::undo_move() {
+
+ // transfer states back
+ if (Threads.setupStates.get())
+ states = std::move(Threads.setupStates);
+
+ pos.undo_move(moveList.back());
+ states->pop_back();
+ moveList.pop_back();
+ }
+
+ std::string StateMachine::highlight(std::string square) {
+ Bitboard promotions = 0, captures = 0, quiets = 0;
+ // Collect targets
+ for (const auto& m : MoveList<LEGAL>(pos))
+ {
+ Square from = from_sq(m), to = to_sq(m);
+ if (is_ok(from) && UCI::square(pos, from) == square && !is_pass(m))
+ {
+ if (type_of(m) == PROMOTION)
+ promotions |= to;
+ else if (pos.capture(m))
+ captures |= to;
+ else
+ {
+ if (type_of(m) == CASTLING && !pos.is_chess960())
+ to = make_square(to > from ? pos.castling_kingside_file()
+ : pos.castling_queenside_file(), rank_of(from));
+ quiets |= to;
+ }
+ }
+ }
+ // Generate color FEN
+ int emptyCnt;
+ std::ostringstream ss;
+ for (Rank r = pos.max_rank(); r >= RANK_1; --r)
+ {
+ for (File f = FILE_A; f <= pos.max_file(); ++f)
+ {
+ for (emptyCnt = 0; f <= pos.max_file() && !((promotions | captures | quiets) & make_square(f, r)); ++f)
+ ++emptyCnt;
+
+ if (emptyCnt)
+ ss << emptyCnt;
+
+ if (f <= pos.max_file())
+ ss << (promotions & make_square(f, r) ? "M" : captures & make_square(f, r) ? "R" : "Y");
+ }
+
+ if (r > RANK_1)
+ ss << '/';
+ }
+ return ss.str();
+ }
+
+/// StateMachine::process_command() processes commands of the XBoard protocol.
+
+void StateMachine::process_command(std::string token, std::istringstream& is) {
+ if (token == "protover")
+ {
+ std::string vars = "chess";
+ for (std::string v : variants.get_keys())
+ if (v != "chess")
+ vars += "," + v;
+ sync_cout << "feature setboard=1 usermove=1 time=1 memory=1 smp=1 colors=0 draw=0 "
+ << "highlight=1 name=0 sigint=0 ping=1 myname=\""
+ << engine_info(false, true) << "\" " << "variants=\"" << vars << "\""
+ << Options << sync_endl;
+ sync_cout << "feature done=1" << sync_endl;
+ }
+ else if (token == "accepted" || token == "rejected") {}
+ else if (token == "hover" || token == "put") {}
+ else if (token == "lift")
+ {
+ if (is >> token)
+ {
+ if (Threads.main()->ponder)
+ {
+ if (token == UCI::square(pos, from_sq(moveList.back())))
+ sync_cout << "highlight " << ponderHighlight << sync_endl;
+ else
+ {
+ Move currentPonderMove = moveList.back();
+ stop();
+ sync_cout << "highlight " << highlight(token) << sync_endl;
+ // Restart ponder search with random guess
+ auto moves = MoveList<LEGAL>(pos);
+ std::vector<Move> filteredMoves;
+ copy_if(moves.begin(), moves.end(), back_inserter(filteredMoves), [&](const Move m) {
+ return is_ok(from_sq(m)) && UCI::square(pos, from_sq(m)) == token;
+ });
+ if (filteredMoves.size())
+ {
+ static PRNG rng(now());
+ ponderMove = filteredMoves.at(rng.rand<unsigned>() % filteredMoves.size());
+ }
+ else
+ ponderMove = currentPonderMove;
+ ponder();
+ }
+ }
+ else
+ sync_cout << "highlight " << highlight(token) << sync_endl;
+ }
+ }
+ else if (token == "ping")
+ {
+ if (!(is >> token))
+ token = "";
+ sync_cout << "pong " << token << sync_endl;
+ }
+ else if (token == "new")
+ {
+ stop();
+ Search::clear();
+ setboard();
+ // play second by default
+ playColor = ~pos.side_to_move();
+ Threads.sit = false;
+ Partner.reset();
+ }
+ else if (token == "variant")
+ {
+ stop();
+ if (is >> token)
+ Options["UCI_Variant"] = token;
+ setboard();
+ }
+ else if (token == "force" || token == "result")
+ {
+ stop();
+ playColor = COLOR_NB;
+ }
+ else if (token == "?")
+ {
+ if (!Threads.main()->ponder)
+ stop(false);
+ }
+ else if (token == "go")
+ {
+ stop();
+ playColor = pos.side_to_move();
+ go(limits);
+ moveAfterSearch = true;
+ }
+ else if (token == "level" || token == "st" || token == "sd" || token == "time" || token == "otim")
+ {
+ int num;
+ if (token == "level")
+ {
+ // moves to go
+ is >> limits.movestogo;
+ // base time
+ is >> token;
+ size_t idx = token.find(":");
+ if (idx != std::string::npos)
+ num = std::stoi(token.substr(0, idx)) * 60 + std::stoi(token.substr(idx + 1));
+ else
+ num = std::stoi(token) * 60;
+ limits.time[WHITE] = num * 1000;
+ limits.time[BLACK] = num * 1000;
+ // increment
+ is >> num;
+ limits.inc[WHITE] = num * 1000;
+ limits.inc[BLACK] = num * 1000;
+ }
+ else if (token == "sd")
+ is >> limits.depth;
+ else if (token == "st")
+ {
+ is >> num;
+ limits.movetime = num * 1000;
+ limits.time[WHITE] = limits.time[BLACK] = 0;
+ }
+ // Note: time/otim are in centi-, not milliseconds
+ else if (token == "time")
+ {
+ is >> num;
+ Color us = playColor != COLOR_NB ? playColor : pos.side_to_move();
+ if (limits.time[us])
+ limits.time[us] = num * 10;
+ }
+ else if (token == "otim")
+ {
+ is >> num;
+ Color them = playColor != COLOR_NB ? ~playColor : ~pos.side_to_move();
+ if (limits.time[them])
+ limits.time[them] = num * 10;
+ }
+ }
+ else if (token == "setboard")
+ {
+ stop();
+ std::string fen;
+ std::getline(is >> std::ws, fen);
+ // Check if setboard actually indicates a passing move
+ // to avoid unnecessarily clearing the move history
+ if (pos.pass())
+ {
+ StateInfo st;
+ Position p;
+ p.set(pos.variant(), fen, pos.is_chess960(), &st, pos.this_thread());
+ Move m;
+ std::string passMove = "@@@@";
+ if ((m = UCI::to_move(pos, passMove)) != MOVE_NONE)
+ do_move(m);
+ // apply setboard if passing does not lead to a match
+ if (pos.key() != p.key())
+ setboard(fen);
+ }
+ else
+ setboard(fen);
+ // Winboard sends setboard after passing moves
+ if (Options["UCI_AnalyseMode"])
+ go(analysisLimits);
+ else if (pos.side_to_move() == playColor)
+ {
+ go(limits);
+ moveAfterSearch = true;
+ }
+ }
+ else if (token == "cores")
+ {
+ stop();
+ if (is >> token)
+ Options["Threads"] = token;
+ }
+ else if (token == "memory")
+ {
+ stop();
+ if (is >> token)
+ Options["Hash"] = token;
+ }
+ else if (token == "hard" || token == "easy")
+ Options["Ponder"] = token == "hard";
+ else if (token == "option")
+ {
+ std::string name, value;
+ is.get();
+ std::getline(is, name, '=');
+ std::getline(is, value);
+ if (Options.count(name))
+ {
+ if (Options[name].get_type() == "check")
+ value = value == "1" ? "true" : "false";
+ Options[name] = value;
+ }
+ }
+ else if (token == "analyze")
+ {
+ stop();
+ Options["UCI_AnalyseMode"] = std::string("true");
+ go(analysisLimits);
+ }
+ else if (token == "exit")
+ {
+ stop();
+ Options["UCI_AnalyseMode"] = std::string("false");
+ }
+ else if (token == "undo")
+ {
+ stop();
+ if (moveList.size())
+ {
+ undo_move();
+ if (Options["UCI_AnalyseMode"])
+ go(analysisLimits);
+ }
+ }
+ // Bughouse commands
+ else if (token == "partner")
+ Partner.parse_partner(is);
+ else if (token == "ptell")
+ {
+ Partner.parse_ptell(is, pos);
+ // play move requested by partner
+ // Partner.moveRequested can only be set if search was successfully aborted
+ if (moveAfterSearch && Partner.moveRequested)
+ {
+ assert(Threads.abort);
+ stop();
+ sync_cout << "move " << UCI::move(pos, Partner.moveRequested) << sync_endl;
+ do_move(Partner.moveRequested);
+ moveAfterSearch = false;
+ Partner.moveRequested = MOVE_NONE;
+ }
+ }
+ else if (token == "holding")
+ {
+ stop();
+ // holding [<white>] [<black>] <color><piece>
+ std::string white_holdings, black_holdings;
+ if ( std::getline(is, token, '[') && std::getline(is, white_holdings, ']')
+ && std::getline(is, token, '[') && std::getline(is, black_holdings, ']'))
+ {
+ std::string fen;
+ char color, pieceType;
+ // Use the obtained holding if available to avoid race conditions
+ if (is >> color && is >> pieceType)
+ {
+ fen = pos.fen();
+ fen.insert(fen.find(']'), 1, toupper(color) == 'B' ? tolower(pieceType) : toupper(pieceType));
+ }
+ else
+ {
+ std::transform(black_holdings.begin(), black_holdings.end(), black_holdings.begin(), ::tolower);
+ fen = pos.fen(false, false, 0, white_holdings + black_holdings);
+ }
+ setboard(fen);
+ }
+ // restart search
+ if (moveAfterSearch)
+ go(limits);
+ }
+ // Additional custom non-XBoard commands
+ else if (token == "perft")
+ {
+ stop();
+ Search::LimitsType perft_limits;
+ is >> perft_limits.perft;
+ go(perft_limits);
+ }
+ else if (token == "d")
+ sync_cout << pos << sync_endl;
+ else if (token == "eval")
+ sync_cout << Eval::trace(pos) << sync_endl;
+ // Move strings and unknown commands
+ else
+ {
+ bool isMove = false;
+
+ if (token == "usermove")
+ {
+ is >> token;
+ isMove = true;
+ }
+
+ // Handle pondering
+ if (Threads.main()->ponder)
+ {
+ assert(moveList.size());
+ if (token == UCI::move(pos, moveList.back()))
+ {
+ // ponderhit
+ moveAfterSearch = true;
+ Threads.main()->ponder = false;
+ return;
+ }
+ }
+ stop(false);
+
+ // Apply move
+ Move m;
+ if ((m = UCI::to_move(pos, token)) != MOVE_NONE)
+ do_move(m);
+ else
+ sync_cout << (isMove ? "Illegal move: " : "Error (unknown command): ") << token << sync_endl;
+
+ // Restart search if applicable
+ if (Options["UCI_AnalyseMode"])
+ go(analysisLimits);
+ else if (pos.side_to_move() == playColor)
+ {
+ moveAfterSearch = true;
+ go(limits);
+ }
+ }
+}
+
+} // namespace XBoard
++
++} // namespace Stockfish
--- /dev/null
+/*
+ Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
+ Copyright (C) 2018-2021 Fabian Fichter
+
+ Fairy-Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fairy-Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef XBOARD_H_INCLUDED
+#define XBOARD_H_INCLUDED
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+#include "types.h"
+
++namespace Stockfish {
++
+class Position;
+
+namespace XBoard {
+
+/// StateMachine class maintains the states required by XBoard protocol
+
+class StateMachine {
+public:
+ StateMachine(Position& uciPos, StateListPtr& uciPosStates) : pos(uciPos), states(uciPosStates) {
+ moveList = std::deque<Move>();
+ moveAfterSearch = false;
+ playColor = COLOR_NB;
+ ponderMove = MOVE_NONE;
+ ponderHighlight = "";
+ }
+ void go(Search::LimitsType searchLimits, bool ponder = false);
+ void ponder();
+ void stop(bool abort = true);
+ void setboard(std::string fen = "");
+ void do_move(Move m);
+ void undo_move();
+ std::string highlight(std::string square);
+ void process_command(std::string token, std::istringstream& is);
+ bool moveAfterSearch;
+ Move ponderMove;
+
+private:
+ Position& pos;
+ StateListPtr& states;
+ std::deque<Move> moveList;
+ Search::LimitsType limits;
+ Color playColor;
+ std::string ponderHighlight;
+};
+
+extern StateMachine* stateMachine;
+
+} // namespace XBoard
+
++} // namespace Stockfish
++
+#endif // #ifndef XBOARD_H_INCLUDED