From: Fabian Fichter Date: Sat, 22 May 2021 11:48:31 +0000 (+0200) Subject: Merge official-stockfish/master X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=b4718185dfc324b09cb5b2e414a770116d359eb1;p=fairystockfish.git Merge official-stockfish/master No functional change. --- b4718185dfc324b09cb5b2e414a770116d359eb1 diff --cc src/apiutil.h index 7fe7c54,0000000..b1d3621 mode 100644,000000..100644 --- a/src/apiutil.h +++ b/src/apiutil.h @@@ -1,930 -1,0 +1,933 @@@ +/* + 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 . +*/ + +#ifndef APIUTIL_H_INCLUDED +#define APIUTIL_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +#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(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(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 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(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 get_squares_for_piece(char piece) const { + std::vector 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 get_fen_parts(const std::string& fullFen, char delim) { + std::vector 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& 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& 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& 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& castlingInfoSplitted, const CharBoard& board, + const std::array& kingPositions, const std::array& kingPositionsStart, + const std::array, 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 fenParts = get_fen_parts(fen, ' '); + std::vector 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 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 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 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 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, 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 diff --cc src/bitboard.cpp index eb7c628,a202144..d0f29c5 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@@ -20,10 -20,10 +20,12 @@@ #include #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]; @@@ -349,9 -112,25 +350,8 @@@ void Bitboards::init() } } - 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 @@@ -462,9 -209,7 +462,11 @@@ } } } + + delete[] occupancy; + delete[] reference; + delete[] epoch; } } + + } // namespace Stockfish diff --cc src/bitboard.h index 5c177b8,e14fe0d..d463cdb --- a/src/bitboard.h +++ b/src/bitboard.h @@@ -36,24 -37,12 +38,24 @@@ void init_pieces() 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; diff --cc src/endgame.cpp index 83b6604,a44d3a1..7fc242a --- a/src/endgame.cpp +++ b/src/endgame.cpp @@@ -931,10 -741,7 +933,12 @@@ ScaleFactor Endgame::operator()(c // 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 diff --cc src/evaluate.cpp index 2975d6f,d43b8fa..c84dd69 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@@ -55,10 -54,10 +55,12 @@@ using namespace std; - using namespace Eval::NNUE; + using namespace Stockfish::Eval::NNUE; + + namespace Stockfish { +NnueFeatures currentNnueFeatures; + namespace Eval { bool useNNUE; diff --cc src/evaluate.h index 5b26806,6210bd5..2776422 --- a/src/evaluate.h +++ b/src/evaluate.h @@@ -50,6 -51,6 +52,8 @@@ namespace Eval } // namespace Eval +extern NnueFeatures currentNnueFeatures; + + } // namespace Stockfish + #endif // #ifndef EVALUATE_H_INCLUDED diff --cc src/ffishjs.cpp index df29368,0000000..bc67016 mode 100644,000000..100644 --- a/src/ffishjs.cpp +++ b/src/ffishjs.cpp @@@ -1,665 -1,0 +1,666 @@@ +/* + 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 . +*/ + +#include +#include +#include +#include +#include +#include + +#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 +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 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(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(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(pos).size(); + } + + bool push(std::string uciMove) { + const Move move = UCI::to_move(this->pos, uciMove); + if (is_move_none(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(pos)) { + if (sanMove == SAN::move_to_san(this->pos, move, notation)) { + foundMove = move; + break; + } + } + if (is_move_none(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(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 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(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(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(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 + 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(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 header; + std::unique_ptr 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(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") + .constructor<>() + .constructor() + .constructor() + .constructor() + .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(&Board::push_san)) + .function("pushSan", select_overload(&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(&Board::san_move)) + .function("sanMove", select_overload(&Board::san_move)) + .function("variationSan", select_overload(&Board::variation_san)) + .function("variationSan", select_overload(&Board::variation_san)) + .function("variationSan", select_overload(&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(&Board::push_san_moves)) + .function("pushSanMoves", select_overload(&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") + .function("headerKeys", &Game::header_keys) + .function("headers", &Game::headers) + .function("mainlineMoves", &Game::mainline_moves); + // usage: e.g. ffish.Notation.DEFAULT + enum_("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); + function("setOptionInt", &ffish::set_option); + function("setOptionBool", &ffish::set_option); + 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(&ffish::validate_fen)); + function("validateFen", select_overload(&ffish::validate_fen)); + function("validateFen", select_overload(&ffish::validate_fen)); + // TODO: enable to string conversion method + // .class_function("getStringFromInstance", &Board::get_string_from_instance); +} diff --cc src/magic.h index 5413a3e,0000000..793ae17 mode 100644,000000..100644 --- a/src/magic.h +++ b/src/magic.h @@@ -1,1615 -1,0 +1,1619 @@@ +/* + 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 . +*/ + +#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 diff --cc src/main.cpp index c0a79b8,62e0ed5..d291c76 --- a/src/main.cpp +++ b/src/main.cpp @@@ -28,9 -28,8 +28,12 @@@ #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; diff --cc src/misc.h index 61fa2b1,f834e47..e74ae43 --- a/src/misc.h +++ b/src/misc.h @@@ -28,7 -28,9 +28,9 @@@ #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); diff --cc src/movegen.cpp index c2cefbc,51df6d0..8a82ff8 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@@ -21,50 -21,26 +21,52 @@@ #include "movegen.h" #include "position.h" + namespace Stockfish { + namespace { - template - ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { + template + 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(to - D, to, QUEEN); - if (attacks_bb(to) & ksq) - *moveList++ = make(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(from, to, pt_gating, pop_lsb(&b)); + } + return moveList; } - if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) + *moveList++ = make(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(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(from, to, pt_gating, to); + + return moveList; + } + + template + ExtMove* make_promotions(const Position& pos, ExtMove* moveList, Square to) { + + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { - *moveList++ = make(to - D, to, ROOK); - *moveList++ = make(to - D, to, BISHOP); - if (!(attacks_bb(to) & ksq)) - *moveList++ = make(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(to - D, to, pt); + PieceType pt = pos.promoted_piece_type(PAWN); + if (pt && !(pos.piece_promotion_on_capture() && pos.empty(to))) + *moveList++ = make(to - D, to); } return moveList; diff --cc src/movepick.cpp index 5718dc5,4ff4cff..77b8cc0 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@@ -20,15 -20,8 +20,15 @@@ #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 { diff --cc src/nnue/architectures/halfkp_256x2-32-32.h index 41d97b1,a676820..a88df9c --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@@ -28,13 -28,7 +28,13 @@@ #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 +using HalfKP = HalfKPChess; +} + - namespace Eval::NNUE { + namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using RawFeatures = Features::FeatureSet< diff --cc src/nnue/architectures/halfkp_shogi_256x2-32-32.h index 8879f4f,0000000..935e1ad mode 100644,000000..100644 --- a/src/nnue/architectures/halfkp_shogi_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_shogi_256x2-32-32.h @@@ -1,60 -1,0 +1,60 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// 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 +using HalfKP = HalfKPShogi; +} + - namespace Eval::NNUE { ++namespace Stockfish::Eval::NNUE { + +// Input features used in evaluation function +using RawFeatures = Features::FeatureSet< + Features::HalfKPShogi>; + +// Number of input feature dimensions after conversion +constexpr IndexType kTransformedFeatureDimensions = 256; + +namespace Layers { + +// Define network structure +using InputLayer = InputSlice; +using HiddenLayer1 = ClippedReLU>; +using HiddenLayer2 = ClippedReLU>; +using OutputLayer = AffineTransform; + +} // namespace Layers + +using Network = Layers::OutputLayer; + - } // namespace Eval::NNUE ++} // namespace Stockfish::Eval::NNUE + +#endif // #ifndef NNUE_HALFKP_SHOGI_256X2_32_32_H_INCLUDED diff --cc src/nnue/architectures/halfkp_variants_256x2-32-32.h index 2116ea9,0000000..d0aca10 mode 100644,000000..100644 --- a/src/nnue/architectures/halfkp_variants_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_variants_256x2-32-32.h @@@ -1,60 -1,0 +1,60 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// 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 +using HalfKP = HalfKPVariants; +} + - namespace Eval::NNUE { ++namespace Stockfish::Eval::NNUE { + +// Input features used in evaluation function +using RawFeatures = Features::FeatureSet< + Features::HalfKPVariants>; + +// Number of input feature dimensions after conversion +constexpr IndexType kTransformedFeatureDimensions = 256; + +namespace Layers { + +// Define network structure +using InputLayer = InputSlice; +using HiddenLayer1 = ClippedReLU>; +using HiddenLayer2 = ClippedReLU>; +using OutputLayer = AffineTransform; + +} // namespace Layers + +using Network = Layers::OutputLayer; + - } // namespace Eval::NNUE ++} // namespace Stockfish::Eval::NNUE + +#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED diff --cc src/nnue/features/half_kp.cpp index 085aa40,ac6317e..7c6648c --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@@ -21,16 -21,11 +21,16 @@@ #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 @@@ -68,6 -63,6 +68,6 @@@ } } - template class HalfKP; + template class HalfKPChess; - } // namespace Eval::NNUE::Features + } // namespace Stockfish::Eval::NNUE::Features diff --cc src/nnue/features/half_kp_shogi.cpp index 8b3b1e3,0000000..3c9f551 mode 100644,000000..100644 --- a/src/nnue/features/half_kp_shogi.cpp +++ b/src/nnue/features/half_kp_shogi.cpp @@@ -1,98 -1,0 +1,98 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//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 + void HalfKPShogi::AppendActiveIndices( + const Position& pos, Color perspective, IndexList* active) { + + Square ksq = orient(perspective, pos.square(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 + void HalfKPShogi::AppendChangedIndices( + const Position& pos, const DirtyPiece& dp, Color perspective, + IndexList* removed, IndexList* added) { + + Square ksq = orient(perspective, pos.square(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; + - } // namespace Eval::NNUE::Features ++} // namespace Stockfish::Eval::NNUE::Features diff --cc src/nnue/features/half_kp_shogi.h index 1e56f85,0000000..3e25339 mode 100644,000000..100644 --- a/src/nnue/features/half_kp_shogi.h +++ b/src/nnue/features/half_kp_shogi.h @@@ -1,59 -1,0 +1,59 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//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 + 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(SQUARE_NB_SHOGI) * static_cast(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 diff --cc src/nnue/features/half_kp_variants.cpp index 6167d14,0000000..cbfe0a3 mode 100644,000000..100644 --- a/src/nnue/features/half_kp_variants.cpp +++ b/src/nnue/features/half_kp_variants.cpp @@@ -1,96 -1,0 +1,96 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//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 + void HalfKPVariants::AppendActiveIndices( + const Position& pos, Color perspective, IndexList* active) { + + // Re-route to shogi features +#ifdef LARGEBOARDS + if (currentNnueFeatures == NNUE_SHOGI) + { + assert(HalfKPShogi::kDimensions <= kDimensions); + return HalfKPShogi::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 + void HalfKPVariants::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::kDimensions <= kDimensions); + return HalfKPShogi::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; + - } // namespace Eval::NNUE::Features ++} // namespace Stockfish::Eval::NNUE::Features diff --cc src/nnue/features/half_kp_variants.h index 92517db,0000000..09e5b94 mode 100644,000000..100644 --- a/src/nnue/features/half_kp_variants.h +++ b/src/nnue/features/half_kp_variants.h @@@ -1,63 -1,0 +1,63 @@@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//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 + 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(SQUARE_NB_SHOGI) * static_cast(SHOGI_PS_END); +#else + static_cast(SQUARE_NB_CHESS) * static_cast(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 diff --cc src/nnue/nnue_architecture.h index fba1d56,1680368..478e939 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@@ -22,11 -22,9 +22,11 @@@ #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, ""); diff --cc src/parser.cpp index e25bf91,0000000..7d6ac68 mode 100644,000000..100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@@ -1,435 -1,0 +1,439 @@@ +/* + 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 . +*/ + +#include +#include + +#include "apiutil.h" +#include "parser.h" +#include "piece.h" +#include "types.h" + ++namespace Stockfish { ++ +namespace { + + template 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 +template void VariantParser::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() ? "int" + : std::is_same() ? "Rank" + : std::is_same() ? "File" + : std::is_same() ? "bool" + : std::is_same() ? "Value" + : std::is_same() ? "MaterialCounting" + : std::is_same() ? "CountingRule" + : std::is_same() ? "Bitboard" + : typeid(T).name(); + std::cerr << key << " - Invalid value " << it->second << " for type " << typeName << std::endl; + } + } +} + +template +void VariantParser::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 +Variant* VariantParser::parse() { + Variant* v = new Variant(); + v->reset_pieces(); + v->promotionPieceTypes = {}; + return parse(v); +} + +template +Variant* VariantParser::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& 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& d) { return d.second; })) + std::cerr << piece_name(v->kingType) << " is not supported as kingType." << std::endl; + } + } + } + return v; +} + +template Variant* VariantParser::parse(); +template Variant* VariantParser::parse(); +template Variant* VariantParser::parse(Variant* v); +template Variant* VariantParser::parse(Variant* v); ++ ++} // namespace Stockfish diff --cc src/parser.h index 247027a,0000000..3ce3156 mode 100644,000000..100644 --- a/src/parser.h +++ b/src/parser.h @@@ -1,55 -1,0 +1,59 @@@ +/* + 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 . +*/ + +#ifndef PARSER_H_INCLUDED +#define PARSER_H_INCLUDED + +#include + +#include "variant.h" + ++namespace Stockfish { ++ +class Config : public std::map { +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::find(s); + } + const std::set& get_comsumed_keys() { + return consumedKeys; + } +private: + std::set consumedKeys = {}; +}; + +template +class VariantParser { +public: + VariantParser(const Config& c) : config (c) {}; + Variant* parse(); + Variant* parse(Variant* v); + +private: + Config config; + template 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 diff --cc src/partner.cpp index 3225255,0000000..21056cb mode 100644,000000..100644 --- a/src/partner.cpp +++ b/src/partner.cpp @@@ -1,154 -1,0 +1,158 @@@ +/* + 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 . +*/ + +#include +#include +#include + +#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 +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("I listen to the commands help, sit, go, move, fast, slow, dead, x, time, and otim."); + ptell("Tell 'help sit', etc. for details."); + } + else if (token == "sit") + ptell("After receiving 'sit', I stop moving. Also see 'go'."); + else if (token == "go") + ptell("After receiving 'go', I will no longer sit."); + else if (token == "move") + { + ptell("After receiving 'move', I will move immediately." ); + ptell("If you specify a valid move, e.g., 'move e2e4', I will play it."); + } + else if (token == "fast") + ptell("After receiving 'go', I will play fast."); + else if (token == "slow") + ptell("After receiving 'slow', I will play at normal speed."); + else if (token == "dead") + ptell("After receiving 'dead', I assume you are dead and I play fast."); + else if (token == "x") + ptell("After receiving 'x', I assume I can play normally again."); + else if (token == "time") + { + ptell("'time' together with your time in centiseconds allows me to consider your time."); + ptell("E.g., 'time 1000' for 10 seconds."); + } + else if (token == "otim") + ptell("'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("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("sorry, not possible"); + } + else + // Move immediately on request + Threads.stop = true; + } + else if (token == "fast") + { + fast = true; + ptell("I play fast, tell me 'slow' to play normally again"); + } + else if (token == "slow") + { + fast = false; + ptell("I play at normal speed again."); + } + else if (token == "dead") + { + partnerDead = true; + ptell("I play fast, tell me 'x' if you are no longer dead."); + } + else if (token == "x") + { + partnerDead = false; + sitRequested = false; + ptell("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(const std::string&); +template void PartnerHandler::ptell(const std::string&); +template void PartnerHandler::ptell(const std::string&); ++ ++} // namespace Stockfish diff --cc src/partner.h index cf61c03,0000000..29f60a1 mode 100644,000000..100644 --- a/src/partner.h +++ b/src/partner.h @@@ -1,52 -1,0 +1,56 @@@ +/* + 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 . +*/ + +#ifndef PARTNER_H_INCLUDED +#define PARTNER_H_INCLUDED + +#include +#include + +#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 + void ptell(const std::string& message); + void parse_partner(std::istringstream& is); + void parse_ptell(std::istringstream& is, const Position& pos); + + std::atomic isFairy; + std::atomic fast, sitRequested, partnerDead, weDead, weWin, weVirtualWin, weVirtualLoss; + std::atomic time, opptime; + Move moveRequested; +}; + +extern PartnerHandler Partner; + ++} // namespace Stockfish ++ +#endif // #ifndef PARTNER_H_INCLUDED diff --cc src/piece.cpp index 6a900ce,0000000..6d91ecf mode 100644,000000..100644 --- a/src/piece.cpp +++ b/src/piece.cpp @@@ -1,232 -1,0 +1,236 @@@ +/* + 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 . +*/ + +#include +#include +#include + +#include "types.h" +#include "piece.h" + ++namespace Stockfish { ++ +PieceMap pieceMap; // Global object + + +namespace { + std::map>> 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>> 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 moveModalities = {}; + bool hopper = false; + bool rider = false; + bool lame = false; + int distance = 0; + std::vector 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 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(pt, p)); +} + +void PieceMap::clear_all() { + for (auto const& element : *this) + delete element.second; + clear(); +} ++ ++} // namespace Stockfish diff --cc src/piece.h index ac73e6e,0000000..b8def1c mode 100644,000000..100644 --- a/src/piece.h +++ b/src/piece.h @@@ -1,54 -1,0 +1,57 @@@ +/* + 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 . +*/ + +#ifndef PIECE_H_INCLUDED +#define PIECE_H_INCLUDED + +#include +#include + +#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 steps[MOVE_MODALITY_NB] = {}; + std::map slider[MOVE_MODALITY_NB] = {}; + std::map hopper[MOVE_MODALITY_NB] = {}; +}; + +struct PieceMap : public std::map { + 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 diff --cc src/position.h index f072393,4ab3761..2cd8f26 --- a/src/position.h +++ b/src/position.h @@@ -1242,91 -424,6 +1243,93 @@@ inline StateInfo* Position::state() con 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 diff --cc src/psqt.cpp index 64b8161,33a3e00..93e8548 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@@ -26,55 -24,8 +26,58 @@@ #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 { diff --cc src/psqt.h index c0030de,7abb148..116091b --- a/src/psqt.h +++ b/src/psqt.h @@@ -23,17 -23,16 +23,17 @@@ #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 diff --cc src/pyffish.cpp index 7d72ec9,0000000..e2dfc9c mode 100644,000000..100644 --- a/src/pyffish.cpp +++ b/src/pyffish.cpp @@@ -1,415 -1,0 +1,416 @@@ +/* + Based on Jean-Francois Romang work + https://github.com/jromang/Stockfish/blob/pyfish/src/pyfish.cpp +*/ + +#include +#include + +#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(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(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(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(1)); + buildPosition(pos, states, variant, fen, sanMoves, chess960); + + int numMoves = PyList_Size(moveList); + for (int i=0; iemplace_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(1)); + buildPosition(pos, states, variant, fen, moveList, chess960); + for (const auto& m : MoveList(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(countStarted, INT_MAX); // pseudo-unsigned + + StateListPtr states(new std::deque(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(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(1)); + buildPosition(pos, states, variant, fen, moveList, chess960); + assert(!MoveList(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(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(countStarted, INT_MAX); // pseudo-unsigned + + StateListPtr states(new std::deque(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(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; +}; diff --cc src/search.cpp index 2f3018d,4e2ec09..8d52294 --- a/src/search.cpp +++ b/src/search.cpp @@@ -34,9 -33,10 +34,11 @@@ #include "timeman.h" #include "tt.h" #include "uci.h" +#include "xboard.h" #include "syzygy/tbprobe.h" + namespace Stockfish { + namespace Search { LimitsType Limits; diff --cc src/thread.cpp index 7780d5e,3ef73df..74fd736 --- a/src/thread.cpp +++ b/src/thread.cpp @@@ -26,8 -25,9 +26,10 @@@ #include "uci.h" #include "syzygy/tbprobe.h" #include "tt.h" +#include "xboard.h" + namespace Stockfish { + ThreadPool Threads; // Global object diff --cc src/thread_win32_osx.h index 0a95c0d,a21674c..836794d --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@@ -55,13 -57,18 +57,19 @@@ public pthread_create(&thread, attr, start_routine, 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 diff --cc src/tt.h index cc01c34,d915d92..f54b255 --- a/src/tt.h +++ b/src/tt.h @@@ -22,7 -22,9 +22,9 @@@ #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 diff --cc src/ucioption.cpp index f515237,d59c010..669258c --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@@ -34,21 -31,12 +34,23 @@@ 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 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)); } @@@ -352,21 -191,6 +354,23 @@@ Option& Option::operator=(const string return *this; } +void Option::set_combo(std::vector 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 diff --cc src/variant.cpp index 1f2ef8a,0000000..6f3f048 mode 100644,000000..100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@@ -1,1494 -1,0 +1,1498 @@@ +/* + 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 . +*/ + +#include +#include +#include +#include + +#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 +void VariantMap::parse_istream(std::istream& file) { + std::string variant, variant_template, key, value, input; + while (file.peek() != '[' && std::getline(file, input)) {} + + std::vector 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(attribs).parse(new Variant(*variants.find(variant_template)->second)) + : VariantParser(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 +void VariantMap::parse(std::string path) { + if (path.empty() || path == "") + return; + std::ifstream file(path); + if (!file.is_open()) + { + std::cerr << "Unable to open file " << path << std::endl; + return; + } + parse_istream(file); + file.close(); +} + +template void VariantMap::parse(std::string path); +template void VariantMap::parse(std::string path); + +void VariantMap::add(std::string s, const Variant* v) { + insert(std::pair(s, v)); +} + +void VariantMap::clear_all() { + for (auto const& element : *this) + delete element.second; + clear(); +} + +std::vector VariantMap::get_keys() { + std::vector keys; + for (auto const& element : *this) + keys.push_back(element.first); + return keys; +} ++ ++} // namespace Stockfish diff --cc src/variant.h index 9faeec9,0000000..9bb4aa0 mode 100644,000000..100644 --- a/src/variant.h +++ b/src/variant.h @@@ -1,231 -1,0 +1,234 @@@ +/* + 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 . +*/ + +#ifndef VARIANT_H_INCLUDED +#define VARIANT_H_INCLUDED + +#include +#include +#include +#include +#include + +#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 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 > 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 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 { +public: + void init(); + template void parse(std::string path); + template void parse_istream(std::istream& file); + void clear_all(); + std::vector get_keys(); + +private: + void add(std::string s, const Variant* v); +}; + +extern VariantMap variants; + ++} // namespace Stockfish ++ +#endif // #ifndef VARIANT_H_INCLUDED diff --cc src/xboard.cpp index f88ce58,0000000..b7ee4a4 mode 100644,000000..100644 --- a/src/xboard.cpp +++ b/src/xboard.cpp @@@ -1,481 -1,0 +1,485 @@@ +/* + 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 . +*/ + +#include +#include + +#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(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(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(pos); + std::vector 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() % 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 [] [] + 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 diff --cc src/xboard.h index 48f7679,0000000..f4f061b mode 100644,000000..100644 --- a/src/xboard.h +++ b/src/xboard.h @@@ -1,67 -1,0 +1,71 @@@ +/* + 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 . +*/ + +#ifndef XBOARD_H_INCLUDED +#define XBOARD_H_INCLUDED + +#include +#include +#include + +#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(); + 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 moveList; + Search::LimitsType limits; + Color playColor; + std::string ponderHighlight; +}; + +extern StateMachine* stateMachine; + +} // namespace XBoard + ++} // namespace Stockfish ++ +#endif // #ifndef XBOARD_H_INCLUDED