Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 22 May 2021 11:48:31 +0000 (13:48 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 22 May 2021 11:48:31 +0000 (13:48 +0200)
No functional change.

62 files changed:
1  2 
src/apiutil.h
src/benchmark.cpp
src/bitbase.cpp
src/bitboard.cpp
src/bitboard.h
src/endgame.cpp
src/endgame.h
src/evaluate.cpp
src/evaluate.h
src/ffishjs.cpp
src/magic.h
src/main.cpp
src/material.cpp
src/material.h
src/misc.cpp
src/misc.h
src/movegen.cpp
src/movepick.cpp
src/movepick.h
src/nnue/architectures/halfkp_256x2-32-32.h
src/nnue/architectures/halfkp_shogi_256x2-32-32.h
src/nnue/architectures/halfkp_variants_256x2-32-32.h
src/nnue/features/half_kp.cpp
src/nnue/features/half_kp.h
src/nnue/features/half_kp_shogi.cpp
src/nnue/features/half_kp_shogi.h
src/nnue/features/half_kp_variants.cpp
src/nnue/features/half_kp_variants.h
src/nnue/nnue_architecture.h
src/nnue/nnue_common.h
src/nnue/nnue_feature_transformer.h
src/parser.cpp
src/parser.h
src/partner.cpp
src/partner.h
src/pawns.cpp
src/piece.cpp
src/piece.h
src/position.cpp
src/position.h
src/psqt.cpp
src/psqt.h
src/pyffish.cpp
src/search.cpp
src/search.h
src/syzygy/tbprobe.cpp
src/thread.cpp
src/thread.h
src/thread_win32_osx.h
src/timeman.cpp
src/timeman.h
src/tt.cpp
src/tt.h
src/types.h
src/uci.cpp
src/uci.h
src/ucioption.cpp
src/variant.cpp
src/variant.h
src/xboard.cpp
src/xboard.h
tests/instrumented.sh

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