Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Sun, 31 Jan 2021 13:59:24 +0000 (14:59 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sun, 31 Jan 2021 13:59:24 +0000 (14:59 +0100)
bench: 4622210

54 files changed:
1  2 
.gitignore
src/Makefile_js
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/features/half_kp.cpp
src/nnue/features/half_kp.h
src/nnue/nnue_common.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/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/variants.ini
src/xboard.cpp
src/xboard.h

diff --cc .gitignore
@@@ -1,20 -1,12 +1,30 @@@
- stockfish
- *.o
- .depend
- *.nnue
+ # Files from build
+ **/*.o
+ **/*.s
+ src/.depend
+ # Built binary
+ src/stockfish*
+ src/-lstdc++.res
+ # Neural network for the NNUE evaluation
+ **/*.nnue
++
++# Build/test artifacts
 +*.exe
 +tests/syzygy
 +
 +# ffishjs
 +tests/js/node_modules
 +ffish.js
 +*.wasm
 +
 +# pyffish
 +venv
 +*.so
 +pyffish.egg-info
 +
 +# IDEs
 +.vscode
- .idea
++.idea
diff --cc src/Makefile_js
index 7e5ff9c,0000000..1e0c34d
mode 100644,000000..100644
--- /dev/null
@@@ -1,61 -1,0 +1,61 @@@
 +# ffish.js, a JavaScript chess variant library derived from Fairy-Stockfish
- # Copyright (C) 2020 Fabian Fichter, Johannes Czech
++# 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/>.
 +
 +EXE = ../tests/js/ffish.js
 +
 +SRCS = ffishjs.cpp benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp \
 +      material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
 +      search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
 +      nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp \
 +      partner.cpp parser.cpp piece.cpp variant.cpp xboard.cpp
 +
 +CXX=emcc
 +CXXFLAGS += --bind -DNNUE_EMBEDDING_OFF -DNO_THREADS -std=c++17 -Wall
 +
 +largeboards = yes
 +optimize = yes
 +debug = no
 +
 +### Debugging
 +ifeq ($(debug),no)
 +      CXXFLAGS += -DNDEBUG -s ASSERTIONS=0 -s SAFE_HEAP=0
 +else
 +      CXXFLAGS += -g -s ASSERTIONS=1 -s SAFE_HEAP=1
 +endif
 +
 +### Optimization
 +ifeq ($(optimize),yes)
 +      CXXFLAGS += -O3
 +endif
 +
 +# Compile version with support for large board variants
 +# Use precomputed magics by default
 +ifneq ($(largeboards),no)
 +      CXXFLAGS += -DLARGEBOARDS -DPRECOMPUTED_MAGICS -s TOTAL_MEMORY=32MB -s ALLOW_MEMORY_GROWTH=1 -s WASM_MEM_MAX=1GB
 +endif
 +
 +### Compile as ES6/ES2015 module
 +ifeq ($(es6),yes)
 +      CXXFLAGS += -s ENVIRONMENT='web,worker' -s EXPORT_ES6=1 -s MODULARIZE=1 -s USE_ES6_IMPORT_META=0
 +endif
 +
 +objclean:
 +      @rm -f $(EXE) *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o
 +
 +clean: objclean
 +
 +build:
 +      $(CXX) $(CXXFLAGS) $(SRCS) -o $(EXE)
diff --cc src/apiutil.h
index 57a5f11,0000000..dc9b8fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,830 -1,0 +1,830 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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 <vector>
 +#include <string>
 +#include <sstream>
 +#include <cctype>
 +#include <iostream>
 +#include <math.h>
 +
 +#include "types.h"
 +#include "variant.h"
 +
 +
 +namespace PSQT {
 +void init(const Variant* v);
 +}
 +
 +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,
 +};
 +
 +Notation default_notation(const Variant* v) {
 +    if (v->variantTemplate == "shogi")
 +        return NOTATION_SHOGI_HODGES_NUMBER;
 +    return NOTATION_SAN;
 +}
 +
 +enum Disambiguation {
 +    NO_DISAMBIGUATION,
 +    FILE_DISAMBIGUATION,
 +    RANK_DISAMBIGUATION,
 +    SQUARE_DISAMBIGUATION,
 +};
 +
 +bool is_shogi(Notation n) {
 +    return n == NOTATION_SHOGI_HOSKING || n == NOTATION_SHOGI_HODGES || n == NOTATION_SHOGI_HODGES_NUMBER;
 +}
 +
 +std::string piece(const Position& pos, Move m, Notation n) {
 +    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]));
 +}
 +
 +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)));
 +    }
 +}
 +
 +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);
 +    }
 +}
 +
 +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);
 +    }
 +}
 +
 +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;
 +}
 +
 +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 "";
 +    }
 +}
 +
 +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;
 +}
 +
 +bool hasInsufficientMaterial(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);
 +
 +    // 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))
 +        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))
 +        return false;
 +
 +    return true;
 +}
 +
 +namespace fen {
 +
 +enum FenValidation : int {
 +    FEN_MISSING_SPACE_DELIM = -12,
 +    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 rowIdx, int fileIdx) : rowIdx(rowIdx), fileIdx(fileIdx) {}
 +};
 +
 +bool operator==(const CharSquare& s1, const CharSquare& s2) {
 +    return s1.rowIdx == s2.rowIdx && s1.fileIdx == s2.fileIdx;
 +}
 +
 +bool operator!=(const CharSquare& s1, const CharSquare& s2) {
 +    return !(s1 == s2);
 +}
 +
 +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 nbRanks, int nbFiles) : nbRanks(nbRanks), nbFiles(nbFiles) {
 +        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);
 +};
 +
 +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;
 +}
 +
 +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 && validSpecialCharacters.find(c) == std::string::npos) {
 +            std::cerr << "Invalid piece character: '" << c << "'." << std::endl;
 +            return NOK;
 +        }
 +    }
 +    return OK;
 +}
 +
 +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
 +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;
 +}
 +
 +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;
 +}
 +
 +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";
 +    }
 +}
 +
 +Validation check_960_castling(const std::array<std::string, 2>& castlingInfoSplitted, const CharBoard& board, const std::array<CharSquare, 2>& kingPositionsStart) {
 +
 +    for (Color color : {WHITE, BLACK}) {
 +        for (char charPiece : {'K', 'R'}) {
 +            if (castlingInfoSplitted[color].size() == 0)
 +                continue;
 +            const Rank rank = Rank(kingPositionsStart[color].rowIdx);
 +            if (color == BLACK)
 +                charPiece = tolower(charPiece);
 +            if (!board.is_piece_on_rank(charPiece, rank)) {
 +                std::cerr << "The " << color_to_string(color) << " king and rook must be on rank " << rank << " if castling is enabled for " << color_to_string(color) << "." << std::endl;
 +                return NOK;
 +            }
 +        }
 +    }
 +    return OK;
 +}
 +
 +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";
 +    }
 +}
 +
 +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;
 +}
 +
 +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) {
 +
 +    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 = 'R'; // we don't use v->pieceToChar[ROOK]; here because in the newzealand_variant the ROOK is replaced by ROOKNI
 +            if (c == BLACK)
 +                rookChar = tolower(rookChar);
 +            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;
 +}
 +
 +Validation check_pocket_info(const std::string& fenBoard, int nbRanks, const Variant* v, std::array<std::string, 2>& pockets) {
 +
 +    char stopChar;
 +    int offset = 0;
 +    if (std::count(fenBoard.begin(), fenBoard.end(), '/') == nbRanks) {
 +        // look for last '/'
 +        stopChar = '/';
 +    }
 +    else {
 +        // pocket is defined as [ and ]
 +        stopChar = '[';
 +        offset = 1;
 +        if (*(fenBoard.end()-1) != ']') {
 +            std::cerr << "Pocket specification does not end with ']'." << std::endl;
 +            return NOK;
 +        }
 +    }
 +
 +    // 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) {
 +                std::cerr << "Invalid pocket piece: '" << c << "'." << std::endl;
 +                return NOK;
 +            }
 +            else {
 +                if (isupper(c))
 +                    pockets[WHITE] += tolower(c);
 +                else
 +                    pockets[BLACK] += c;
 +            }
 +        }
 +    }
 +    std::cerr << "Pocket piece closing character '" << stopChar << "' was not found." << std::endl;
 +    return NOK;
 +}
 +
 +Validation check_number_of_kings(const std::string& fenBoard, const Variant* v) {
 +    int nbWhiteKings = std::count(fenBoard.begin(), fenBoard.end(), toupper(v->pieceToChar[KING]));
 +    int nbBlackKings = std::count(fenBoard.begin(), fenBoard.end(), tolower(v->pieceToChar[KING]));
 +
 +    if (nbWhiteKings != 1) {
 +        std::cerr << "Invalid number of white kings. Expected: 1. Given: " << nbWhiteKings << std::endl;
 +        return NOK;
 +    }
 +    if (nbBlackKings != 1) {
 +        std::cerr << "Invalid number of black kings. Expected: 1. Given: " << nbBlackKings << std::endl;
 +        return NOK;
 +    }
 +    return OK;
 +}
 +
 +Validation check_en_passant_square(const std::string& enPassantInfo) {
 +    const char firstChar = enPassantInfo[0];
 +    if (firstChar != '-') {
 +        if (enPassantInfo.size() != 2) {
 +            std::cerr << "Invalid en-passant square '" << enPassantInfo << "'. Expects 2 characters. Actual: " << enPassantInfo.size() << " character(s)." << std::endl;
 +            return NOK;
 +        }
 +        if (isdigit(firstChar)) {
 +            std::cerr << "Invalid en-passant square '" << enPassantInfo << "'. Expects 1st character to be a digit." << std::endl;
 +            return NOK;
 +        }
 +        const char secondChar = enPassantInfo[1];
 +        if (!isdigit(secondChar)) {
 +            std::cerr << "Invalid en-passant square '" << enPassantInfo << "'. Expects 2nd character to be a non-digit." << std::endl;
 +            return NOK;
 +        }
 +    }
 +    return OK;
 +}
 +
 +bool no_king_piece_in_pockets(const std::array<std::string, 2>& pockets) {
 +    return pockets[WHITE].find('k') == std::string::npos && pockets[BLACK].find('k') == std::string::npos;
 +}
 +
 +FenValidation validate_fen(const std::string& fen, const Variant* v) {
 +
 +    const std::string validSpecialCharacters = "/+~[]-";
 +    // 0) Layout
 +    // check for empty fen
 +    if (fen.size() == 0) {
 +        std::cerr << "Fen is empty." << std::endl;
 +        return FEN_EMPTY;
 +    }
 +
 +    // check for space
 +    if (fen.find(' ') == std::string::npos) {
 +        std::cerr << "Fen misses space as delimiter." << std::endl;
 +        return FEN_MISSING_SPACE_DELIM;
 +    }
 +
 +    std::vector<std::string> fenParts = get_fen_parts(fen, ' ');
 +    std::vector<std::string> starFenParts = get_fen_parts(v->startFen, ' ');
 +    const unsigned int nbFenParts = starFenParts.size();
 +
 +    // check for number of parts
 +    if (fenParts.size() != nbFenParts) {
 +        std::cerr << "Invalid number of fen parts. Expected: " << nbFenParts << " 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::array<std::string, 2> pockets;
 +    if (v->pieceDrops) {
 +        if (check_pocket_info(fenParts[0], nbRanks, v, pockets) == NOK)
 +            return FEN_INVALID_POCKET_INFO;
 +    }
 +
 +    // check for number of kings (skip all extinction variants for this check (e.g. horde is a sepcial case where only one side has a royal king))
 +    if (v->pieceTypes.find(KING) != v->pieceTypes.end() && v->extinctionPieceTypes.size() == 0) {
 +        // we have a royal king in this variant, ensure that each side has exactly one king
 +        // (variants like giveaway use the COMMONER piece type instead)
 +        if (check_number_of_kings(fenParts[0], v) == NOK)
 +            return FEN_INVALID_NUMBER_OF_KINGS;
 +
 +        // if kings are still in pockets skip this check (e.g. placement_variant)
 +        if (no_king_piece_in_pockets(pockets)) {
 +            // check if kings are touching
 +            std::array<CharSquare, 2> kingPositions;
 +            // check if kings are touching
 +            kingPositions[WHITE] = board.get_square_for_piece(toupper(v->pieceToChar[KING]));
 +            kingPositions[BLACK] = board.get_square_for_piece(tolower(v->pieceToChar[KING]));
 +            if (check_touching_kings(board, kingPositions) == NOK)
 +                return FEN_TOUCHING_KINGS;
 +
 +            // 3) Part
 +            // check castling rights
 +            if (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) {
 +
 +                    CharBoard startBoard(board.get_nb_ranks(), board.get_nb_files());
 +                    fill_char_board(startBoard, v->startFen, validSpecialCharacters, v);
 +                    std::array<CharSquare, 2> kingPositionsStart;
 +                    kingPositionsStart[WHITE] = startBoard.get_square_for_piece(toupper(v->pieceToChar[KING]));
 +                    kingPositionsStart[BLACK] = startBoard.get_square_for_piece(tolower(v->pieceToChar[KING]));
 +
 +                    if (v->chess960) {
 +                        if (check_960_castling(castlingInfoSplitted, board, kingPositionsStart) == NOK)
 +                            return FEN_INVALID_CASTLING_INFO;
 +                    }
 +                    else {
 +                        std::array<std::vector<CharSquare>, 2> rookPositionsStart;
 +                        // we don't use v->pieceToChar[ROOK]; here because in the newzealand_variant the ROOK is replaced by ROOKNI
 +                        rookPositionsStart[WHITE] = startBoard.get_squares_for_piece('R');
 +                        rookPositionsStart[BLACK] = startBoard.get_squares_for_piece('r');
 +
 +                        if (check_standard_castling(castlingInfoSplitted, board, kingPositions, kingPositionsStart, rookPositionsStart) == NOK)
 +                            return FEN_INVALID_CASTLING_INFO;
 +                    }
 +                }
 +
 +            }
 +        }
 +    }
 +
 +    // 2) Part
 +    // check side to move char
 +    if (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;
 +    }
 +
 +    // 4) Part
 +    // check en-passant square
 +    if (v->doubleStep && v->pieceTypes.find(PAWN) != v->pieceTypes.end()) {
 +        if (check_en_passant_square(fenParts[3]) == NOK)
 +            return FEN_INVALID_EN_PASSANT_SQ;
 +    }
 +
 +    // 5) Part
 +    // checkCounting is skipped because if only one check is required to win it must not be part of the FEN (e.g. karouk_variant)
 +
 +    // 6) Part
 +    // check half move counter
 +    for (char c : fenParts[nbFenParts-2])
 +        if (!isdigit(c)) {
 +            std::cerr << "Invalid half move counter: '" << c << "'." << std::endl;
 +            return FEN_INVALID_HALF_MOVE_COUNTER;
 +        }
 +
 +    // 7) Part
 +    // check move counter
 +    for (char c : fenParts[nbFenParts-1])
 +        if (!isdigit(c)) {
 +            std::cerr << "Invalid move counter: '" << c << "'." << std::endl;
 +            return FEN_INVALID_MOVE_COUNTER;
 +        }
 +
 +    return FEN_OK;
 +}
 +}
 +
Simple merge
diff --cc src/bitbase.cpp
Simple merge
Simple merge
diff --cc src/bitboard.h
Simple merge
diff --cc src/endgame.cpp
Simple merge
diff --cc src/endgame.h
Simple merge
@@@ -800,20 -616,9 +801,20 @@@ namespace 
          score -= PawnlessFlank;
  
      // Penalty if king flank is under attack, potentially moving toward the king
 -    score -= FlankAttacks * kingFlankAttack;
 +    score -= FlankAttacks * kingFlankAttack * (1 + 5 * pos.captures_to_hand() + pos.check_counting());
 +
 +    if (pos.check_counting())
 +        score += make_score(0, mg_value(score) * 2 / (2 + pos.checks_remaining(Them)));
 +
 +    if (pos.king_type() == WAZIR)
 +        score += make_score(0, mg_value(score) / 2);
 +
 +    // For drop games, king danger is independent of game phase, but dependent on material density
 +    if (pos.captures_to_hand() || pos.two_boards())
 +        score = make_score(mg_value(score) * me->material_density() / 11000,
 +                           mg_value(score) * me->material_density() / 11000);
  
-     if (T)
+     if constexpr (T)
          Trace::add(KING, Us, score);
  
      return score;
              }
          } // r > RANK_3
  
 -        score += bonus - PassedFile * edge_distance(file_of(s));
 +        score += bonus - PassedFile * edge_distance(file_of(s), pos.max_file());
 +    }
 +
 +    // Scale by maximum promotion piece value
 +    Value maxMg = VALUE_ZERO, maxEg = VALUE_ZERO;
 +    for (PieceType pt : pos.promotion_piece_types())
 +    {
 +        maxMg = std::max(maxMg, PieceValue[MG][pt]);
 +        maxEg = std::max(maxEg, PieceValue[EG][pt]);
 +    }
 +    score = make_score(mg_value(score) * int(maxMg - PawnValueMg) / (QueenValueMg - PawnValueMg),
 +                       eg_value(score) * int(maxEg - PawnValueEg) / (QueenValueEg - PawnValueEg));
 +
 +    // Score passed shogi pawns
 +    PieceType pt = pos.promoted_piece_type(SHOGI_PAWN);
 +    if (pt != NO_PIECE_TYPE)
 +    {
 +        b = pos.pieces(Us, SHOGI_PAWN);
 +        while (b)
 +        {
 +            Square s = pop_lsb(&b);
 +            if ((pos.pieces(Them, SHOGI_PAWN) & forward_file_bb(Us, s)) || relative_rank(Us, s, pos.max_rank()) == pos.max_rank())
 +                continue;
 +
 +            Square blockSq = s + Up;
 +            int d = 2 * std::max(pos.promotion_rank() - relative_rank(Us, s, pos.max_rank()), 1);
 +            d += !!(attackedBy[Them][ALL_PIECES] & ~attackedBy2[Us] & blockSq);
 +            score += make_score(PieceValue[MG][pt], PieceValue[EG][pt]) / (d * d);
 +        }
      }
  
-     if (T)
+     if constexpr (T)
          Trace::add(PASSED, Us, score);
  
      return score;
      int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
      Score score = make_score(bonus * weight * weight / 16, 0);
  
 +    if (pos.capture_the_flag(Us))
 +        score += make_score(200, 200) * popcount(behind & safe & pos.capture_the_flag(Us));
 +
-     if (T)
+     if constexpr (T)
          Trace::add(SPACE, Us, score);
  
      return score;
    template<Tracing T>
    Value Evaluation<T>::winnable(Score score) const {
  
 -    int outflanking =  distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
 +    // No initiative bonus for extinction variants
 +    int complexity = 0;
 +    bool pawnsOnBothFlanks = true;
 +    if (pos.extinction_value() == VALUE_NONE && !pos.captures_to_hand() && !pos.connect_n() && !pos.material_counting())
 +    {
 +    int outflanking = !pos.count<KING>(WHITE) || !pos.count<KING>(BLACK) ? 0
 +                     :  distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
-                       - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
+                     + int(rank_of(pos.square<KING>(WHITE)) - rank_of(pos.square<KING>(BLACK)));
  
 -    bool pawnsOnBothFlanks =   (pos.pieces(PAWN) & QueenSide)
 +        pawnsOnBothFlanks =   (pos.pieces(PAWN) & QueenSide)
                              && (pos.pieces(PAWN) & KingSide);
  
      bool almostUnwinnable =   outflanking < 0
@@@ -1483,8 -1020,9 +1486,8 @@@ make_v
      Value v = winnable(score);
  
      // In case of tracing add all remaining individual evaluation terms
-     if (T)
+     if constexpr (T)
      {
 -        Trace::add(MATERIAL, pos.psq_score());
          Trace::add(IMBALANCE, me->imbalance());
          Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK));
          Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
@@@ -1522,15 -1053,8 +1525,15 @@@ Value Eval::evaluate(const Position& po
    {
        // Scale and shift NNUE for compatibility with search and classical evaluation
        auto  adjusted_NNUE = [&](){
-          int mat = pos.non_pawn_material() + PawnValueMg * pos.count<PAWN>();
+          int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count<PAWN>();
 -         return NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo;
 +         int v2 = VALUE_ZERO;
 +         if (pos.check_counting())
 +         {
 +             Color us = pos.side_to_move();
 +             v2 =  mat / (30 * pos.checks_remaining( us))
 +                 - mat / (30 * pos.checks_remaining(~us));
 +         }
-          return NNUE::evaluate(pos) * (679 + mat / 32) / 1024 + Tempo + v2;
++         return NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo + v2;
        };
  
        // If there is PSQ imbalance use classical eval, with small probability if it is small
diff --cc src/evaluate.h
Simple merge
diff --cc src/ffishjs.cpp
index d9b765c,0000000..d615140
mode 100644,000000..100644
--- /dev/null
@@@ -1,627 -1,0 +1,627 @@@
 +/*
 +  ffish.js, a JavaScript chess variant library derived from Fairy-Stockfish
-   Copyright (C) 2020 Fabian Fichter, Johannes Czech
++  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;
 +
 +
 +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;
 +}
 +
 +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 += 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();
 +  }
 +
 +  void push(std::string uciMove) {
 +    do_move(UCI::to_move(this->pos, uciMove));
 +  }
 +
 +  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 == move_to_san(this->pos, move, notation)) {
 +        foundMove = move;
 +        break;
 +      }
 +    }
 +    if (foundMove != MOVE_NONE) {
 +      do_move(foundMove);
 +      return true;
 +    }
 +    return false;
 +  }
 +
 +  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 move_to_san()
 +  std::string san_move(std::string uciMove) {
 +    return move_to_san(this->pos, UCI::to_move(this->pos, uciMove), NOTATION_SAN);
 +  }
 +
 +  std::string san_move(std::string uciMove, Notation notation) {
 +    return 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, ' ')) {
 +      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 += 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 += 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();
 +  }
 +
 +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) {
 +    const Variant* v = get_variant(uciVariant);
 +    return fen::validate_fen(fen, v);
 +  }
 +
 +  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) {
 +    if (pgn[curIdx] == '{') {
 +      // skip comment
 +      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 (!skip_comment(pgn, curIdx, lineEnd))
 +          return game;
 +
 +        // 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));
 +          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);
 +  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));
 +  // TODO: enable to string conversion method
 +  // .class_function("getStringFromInstance", &Board::get_string_from_instance);
 +}
diff --cc src/magic.h
index d24fe46,0000000..d8f305a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1005 -1,0 +1,1005 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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
 +
 +#ifdef PRECOMPUTED_MAGICS
 +#define B(a, b) (Bitboard(a) << 64) ^ Bitboard(b)
 +  // Use precomputed magics if pext is not avaible,
 +  // since the magics generation is very slow.
 +  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),
 +  };
 +  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)
 +  };
 +  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)
 +  };
 +  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),
 +  };
 +  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),
 +  };
 +  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),
 +  };
 +  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),
 +  };
 +  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),
 +  };
 +#undef B
 +#endif
 +
 +#endif // #ifndef MAGIC_H_INCLUDED
diff --cc src/main.cpp
  #include "thread.h"
  #include "tt.h"
  #include "uci.h"
 +#include "piece.h"
 +#include "variant.h"
- #include "syzygy/tbprobe.h"
- namespace PSQT {
-   void init(const Variant* v);
- }
 +
  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
Simple merge
diff --cc src/movegen.cpp
@@@ -182,46 -125,16 +182,46 @@@ namespace 
          Bitboard b3 = shift<Up     >(pawnsOn7) & emptySquares;
  
          while (b1)
 -            moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq);
 +            moveList = make_promotions<Us, Type, UpRight>(pos, moveList, pop_lsb(&b1));
  
          while (b2)
 -            moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(&b2), ksq);
 +            moveList = make_promotions<Us, Type, UpLeft >(pos, moveList, pop_lsb(&b2));
  
          while (b3)
 -            moveList = make_promotions<Type, Up     >(moveList, pop_lsb(&b3), ksq);
 +            moveList = make_promotions<Us, Type, Up     >(pos, moveList, pop_lsb(&b3));
 +    }
 +
 +    // Sittuyin promotions
 +    if (pos.sittuyin_promotion() && (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS))
 +    {
 +        Bitboard pawns = pos.pieces(Us, PAWN);
 +        // Pawns need to be on diagonals on opponent's half if there is more than one pawn
 +        if (pos.count<PAWN>(Us) > 1)
 +            pawns &=  (  PseudoAttacks[Us][BISHOP][make_square(FILE_A, relative_rank(Us, RANK_1, pos.max_rank()))]
 +                       | PseudoAttacks[Us][BISHOP][make_square(pos.max_file(), relative_rank(Us, RANK_1, pos.max_rank()))])
 +                    & forward_ranks_bb(Us, relative_rank(Us, Rank((pos.max_rank() - 1) / 2), pos.max_rank()));
 +        while (pawns)
 +        {
 +            Square from = pop_lsb(&pawns);
 +            for (PieceType pt : pos.promotion_piece_types())
 +            {
 +                if (pos.promotion_limit(pt) && pos.promotion_limit(pt) <= pos.count(Us, pt))
 +                    continue;
 +                Bitboard b = (pos.attacks_from(Us, pt, from) & ~pos.pieces()) | from;
 +                if (Type == EVASIONS)
 +                    b &= target;
 +
 +                while (b)
 +                {
 +                    Square to = pop_lsb(&b);
 +                    if (!(attacks_bb(Us, pt, to, pos.pieces() ^ from) & pos.pieces(Them)))
 +                        *moveList++ = make<PROMOTION>(from, to, pt);
 +                }
 +            }
 +        }
      }
  
-     // Standard and en-passant captures
+     // Standard and en passant captures
      if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
      {
          Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
  
          if (pos.ep_square() != SQ_NONE)
          {
 -            assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
 +            assert(relative_rank(Them, rank_of(pos.ep_square()), pos.max_rank()) <= Rank(pos.double_step_rank_max() + 1));
  
-             // An en passant capture can be an evasion only if the checking piece
-             // is the double pushed pawn and so is in the target. Otherwise this
-             // is a discovery check and we are forced to do otherwise.
-             if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
+             // An en passant capture cannot resolve a discovered check.
+             if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
                  return moveList;
  
              b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
    }
  
  
-   template<Color Us, bool Checks>
-   ExtMove* generate_moves(const Position& pos, ExtMove* moveList, PieceType pt, Bitboard target) {
 -  template<PieceType Pt, bool Checks>
 -  ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) {
++  template<bool Checks>
++  ExtMove* generate_moves(const Position& pos, ExtMove* moveList, PieceType Pt, Bitboard piecesToMove, Bitboard target) {
++
++    assert(Pt != KING && Pt != PAWN);
 -    static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
++    Color us = pos.side_to_move();
  
-     assert(pt != KING && pt != PAWN);
+     Bitboard bb = piecesToMove & pos.pieces(Pt);
  
-     Bitboard bb = pos.pieces(Us, pt);
+     if (!bb)
+         return moveList;
+     [[maybe_unused]] const Bitboard checkSquares = pos.check_squares(Pt);
  
      while (bb) {
          Square from = pop_lsb(&bb);
  
-         // Avoid generating discovered checks twice
-         if (Checks && (pos.blockers_for_king(~Us) & from))
-             continue;
-         Bitboard b1 = (  (pos.attacks_from(Us, pt, from) & pos.pieces())
-                        | (pos.moves_from(Us, pt, from) & ~pos.pieces())) & target;
-         PieceType prom_pt = pos.promoted_piece_type(pt);
-         Bitboard b2 = prom_pt && (!pos.promotion_limit(prom_pt) || pos.promotion_limit(prom_pt) > pos.count(Us, prom_pt)) ? b1 : Bitboard(0);
 -        Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
 -        if constexpr (Checks)
 -            b &= checkSquares;
++        Bitboard b1 = (  (pos.attacks_from(us, Pt, from) & pos.pieces())
++                       | (pos.moves_from(us, Pt, from) & ~pos.pieces())) & target;
++        PieceType promPt = pos.promoted_piece_type(Pt);
++        Bitboard b2 = promPt && (!pos.promotion_limit(promPt) || pos.promotion_limit(promPt) > pos.count(us, promPt)) ? b1 : Bitboard(0);
 +        Bitboard b3 = pos.piece_demotion() && pos.is_promoted(from) ? b1 : Bitboard(0);
  
 -        while (b)
 -            *moveList++ = make_move(from, pop_lsb(&b));
 +        if (Checks)
 +        {
-             b1 &= pos.check_squares(pt);
++            b1 &= checkSquares;
 +            if (b2)
-                 b2 &= pos.check_squares(pos.promoted_piece_type(pt));
++                b2 &= pos.check_squares(pos.promoted_piece_type(Pt));
 +            if (b3)
 +                b3 &= pos.check_squares(type_of(pos.unpromoted_piece_on(from)));
 +        }
 +
 +        // Restrict target squares considering promotion zone
 +        if (b2 | b3)
 +        {
-             Bitboard promotion_zone = zone_bb(Us, pos.promotion_rank(), pos.max_rank());
++            Bitboard promotion_zone = zone_bb(us, pos.promotion_rank(), pos.max_rank());
 +            if (pos.mandatory_piece_promotion())
 +                b1 &= (promotion_zone & from ? Bitboard(0) : ~promotion_zone) | (pos.piece_promotion_on_capture() ? ~pos.pieces() : Bitboard(0));
 +            // Exclude quiet promotions/demotions
 +            if (pos.piece_promotion_on_capture())
 +            {
 +                b2 &= pos.pieces();
 +                b3 &= pos.pieces();
 +            }
 +            // Consider promotions/demotions into promotion zone
 +            if (!(promotion_zone & from))
 +            {
 +                b2 &= promotion_zone;
 +                b3 &= promotion_zone;
 +            }
 +        }
 +
 +        while (b1)
-             moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, from, pop_lsb(&b1));
++            moveList = make_move_and_gating<NORMAL>(pos, moveList, us, from, pop_lsb(&b1));
 +
 +        // Shogi-style piece promotions
 +        while (b2)
 +            *moveList++ = make<PIECE_PROMOTION>(from, pop_lsb(&b2));
 +
 +        // Piece demotions
 +        while (b3)
 +            *moveList++ = make<PIECE_DEMOTION>(from, pop_lsb(&b3));
      }
  
      return moveList;
          case NON_EVASIONS:
              target = ~pos.pieces(Us);
              break;
-         default:
-             static_assert(true, "Unsupported type in generate_all()");
      }
 +    target &= pos.board_bb();
  
      moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
 -    moveList = generate_moves<KNIGHT, Checks>(pos, moveList, piecesToMove, target);
 -    moveList = generate_moves<BISHOP, Checks>(pos, moveList, piecesToMove, target);
 -    moveList = generate_moves<  ROOK, Checks>(pos, moveList, piecesToMove, target);
 -    moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target);
 -
 -    if (Type != QUIET_CHECKS && Type != EVASIONS)
 +    for (PieceType pt : pos.piece_types())
 +        if (pt != PAWN && pt != KING)
-             moveList = generate_moves<Us, Checks>(pos, moveList, pt, target);
++            moveList = generate_moves<Checks>(pos, moveList, pt, piecesToMove, target);
 +    // generate drops
 +    if (pos.piece_drops() && Type != CAPTURES && pos.count_in_hand(Us, ALL_PIECES))
 +        for (PieceType pt : pos.piece_types())
 +            moveList = generate_drops<Us, Checks>(pos, moveList, pt, target & ~pos.pieces(~Us));
 +
 +    if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count<KING>(Us))
      {
          Square ksq = pos.square<KING>(Us);
 -        Bitboard b = attacks_bb<KING>(ksq) & target;
 +        Bitboard b = (  (pos.attacks_from(Us, KING, ksq) & pos.pieces())
 +                      | (pos.moves_from(Us, KING, ksq) & ~pos.pieces())) & target;
          while (b)
 -            *moveList++ = make_move(ksq, pop_lsb(&b));
 +            moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, ksq, pop_lsb(&b));
 +
 +        // Passing move by king
 +        if (pos.pass())
 +            *moveList++ = make<SPECIAL>(ksq, ksq);
  
          if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING))
              for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
Simple merge
diff --cc src/movepick.h
@@@ -84,7 -84,7 +84,7 @@@ enum StatsType { NoCaptures, Captures }
  /// unsuccessful during the current search, and is used for reduction and move
  /// ordering decisions. It uses 2 tables (one for each color) indexed by
  /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
- typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB + 1) * int(1 << SQUARE_BITS)> ButterflyHistory;
 -typedef Stats<int16_t, 13365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
++typedef Stats<int16_t, 13365, COLOR_NB, int(SQUARE_NB + 1) * int(1 << SQUARE_BITS)> ButterflyHistory;
  
  /// At higher depths LowPlyHistory records successful quiet moves near the root
  /// and quiet moves which are/were in the PV (ttPv). It is cleared with each new
@@@ -105,16 -105,15 +105,16 @@@ typedef Stats<int16_t, 29952, 2 * PIECE
  /// ContinuationHistory is the combined history of a given pair of moves, usually
  /// the current one given a previous one. The nested history table is based on
  /// PieceToHistory instead of ButterflyBoards.
 -typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
 +typedef Stats<PieceToHistory, NOT_USED, 2 * PIECE_SLOTS, SQUARE_NB> ContinuationHistory;
  
 +int history_slot(Piece pc);
  
- /// MovePicker class is used to pick one pseudo legal move at a time from the
+ /// MovePicker class is used to pick one pseudo-legal move at a time from the
  /// current position. The most important method is next_move(), which returns a
- /// new pseudo legal move each time it is called, until there are no moves left,
- /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
- /// beta algorithm, MovePicker attempts to return the moves which are most likely
- /// to get a cut-off first.
+ /// new pseudo-legal move each time it is called, until there are no moves left,
+ /// when MOVE_NONE is returned. In order to improve the efficiency of the
+ /// alpha-beta algorithm, MovePicker attempts to return the moves which are most
+ /// likely to get a cut-off first.
  class MovePicker {
  
    enum PickType { Next, Best };
Simple merge
Simple merge
Simple merge
diff --cc src/parser.cpp
index d45c763,0000000..ddb29c1
mode 100644,000000..100644
--- /dev/null
@@@ -1,388 -1,0 +1,388 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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 "parser.h"
 +#include "piece.h"
 +#include "types.h"
 +
 +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 = 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 (const auto& pieceInfo : pieceMap)
 +    {
 +        // piece char
 +        const auto& keyValue = config.find(pieceInfo.second->name);
 +        if (keyValue != config.end() && !keyValue->second.empty())
 +        {
 +            if (isalpha(keyValue->second.at(0)))
 +                v->add_piece(pieceInfo.first, keyValue->second.at(0));
 +            else
 +            {
 +                if (DoCheck && keyValue->second.at(0) != '-')
 +                    std::cerr << pieceInfo.second->name << " - Invalid letter: " << keyValue->second.at(0) << std::endl;
 +                v->remove_piece(pieceInfo.first);
 +            }
 +        }
 +        // mobility region
 +        std::string capitalizedPiece = pieceInfo.second->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][pieceInfo.first]);
 +        }
 +    }
 +    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("endgameEval", v->endgameEval);
 +    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("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("shogiDoubledPawn", v->shogiDoubledPawn);
 +    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);
 +    // 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)
 +    {
 +        // startFen
 +        std::string fenBoard = v->startFen.substr(0, v->startFen.find(' '));
 +        std::stringstream ss(fenBoard);
 +        char token;
 +        ss >> std::noskipws;
 +
 +        while (ss >> token)
 +        {
 +            if (token == '+')
 +            {
 +                ss >> token;
 +                size_t idx = v->pieceToChar.find(toupper(token));
 +                if (idx == std::string::npos || !v->promotedPieceType[idx])
 +                    std::cerr << "startFen - Invalid piece type: +" << token << std::endl;
 +            }
 +            else if (isalpha(token) && v->pieceToChar.find(toupper(token)) == std::string::npos)
 +                std::cerr << "startFen - Invalid piece type: " << token << std::endl;
 +        }
 +
 +        // pieceToCharTable
 +        if (v->pieceToCharTable != "-")
 +        {
 +            ss = std::stringstream(v->pieceToCharTable);
 +            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;
 +            }
 +        }
 +
 +        // 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;
 +        }
 +
 +    }
 +    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);
diff --cc src/parser.h
index a5eca49,0000000..247027a
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,55 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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"
 +
 +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);
 +};
 +
 +#endif // #ifndef PARSER_H_INCLUDED
diff --cc src/partner.cpp
index 21bed35,0000000..306b6e2
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,154 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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"
 +
 +PartnerHandler Partner; // Global object
 +
 +void PartnerHandler::reset() {
 +    fast = sitRequested = partnerDead = weDead = weWin = 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 : 0;
 +    }
 +    else if (token == "otim")
 +    {
 +        int value;
 +        opptime = (is >> value) ? value : 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&);
diff --cc src/partner.h
index 25c4f49,0000000..01b68e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,51 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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 "position.h"
 +
 +/// 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;
 +    std::atomic<int> time, opptime;
 +    Move moveRequested;
 +};
 +
 +extern PartnerHandler Partner;
 +
 +#endif // #ifndef PARTNER_H_INCLUDED
diff --cc src/pawns.cpp
@@@ -30,14 -30,15 +30,15 @@@ namespace 
    #define S(mg, eg) make_score(mg, eg)
  
    // Pawn penalties
-   constexpr Score Backward      = S( 8, 25);
-   constexpr Score Doubled       = S(10, 55);
-   constexpr Score Isolated      = S( 3, 15);
-   constexpr Score WeakLever     = S( 3, 55);
-   constexpr Score WeakUnopposed = S(13, 25);
+   constexpr Score Backward      = S( 6, 23);
+   constexpr Score Doubled       = S(13, 53);
+   constexpr Score DoubledEarly  = S(20, 10);
+   constexpr Score Isolated      = S( 2, 15);
+   constexpr Score WeakLever     = S( 5, 57);
+   constexpr Score WeakUnopposed = S(16, 22);
  
    // Bonus for blocked pawns at 5th or 6th rank
 -  constexpr Score BlockedPawn[2] = { S(-15, -3), S(-6, 3) };
 +  constexpr Score BlockedPawn[RANK_NB - 5] = { S(-15, -3), S(-6, 3) };
  
    constexpr Score BlockedStorm[RANK_NB] = {
      S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2)
  
    // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
    // for king when the king is on a semi-open or open file.
-   constexpr Score KingOnFile[2][2] = {{ S(-19,12), S(-6, 7)  },
-                                      {  S(  0, 2), S( 6,-5) }};
+   constexpr Score KingOnFile[2][2] = {{ S(-21,10), S(-7, 1)  },
+                                      {  S(  0,-3), S( 9,-4) }};
  
 +
 +  // Variant bonuses
 +  constexpr int HordeConnected[2][RANK_NB] = {{   5, 10,  20, 55, 55, 100, 80 },
 +                                              { -10,  5, -10,  5, 25,  40, 30 }};
 +
    #undef S
    #undef V
  
  
          // Flag the pawn
          opposed    = theirPawns & forward_file_bb(Us, s);
 -        blocked    = theirPawns & (s + Up);
 +        blocked    = is_ok(s + Up) ? theirPawns & (s + Up) : Bitboard(0);
          stoppers   = theirPawns & passed_pawn_span(Us, s);
          lever      = theirPawns & pawn_attacks_bb(Us, s);
 -        leverPush  = theirPawns & pawn_attacks_bb(Us, s + Up);
 -        doubled    = ourPawns   & (s - Up);
 +        leverPush  = relative_rank(Them, s, pos.max_rank()) > RANK_1 ? theirPawns & pawn_attacks_bb(Us, s + Up) : Bitboard(0);
 +        doubled    = r > RANK_1 ? ourPawns & (s - Up) : Bitboard(0);
          neighbours = ourPawns   & adjacent_files_bb(s);
          phalanx    = neighbours & rank_bb(s);
 -        support    = neighbours & rank_bb(s - Up);
 +        support    = r > RANK_1 ? neighbours & rank_bb(s - Up) : Bitboard(0);
  
+         if (doubled)
+         {
+             // Additional doubled penalty if none of their pawns is fixed
+             if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
+                 score -= DoubledEarly;
+         }
          // A pawn is backward when it is behind all pawns of the same color on
          // the adjacent files and cannot safely advance.
 -        backward =  !(neighbours & forward_ranks_bb(Them, s + Up))
 -                  && (leverPush | blocked);
 +        backward =   is_ok(s + Up)
 +                  && !(neighbours & forward_ranks_bb(Them, s + Up))
 +                  && (stoppers & blocked);
  
          // Compute additional span if pawn is not backward nor blocked
          if (!backward && !blocked)
diff --cc src/piece.cpp
index 515e2ae,0000000..48bd240
mode 100644,000000..100644
--- /dev/null
@@@ -1,435 -1,0 +1,435 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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 "types.h"
 +#include "piece.h"
 +
 +PieceMap pieceMap; // Global object
 +
 +void PieceInfo::merge(const PieceInfo* pi) {
 +    stepsQuiet.insert(stepsQuiet.end(), pi->stepsQuiet.begin(), pi->stepsQuiet.end());
 +    stepsCapture.insert(stepsCapture.end(), pi->stepsCapture.begin(), pi->stepsCapture.end());
 +    sliderQuiet.insert(sliderQuiet.end(), pi->sliderQuiet.begin(), pi->sliderQuiet.end());
 +    sliderCapture.insert(sliderCapture.end(), pi->sliderCapture.begin(), pi->sliderCapture.end());
 +}
 +
 +namespace {
 +  PieceInfo* pawn_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "pawn";
 +      p->betza = "fmWfceF";
 +      p->stepsQuiet = {NORTH};
 +      p->stepsCapture = {NORTH_WEST, NORTH_EAST};
 +      return p;
 +  }
 +  PieceInfo* knight_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "knight";
 +      p->betza = "N";
 +      p->stepsQuiet = {2 * SOUTH + WEST, 2 * SOUTH + EAST, SOUTH + 2 * WEST, SOUTH + 2 * EAST,
 +                       NORTH + 2 * WEST, NORTH + 2 * EAST, 2 * NORTH + WEST, 2 * NORTH + EAST };
 +      p->stepsCapture = {2 * SOUTH + WEST, 2 * SOUTH + EAST, SOUTH + 2 * WEST, SOUTH + 2 * EAST,
 +                         NORTH + 2 * WEST, NORTH + 2 * EAST, 2 * NORTH + WEST, 2 * NORTH + EAST };
 +      return p;
 +  }
 +  PieceInfo* bishop_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "bishop";
 +      p->betza = "B";
 +      p->sliderQuiet = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
 +      p->sliderCapture = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
 +      return p;
 +  }
 +  PieceInfo* rook_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "rook";
 +      p->betza = "R";
 +      p->sliderQuiet = {NORTH, EAST, SOUTH, WEST};
 +      p->sliderCapture = {NORTH, EAST, SOUTH, WEST};
 +      return p;
 +  }
 +  PieceInfo* queen_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "queen";
 +      p->betza = "RB";
 +      p->sliderQuiet = {NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
 +      p->sliderCapture = {NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
 +      return p;
 +  }
 +  PieceInfo* king_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "king";
 +      p->betza = "K";
 +      p->stepsQuiet = {SOUTH_WEST, SOUTH, SOUTH_EAST, WEST, EAST, NORTH_WEST, NORTH, NORTH_EAST};
 +      p->stepsCapture = {SOUTH_WEST, SOUTH, SOUTH_EAST, WEST, EAST, NORTH_WEST, NORTH, NORTH_EAST};
 +      return p;
 +  }
 +  PieceInfo* commoner_piece() {
 +      PieceInfo* p = king_piece();
 +      p->name = "commoner";
 +      return p;
 +  }
 +  PieceInfo* fers_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "fers";
 +      p->betza = "F";
 +      p->stepsQuiet = {SOUTH_WEST, SOUTH_EAST, NORTH_WEST, NORTH_EAST};
 +      p->stepsCapture = {SOUTH_WEST, SOUTH_EAST, NORTH_WEST, NORTH_EAST};
 +      return p;
 +  }
 +  PieceInfo* wazir_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "wazir";
 +      p->betza = "W";
 +      p->stepsQuiet = {SOUTH, WEST, EAST, NORTH};
 +      p->stepsCapture = {SOUTH, WEST, EAST, NORTH};
 +      return p;
 +  }
 +  PieceInfo* alfil_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "alfil";
 +      p->betza = "A";
 +      p->stepsQuiet = {2 * SOUTH_WEST, 2 * SOUTH_EAST, 2 * NORTH_WEST, 2 * NORTH_EAST};
 +      p->stepsCapture = {2 * SOUTH_WEST, 2 * SOUTH_EAST, 2 * NORTH_WEST, 2 * NORTH_EAST};
 +      return p;
 +  }
 +  PieceInfo* fers_alfil_piece() {
 +      PieceInfo* p = fers_piece();
 +      p->name = "fersAlfil";
 +      p->betza = "FA";
 +      PieceInfo* p2 = alfil_piece();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* silver_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "silver";
 +      p->betza = "FfW";
 +      p->stepsQuiet = {SOUTH_WEST, SOUTH_EAST, NORTH_WEST, NORTH, NORTH_EAST};
 +      p->stepsCapture = {SOUTH_WEST, SOUTH_EAST, NORTH_WEST, NORTH, NORTH_EAST};
 +      return p;
 +  }
 +  PieceInfo* aiwok_piece() {
 +      PieceInfo* p = rook_piece();
 +      p->name = "aiwok";
 +      p->betza = "RNF";
 +      PieceInfo* p2 = knight_piece();
 +      PieceInfo* p3 = fers_piece();
 +      p->merge(p2);
 +      p->merge(p3);
 +      delete p2;
 +      delete p3;
 +      return p;
 +  }
 +  PieceInfo* bers_piece() {
 +      PieceInfo* p = rook_piece();
 +      p->name = "bers";
 +      p->betza = "RF";
 +      PieceInfo* p2 = fers_piece();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* archbishop_piece() {
 +      PieceInfo* p = bishop_piece();
 +      p->name = "archbishop";
 +      p->betza = "BN";
 +      PieceInfo* p2 = knight_piece();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* chancellor_piece() {
 +      PieceInfo* p = rook_piece();
 +      p->name = "chancellor";
 +      p->betza = "RN";
 +      PieceInfo* p2 = knight_piece();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* amazon_piece() {
 +      PieceInfo* p = queen_piece();
 +      p->name = "amazon";
 +      p->betza = "RBN";
 +      PieceInfo* p2 = knight_piece();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* knibis_piece() {
 +      PieceInfo* p = bishop_piece();
 +      p->name = "knibis";
 +      p->betza = "mNcB";
 +      p->sliderQuiet.clear();
 +      PieceInfo* p2 = knight_piece();
 +      p2->stepsCapture.clear();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* biskni_piece() {
 +      PieceInfo* p = bishop_piece();
 +      p->name = "biskni";
 +      p->betza = "mBcN";
 +      p->sliderCapture.clear();
 +      PieceInfo* p2 = knight_piece();
 +      p2->stepsQuiet.clear();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* kniroo_piece() {
 +      PieceInfo* p = rook_piece();
 +      p->name = "kniroo";
 +      p->betza = "mNcR";
 +      p->sliderQuiet.clear();
 +      PieceInfo* p2 = knight_piece();
 +      p2->stepsCapture.clear();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* rookni_piece() {
 +      PieceInfo* p = rook_piece();
 +      p->name = "rookni";
 +      p->betza = "mRcN";
 +      p->sliderCapture.clear();
 +      PieceInfo* p2 = knight_piece();
 +      p2->stepsQuiet.clear();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* shogi_pawn_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "shogiPawn";
 +      p->betza = "fW";
 +      p->stepsQuiet = {NORTH};
 +      p->stepsCapture = {NORTH};
 +      return p;
 +  }
 +  PieceInfo* lance_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "lance";
 +      p->betza = "fR";
 +      p->sliderQuiet = {NORTH};
 +      p->sliderCapture = {NORTH};
 +      return p;
 +  }
 +  PieceInfo* shogi_knight_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "shogiKnight";
 +      p->betza = "fN";
 +      p->stepsQuiet = {2 * NORTH + WEST, 2 * NORTH + EAST};
 +      p->stepsCapture = {2 * NORTH + WEST, 2 * NORTH + EAST};
 +      return p;
 +  }
 +  PieceInfo* euroshogi_knight_piece() {
 +      PieceInfo* p = shogi_knight_piece();
 +      p->name = "euroshogiKnight";
 +      p->betza = "fNsW";
 +      p->stepsQuiet.push_back(WEST);
 +      p->stepsQuiet.push_back(EAST);
 +      p->stepsCapture.push_back(WEST);
 +      p->stepsCapture.push_back(EAST);
 +      return p;
 +  }
 +  PieceInfo* gold_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "gold";
 +      p->betza = "WfF";
 +      p->stepsQuiet = {SOUTH, WEST, EAST, NORTH_WEST, NORTH, NORTH_EAST};
 +      p->stepsCapture = {SOUTH, WEST, EAST, NORTH_WEST, NORTH, NORTH_EAST};
 +      return p;
 +  }
 +  PieceInfo* dragon_horse_piece() {
 +      PieceInfo* p = bishop_piece();
 +      p->name = "dragonHorse";
 +      p->betza = "BW";
 +      PieceInfo* p2 = wazir_piece();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +  PieceInfo* clobber_piece() {
 +      PieceInfo* p = wazir_piece();
 +      p->name = "clobber";
 +      p->betza = "cW";
 +      p->stepsQuiet.clear();
 +      return p;
 +  }
 +  PieceInfo* breakthrough_piece() {
 +      PieceInfo* p = pawn_piece();
 +      p->name = "breakthrough";
 +      p->betza = "fWfFcF";
 +      p->stepsQuiet.push_back(NORTH_WEST);
 +      p->stepsQuiet.push_back(NORTH_EAST);
 +      return p;
 +  }
 +  PieceInfo* immobile_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "immobile";
 +      return p;
 +  }
 +  PieceInfo* ataxx_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "ataxx";
 +      p->betza = "mDNA";
 +      p->stepsQuiet = {2 * NORTH_WEST, 2 * NORTH + WEST, 2 * NORTH, 2 * NORTH + EAST, 2 * NORTH_EAST,
 +                       NORTH + 2 * WEST, NORTH + 2 * EAST, 2 * WEST, 2 * EAST, SOUTH + 2 * WEST, SOUTH + 2 * EAST,
 +                       2 * SOUTH_WEST, 2 * SOUTH + WEST, 2 * SOUTH, 2 * SOUTH + EAST, 2 * SOUTH_EAST};
 +      return p;
 +  }
 +  PieceInfo* quiet_queen_piece() {
 +      PieceInfo* p = queen_piece();
 +      p->name = "quietQueen";
 +      p->betza = "mQ";
 +      p->sliderCapture.clear();
 +      return p;
 +  }
 +  PieceInfo* cannon_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "cannon";
 +      p->betza = "mRcpR";
 +      p->sliderQuiet = {NORTH, EAST, SOUTH, WEST};
 +      p->hopperCapture = {NORTH, EAST, SOUTH, WEST};
 +      return p;
 +  }
 +  PieceInfo* janggi_cannon_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "janggiCannon";
 +      p->betza = "pR";
 +      p->hopperQuiet = {NORTH, EAST, SOUTH, WEST};
 +      p->hopperCapture = {NORTH, EAST, SOUTH, WEST};
 +      return p;
 +  }
 +  PieceInfo* soldier_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "soldier";
 +      p->betza = "fsW";
 +      p->stepsQuiet = {NORTH, WEST, EAST};
 +      p->stepsCapture = {NORTH, WEST, EAST};
 +      return p;
 +  }
 +  PieceInfo* horse_piece() {
 +      PieceInfo* p = knight_piece();
 +      p->name = "horse";
 +      p->betza = "nN";
 +      p->lameLeaper = true;
 +      return p;
 +  }
 +  PieceInfo* elephant_piece() {
 +      PieceInfo* p = alfil_piece();
 +      p->name = "elephant";
 +      p->betza = "nA";
 +      p->lameLeaper = true;
 +      return p;
 +  }
 +  PieceInfo* janggi_elephant_piece() {
 +      PieceInfo* p = new PieceInfo();
 +      p->name = "janggiElephant";
 +      p->betza = "mafsmafW";
 +      p->stepsQuiet = {SOUTH + 2 * SOUTH_WEST, SOUTH + 2 * SOUTH_EAST,
 +                       WEST  + 2 * SOUTH_WEST, EAST  + 2 * SOUTH_EAST,
 +                       WEST  + 2 * NORTH_WEST, EAST  + 2 * NORTH_EAST,
 +                       NORTH + 2 * NORTH_WEST, NORTH + 2 * NORTH_EAST};
 +      p->stepsCapture = {SOUTH + 2 * SOUTH_WEST, SOUTH + 2 * SOUTH_EAST,
 +                         WEST  + 2 * SOUTH_WEST, EAST  + 2 * SOUTH_EAST,
 +                         WEST  + 2 * NORTH_WEST, EAST  + 2 * NORTH_EAST,
 +                         NORTH + 2 * NORTH_WEST, NORTH + 2 * NORTH_EAST};
 +      p->lameLeaper = true;
 +      return p;
 +  }
 +  PieceInfo* banner_piece() {
 +      PieceInfo* p = rook_piece();
 +      p->name = "banner";
 +      p->betza = "RcpRnN";
 +      PieceInfo* p2 = horse_piece();
 +      p->merge(p2);
 +      delete p2;
 +      p->hopperCapture = {NORTH, EAST, SOUTH, WEST};
 +      p->lameLeaper = true;
 +      return p;
 +  }
 +  PieceInfo* centaur_piece() {
 +      PieceInfo* p = commoner_piece();
 +      p->name = "centaur";
 +      p->betza = "KN";
 +      PieceInfo* p2 = knight_piece();
 +      p->merge(p2);
 +      delete p2;
 +      return p;
 +  }
 +}
 +
 +void PieceMap::init() {
 +  add(PAWN, pawn_piece());
 +  add(KNIGHT, knight_piece());
 +  add(BISHOP, bishop_piece());
 +  add(ROOK, rook_piece());
 +  add(QUEEN, queen_piece());
 +  add(FERS, fers_piece());
 +  add(ALFIL, alfil_piece());
 +  add(FERS_ALFIL, fers_alfil_piece());
 +  add(SILVER, silver_piece());
 +  add(AIWOK, aiwok_piece());
 +  add(BERS, bers_piece());
 +  add(ARCHBISHOP, archbishop_piece());
 +  add(CHANCELLOR, chancellor_piece());
 +  add(AMAZON, amazon_piece());
 +  add(KNIBIS, knibis_piece());
 +  add(BISKNI, biskni_piece());
 +  add(KNIROO, kniroo_piece());
 +  add(ROOKNI, rookni_piece());
 +  add(SHOGI_PAWN, shogi_pawn_piece());
 +  add(LANCE, lance_piece());
 +  add(SHOGI_KNIGHT, shogi_knight_piece());
 +  add(EUROSHOGI_KNIGHT, euroshogi_knight_piece());
 +  add(GOLD, gold_piece());
 +  add(DRAGON_HORSE, dragon_horse_piece());
 +  add(CLOBBER_PIECE, clobber_piece());
 +  add(BREAKTHROUGH_PIECE, breakthrough_piece());
 +  add(IMMOBILE_PIECE, immobile_piece());
 +  add(ATAXX_PIECE, ataxx_piece());
 +  add(QUIET_QUEEN, quiet_queen_piece());
 +  add(CANNON, cannon_piece());
 +  add(JANGGI_CANNON, janggi_cannon_piece());
 +  add(SOLDIER, soldier_piece());
 +  add(HORSE, horse_piece());
 +  add(ELEPHANT, elephant_piece());
 +  add(JANGGI_ELEPHANT, janggi_elephant_piece());
 +  add(BANNER, banner_piece());
 +  add(WAZIR, wazir_piece());
 +  add(COMMONER, commoner_piece());
 +  add(CENTAUR, centaur_piece());
 +  add(KING, king_piece());
 +}
 +
 +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();
 +}
diff --cc src/piece.h
index c93e1d5,0000000..61670bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,53 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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 <vector>
 +
 +#include "types.h"
 +
 +
 +/// PieceInfo struct stores information about the piece movements.
 +
 +struct PieceInfo {
 +  std::string name = "";
 +  std::string betza = "";
 +  std::vector<Direction> stepsQuiet = {};
 +  std::vector<Direction> stepsCapture = {};
 +  std::vector<Direction> sliderQuiet = {};
 +  std::vector<Direction> sliderCapture = {};
 +  std::vector<Direction> hopperQuiet = {};
 +  std::vector<Direction> hopperCapture = {};
 +  bool lameLeaper = false;
 +
 +  void merge(const PieceInfo* pi);
 +};
 +
 +struct PieceMap : public std::map<PieceType, const PieceInfo*> {
 +  void init();
 +  void add(PieceType pt, const PieceInfo* v);
 +  void clear_all();
 +};
 +
 +extern PieceMap pieceMap;
 +
 +#endif // #ifndef PIECE_H_INCLUDED
@@@ -929,9 -505,9 +929,9 @@@ bool Position::legal(Move m) const 
    // En passant captures are a tricky special case. Because they are rather
    // uncommon, we do it simply by testing whether the king is attacked after
    // the move is made.
-   if (type_of(m) == ENPASSANT)
+   if (type_of(m) == EN_PASSANT)
    {
 -      Square ksq = square<KING>(us);
 +      Square ksq = count<KING>(us) ? square<KING>(us) : SQ_NONE;
        Square capsq = to - pawn_push(us);
        Bitboard occupied = (pieces() ^ from ^ capsq) | to;
  
@@@ -1252,16 -715,8 +1252,16 @@@ void Position::do_move(Move m, StateInf
    Color them = ~us;
    Square from = from_sq(m);
    Square to = to_sq(m);
 -  Piece pc = piece_on(from);
 +  Piece pc = moved_piece(m);
-   Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
+   Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
 +  if (to == from)
 +  {
 +      assert((type_of(m) == PROMOTION && sittuyin_promotion()) || (is_pass(m) && pass()));
 +      captured = NO_PIECE;
 +  }
 +  st->capturedpromoted = is_promoted(to);
 +  st->unpromotedCapturedPiece = captured ? unpromoted_piece_on(to) : NO_PIECE;
 +  st->pass = is_pass(m);
  
    assert(color_of(pc) == us);
    assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
        }
  
        // Update board and piece lists
 +      bool capturedPromoted = is_promoted(capsq);
 +      Piece unpromotedCaptured = unpromoted_piece_on(capsq);
        remove_piece(capsq);
-       if (type_of(m) == ENPASSANT)
+       if (type_of(m) == EN_PASSANT)
            board[capsq] = NO_PIECE;
 +      if (captures_to_hand())
 +      {
 +          Piece pieceToHand = !capturedPromoted || drop_loop() ? ~captured
 +                             : unpromotedCaptured ? ~unpromotedCaptured
 +                                                  : make_piece(~color_of(captured), PAWN);
 +          add_to_hand(pieceToHand);
 +          k ^=  Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)] - 1]
 +              ^ Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]];
 +      }
  
        // Update material hash key and prefetch access to materialTable
        k ^= Zobrist::psq[captured][capsq];
    // If the moving piece is a pawn do some special extra work
    if (type_of(pc) == PAWN)
    {
-       // Set en-passant square if the moved pawn can be captured
+       // Set en passant square if the moved pawn can be captured
 -      if (   (int(to) ^ int(from)) == 16
 +      if (   type_of(m) != DROP
 +          && std::abs(int(to) - int(from)) == 2 * NORTH
 +          && (var->enPassantRegion & (to - pawn_push(us)))
            && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
        {
            st->epSquare = to - pawn_push(us);
@@@ -1990,8 -1067,8 +1991,8 @@@ bool Position::see_ge(Move m, Value thr
  
    assert(is_ok(m));
  
-   // Only deal with normal moves, assume others pass a simple see
+   // Only deal with normal moves, assume others pass a simple SEE
 -  if (type_of(m) != NORMAL)
 +  if (type_of(m) != NORMAL && type_of(m) != DROP && type_of(m) != PIECE_PROMOTION)
        return VALUE_ZERO >= threshold;
  
    Square from = from_sq(m), to = to_sq(m);
diff --cc src/position.h
  
  #include "bitboard.h"
  #include "evaluate.h"
+ #include "psqt.h"
  #include "types.h"
 +#include "variant.h"
 +#include "movegen.h"
  
  #include "nnue/nnue_accumulator.h"
  
@@@ -319,513 -198,11 +320,509 @@@ private
    Score psq;
    Thread* thisThread;
    StateInfo* st;
 +
 +  // variant-specific
 +  const Variant* var;
 +  bool tsumeMode;
    bool chess960;
 +  int pieceCountInHand[COLOR_NB][PIECE_TYPE_NB];
 +  Bitboard promotedPieces;
 +  void add_to_hand(Piece pc);
 +  void remove_from_hand(Piece pc);
 +  void drop_piece(Piece pc_hand, Piece pc_drop, Square s);
 +  void undrop_piece(Piece pc_hand, Square s);
  };
  
- namespace PSQT {
-   extern Score psq[PIECE_NB][SQUARE_NB + 1];
- }
  extern std::ostream& operator<<(std::ostream& os, const Position& pos);
  
 +inline const Variant* Position::variant() const {
 +  assert(var != nullptr);
 +  return var;
 +}
 +
 +inline Rank Position::max_rank() const {
 +  assert(var != nullptr);
 +  return var->maxRank;
 +}
 +
 +inline File Position::max_file() const {
 +  assert(var != nullptr);
 +  return var->maxFile;
 +}
 +
 +inline bool Position::two_boards() const {
 +  assert(var != nullptr);
 +  return var->twoBoards;
 +}
 +
 +inline Bitboard Position::board_bb() const {
 +  assert(var != nullptr);
 +  return board_size_bb(var->maxFile, var->maxRank);
 +}
 +
 +inline Bitboard Position::board_bb(Color c, PieceType pt) const {
 +  assert(var != nullptr);
 +  return var->mobilityRegion[c][pt] ? var->mobilityRegion[c][pt] & board_bb() : board_bb();
 +}
 +
 +inline const std::set<PieceType>& Position::piece_types() const {
 +  assert(var != nullptr);
 +  return var->pieceTypes;
 +}
 +
 +inline const std::string& Position::piece_to_char() const {
 +  assert(var != nullptr);
 +  return var->pieceToChar;
 +}
 +
 +inline const std::string& Position::piece_to_char_synonyms() const {
 +  assert(var != nullptr);
 +  return var->pieceToCharSynonyms;
 +}
 +
 +inline Rank Position::promotion_rank() const {
 +  assert(var != nullptr);
 +  return var->promotionRank;
 +}
 +
 +inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types() const {
 +  assert(var != nullptr);
 +  return var->promotionPieceTypes;
 +}
 +
 +inline bool Position::sittuyin_promotion() const {
 +  assert(var != nullptr);
 +  return var->sittuyinPromotion;
 +}
 +
 +inline int Position::promotion_limit(PieceType pt) const {
 +  assert(var != nullptr);
 +  return var->promotionLimit[pt];
 +}
 +
 +inline PieceType Position::promoted_piece_type(PieceType pt) const {
 +  assert(var != nullptr);
 +  return var->promotedPieceType[pt];
 +}
 +
 +inline bool Position::piece_promotion_on_capture() const {
 +  assert(var != nullptr);
 +  return var->piecePromotionOnCapture;
 +}
 +
 +inline bool Position::mandatory_pawn_promotion() const {
 +  assert(var != nullptr);
 +  return var->mandatoryPawnPromotion;
 +}
 +
 +inline bool Position::mandatory_piece_promotion() const {
 +  assert(var != nullptr);
 +  return var->mandatoryPiecePromotion;
 +}
 +
 +inline bool Position::piece_demotion() const {
 +  assert(var != nullptr);
 +  return var->pieceDemotion;
 +}
 +
 +inline bool Position::blast_on_capture() const {
 +  assert(var != nullptr);
 +  return var->blastOnCapture;
 +}
 +
 +inline bool Position::endgame_eval() const {
 +  assert(var != nullptr);
 +  return var->endgameEval && !count_in_hand(WHITE, ALL_PIECES) && !count_in_hand(BLACK, ALL_PIECES);
 +}
 +
 +inline bool Position::double_step_enabled() const {
 +  assert(var != nullptr);
 +  return var->doubleStep;
 +}
 +
 +inline Rank Position::double_step_rank_max() const {
 +  assert(var != nullptr);
 +  return var->doubleStepRank;
 +}
 +
 +inline Rank Position::double_step_rank_min() const {
 +  assert(var != nullptr);
 +  return var->doubleStepRankMin;
 +}
 +
 +inline bool Position::castling_enabled() const {
 +  assert(var != nullptr);
 +  return var->castling;
 +}
 +
 +inline bool Position::castling_dropped_piece() const {
 +  assert(var != nullptr);
 +  return var->castlingDroppedPiece;
 +}
 +
 +inline File Position::castling_kingside_file() const {
 +  assert(var != nullptr);
 +  return var->castlingKingsideFile;
 +}
 +
 +inline File Position::castling_queenside_file() const {
 +  assert(var != nullptr);
 +  return var->castlingQueensideFile;
 +}
 +
 +inline Rank Position::castling_rank(Color c) const {
 +  assert(var != nullptr);
 +  return relative_rank(c, var->castlingRank, max_rank());
 +}
 +
 +inline PieceType Position::castling_rook_piece() const {
 +  assert(var != nullptr);
 +  return var->castlingRookPiece;
 +}
 +
 +inline PieceType Position::king_type() const {
 +  assert(var != nullptr);
 +  return var->kingType;
 +}
 +
 +inline bool Position::checking_permitted() const {
 +  assert(var != nullptr);
 +  return var->checking;
 +}
 +
 +inline bool Position::drop_checks() const {
 +  assert(var != nullptr);
 +  return var->dropChecks;
 +}
 +
 +inline bool Position::must_capture() const {
 +  assert(var != nullptr);
 +  return var->mustCapture;
 +}
 +
 +inline bool Position::has_capture() const {
 +  return st->legalCapture == VALUE_TRUE || (st->legalCapture == NO_VALUE && MoveList<CAPTURES>(*this).size());
 +}
 +
 +inline bool Position::must_drop() const {
 +  assert(var != nullptr);
 +  return var->mustDrop;
 +}
 +
 +inline bool Position::piece_drops() const {
 +  assert(var != nullptr);
 +  return var->pieceDrops;
 +}
 +
 +inline bool Position::drop_loop() const {
 +  assert(var != nullptr);
 +  return var->dropLoop;
 +}
 +
 +inline bool Position::captures_to_hand() const {
 +  assert(var != nullptr);
 +  return var->capturesToHand;
 +}
 +
 +inline bool Position::first_rank_pawn_drops() const {
 +  assert(var != nullptr);
 +  return var->firstRankPawnDrops;
 +}
 +
 +inline bool Position::drop_on_top() const {
 +  assert(var != nullptr);
 +  return var->dropOnTop;
 +}
 +
 +inline EnclosingRule Position::enclosing_drop() const {
 +  assert(var != nullptr);
 +  return var->enclosingDrop;
 +}
 +
 +inline Bitboard Position::drop_region(Color c) const {
 +  assert(var != nullptr);
 +  return c == WHITE ? var->whiteDropRegion : var->blackDropRegion;
 +}
 +
 +inline Bitboard Position::drop_region(Color c, PieceType pt) const {
 +  Bitboard b = drop_region(c) & board_bb(c, pt);
 +
 +  // Connect4-style drops
 +  if (drop_on_top())
 +      b &= shift<NORTH>(pieces()) | Rank1BB;
 +  // Pawns on back ranks
 +  if (pt == PAWN)
 +  {
 +      if (!var->promotionZonePawnDrops)
 +          b &= ~zone_bb(c, promotion_rank(), max_rank());
 +      if (!first_rank_pawn_drops())
 +          b &= ~rank_bb(relative_rank(c, RANK_1, max_rank()));
 +  }
 +  // Doubled shogi pawns
 +  if (pt == SHOGI_PAWN && !shogi_doubled_pawn())
 +      for (File f = FILE_A; f <= max_file(); ++f)
 +          if (file_bb(f) & pieces(c, pt))
 +              b &= ~file_bb(f);
 +  // Sittuyin rook drops
 +  if (pt == ROOK && sittuyin_rook_drop())
 +      b &= rank_bb(relative_rank(c, RANK_1, max_rank()));
 +
 +  // Filter out squares where the drop does not enclose at least one opponent's piece
 +  if (enclosing_drop())
 +  {
 +      // Reversi start
 +      if (var->enclosingDropStart & ~pieces())
 +          b &= var->enclosingDropStart;
 +      else
 +      {
 +          if (enclosing_drop() == REVERSI)
 +          {
 +              Bitboard theirs = pieces(~c);
 +              b &=  shift<NORTH     >(theirs) | shift<SOUTH     >(theirs)
 +                  | shift<NORTH_EAST>(theirs) | shift<SOUTH_WEST>(theirs)
 +                  | shift<EAST      >(theirs) | shift<WEST      >(theirs)
 +                  | shift<SOUTH_EAST>(theirs) | shift<NORTH_WEST>(theirs);
 +              Bitboard b2 = b;
 +              while (b2)
 +              {
 +                  Square s = pop_lsb(&b2);
 +                  if (!(attacks_bb(c, QUEEN, s, board_bb() & ~pieces(~c)) & ~PseudoAttacks[c][KING][s] & pieces(c)))
 +                      b ^= s;
 +              }
 +          }
 +          else
 +          {
 +              assert(enclosing_drop() == ATAXX);
 +              Bitboard ours = pieces(c);
 +              b &=  shift<NORTH     >(ours) | shift<SOUTH     >(ours)
 +                  | shift<NORTH_EAST>(ours) | shift<SOUTH_WEST>(ours)
 +                  | shift<EAST      >(ours) | shift<WEST      >(ours)
 +                  | shift<SOUTH_EAST>(ours) | shift<NORTH_WEST>(ours);
 +          }
 +      }
 +  }
 +
 +  return b;
 +}
 +
 +inline bool Position::sittuyin_rook_drop() const {
 +  assert(var != nullptr);
 +  return var->sittuyinRookDrop;
 +}
 +
 +inline bool Position::drop_opposite_colored_bishop() const {
 +  assert(var != nullptr);
 +  return var->dropOppositeColoredBishop;
 +}
 +
 +inline bool Position::drop_promoted() const {
 +  assert(var != nullptr);
 +  return var->dropPromoted;
 +}
 +
 +inline bool Position::shogi_doubled_pawn() const {
 +  assert(var != nullptr);
 +  return var->shogiDoubledPawn;
 +}
 +
 +inline bool Position::immobility_illegal() const {
 +  assert(var != nullptr);
 +  return var->immobilityIllegal;
 +}
 +
 +inline bool Position::gating() const {
 +  assert(var != nullptr);
 +  return var->gating;
 +}
 +
 +inline bool Position::arrow_gating() const {
 +  assert(var != nullptr);
 +  return var->arrowGating;
 +}
 +
 +inline bool Position::seirawan_gating() const {
 +  assert(var != nullptr);
 +  return var->seirawanGating;
 +}
 +
 +inline bool Position::cambodian_moves() const {
 +  assert(var != nullptr);
 +  return var->cambodianMoves;
 +}
 +
 +inline Bitboard Position::diagonal_lines() const {
 +  assert(var != nullptr);
 +  return var->diagonalLines;
 +}
 +
 +inline bool Position::pass() const {
 +  assert(var != nullptr);
 +  return var->pass || var->passOnStalemate;
 +}
 +
 +inline bool Position::pass_on_stalemate() const {
 +  assert(var != nullptr);
 +  return var->passOnStalemate;
 +}
 +
 +inline Bitboard Position::promoted_soldiers(Color c) const {
 +  assert(var != nullptr);
 +  return pieces(c, SOLDIER) & zone_bb(c, var->soldierPromotionRank, max_rank());
 +}
 +
 +inline bool Position::makpong() const {
 +  assert(var != nullptr);
 +  return var->makpongRule;
 +}
 +
 +inline int Position::n_move_rule() const {
 +  assert(var != nullptr);
 +  return var->nMoveRule;
 +}
 +
 +inline int Position::n_fold_rule() const {
 +  assert(var != nullptr);
 +  return var->nFoldRule;
 +}
 +
 +inline EnclosingRule Position::flip_enclosed_pieces() const {
 +  assert(var != nullptr);
 +  return var->flipEnclosedPieces;
 +}
 +
 +inline Value Position::stalemate_value(int ply) const {
 +  assert(var != nullptr);
 +  if (var->stalematePieceCount)
 +  {
 +      int c = count<ALL_PIECES>(sideToMove) - count<ALL_PIECES>(~sideToMove);
 +      return c == 0 ? VALUE_DRAW : convert_mate_value(c < 0 ? var->stalemateValue : -var->stalemateValue, ply);
 +  }
 +  return convert_mate_value(var->stalemateValue, ply);
 +}
 +
 +inline Value Position::checkmate_value(int ply) const {
 +  assert(var != nullptr);
 +  // Check for illegal mate by shogi pawn drop
 +  if (    var->shogiPawnDropMateIllegal
 +      && !(checkers() & ~pieces(SHOGI_PAWN))
 +      && !st->capturedPiece
 +      &&  st->pliesFromNull > 0
 +      && (st->materialKey != st->previous->materialKey))
 +  {
 +      return mate_in(ply);
 +  }
 +  // Check for shatar mate rule
 +  if (var->shatarMateRule)
 +  {
 +      // Mate by knight is illegal
 +      if (!(checkers() & ~pieces(KNIGHT)))
 +          return mate_in(ply);
 +
 +      StateInfo* stp = st;
 +      while (stp->checkersBB)
 +      {
 +          // Return mate score if there is at least one shak in series of checks
 +          if (stp->shak)
 +              return convert_mate_value(var->checkmateValue, ply);
 +
 +          if (stp->pliesFromNull < 2)
 +              break;
 +
 +          stp = stp->previous->previous;
 +      }
 +      // Niol
 +      return VALUE_DRAW;
 +  }
 +  // Return mate value
 +  return convert_mate_value(var->checkmateValue, ply);
 +}
 +
 +inline Value Position::extinction_value(int ply) const {
 +  assert(var != nullptr);
 +  return convert_mate_value(var->extinctionValue, ply);
 +}
 +
 +inline bool Position::extinction_claim() const {
 +  assert(var != nullptr);
 +  return var->extinctionClaim;
 +}
 +
 +inline const std::set<PieceType>& Position::extinction_piece_types() const {
 +  assert(var != nullptr);
 +  return var->extinctionPieceTypes;
 +}
 +
 +inline bool Position::extinction_single_piece() const {
 +  assert(var != nullptr);
 +  return   var->extinctionValue == -VALUE_MATE
 +        && std::any_of(var->extinctionPieceTypes.begin(),
 +                       var->extinctionPieceTypes.end(),
 +                       [](PieceType pt) { return pt != ALL_PIECES; });
 +}
 +
 +inline int Position::extinction_piece_count() const {
 +  assert(var != nullptr);
 +  return var->extinctionPieceCount;
 +}
 +
 +inline int Position::extinction_opponent_piece_count() const {
 +  assert(var != nullptr);
 +  return var->extinctionOpponentPieceCount;
 +}
 +
 +inline PieceType Position::capture_the_flag_piece() const {
 +  assert(var != nullptr);
 +  return var->flagPiece;
 +}
 +
 +inline Bitboard Position::capture_the_flag(Color c) const {
 +  assert(var != nullptr);
 +  return c == WHITE ? var->whiteFlag : var->blackFlag;
 +}
 +
 +inline bool Position::flag_move() const {
 +  assert(var != nullptr);
 +  return var->flagMove;
 +}
 +
 +inline bool Position::check_counting() const {
 +  assert(var != nullptr);
 +  return var->checkCounting;
 +}
 +
 +inline int Position::connect_n() const {
 +  assert(var != nullptr);
 +  return var->connectN;
 +}
 +
 +inline CheckCount Position::checks_remaining(Color c) const {
 +  return st->checksRemaining[c];
 +}
 +
 +inline MaterialCounting Position::material_counting() const {
 +  assert(var != nullptr);
 +  return var->materialCounting;
 +}
 +
 +inline CountingRule Position::counting_rule() const {
 +  assert(var != nullptr);
 +  return var->countingRule;
 +}
 +
 +inline bool Position::is_immediate_game_end() const {
 +  Value result;
 +  return is_immediate_game_end(result);
 +}
 +
 +inline bool Position::is_optional_game_end() const {
 +  Value result;
 +  return is_optional_game_end(result);
 +}
 +
 +inline bool Position::is_game_end(Value& result, int ply) const {
 +  return is_immediate_game_end(result, ply) || is_optional_game_end(result, ply);
 +}
 +
  inline Color Position::side_to_move() const {
    return sideToMove;
  }
@@@ -1011,8 -301,8 +1008,8 @@@ inline Bitboard Position::check_squares
    return st->checkSquares[pt];
  }
  
- inline bool Position::is_discovery_check_on_king(Color c, Move m) const {
+ inline bool Position::is_discovered_check_on_king(Color c, Move m) const {
 -  return st->blockersForKing[c] & from_sq(m);
 +  return is_ok(from_sq(m)) && st->blockersForKing[c] & from_sq(m);
  }
  
  inline bool Position::pawn_passed(Color c, Square s) const {
  }
  
  inline bool Position::advanced_pawn_push(Move m) const {
 -  return   type_of(moved_piece(m)) == PAWN
 -        && relative_rank(sideToMove, to_sq(m)) > RANK_5;
 +  return  (   type_of(moved_piece(m)) == PAWN
 +           && relative_rank(sideToMove, to_sq(m), max_rank()) > (max_rank() + 1) / 2)
-         || type_of(m) == ENPASSANT;
++        || type_of(m) == EN_PASSANT;
  }
  
  inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
@@@ -1081,13 -363,13 +1079,13 @@@ inline bool Position::is_chess960() con
  
  inline bool Position::capture_or_promotion(Move m) const {
    assert(is_ok(m));
-   return type_of(m) == PROMOTION || type_of(m) == ENPASSANT || (type_of(m) != CASTLING && !empty(to_sq(m)));
 -  return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
++  return type_of(m) == PROMOTION || type_of(m) == EN_PASSANT || (type_of(m) != CASTLING && !empty(to_sq(m)));
  }
  
  inline bool Position::capture(Move m) const {
    assert(is_ok(m));
    // Castling is encoded as "king captures rook"
-   return (!empty(to_sq(m)) && type_of(m) != CASTLING && from_sq(m) != to_sq(m)) || type_of(m) == ENPASSANT;
 -  return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT;
++  return (!empty(to_sq(m)) && type_of(m) != CASTLING && from_sq(m) != to_sq(m)) || type_of(m) == EN_PASSANT;
  }
  
  inline Piece Position::captured_piece() const {
diff --cc src/psqt.cpp
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
  
+ #include "psqt.h"
  #include <algorithm>
 +#include <sstream>
  
- #include "types.h"
  #include "bitboard.h"
+ #include "types.h"
 +#include "piece.h"
 +#include "variant.h"
 +#include "misc.h"
 +
 +Value EvalPieceValue[PHASE_NB][PIECE_NB];
 +Value CapturePieceValue[PHASE_NB][PIECE_NB];
 +
- namespace PSQT {
 +
- #define S(mg, eg) make_score(mg, eg)
  
- // Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece
- // type on a given square a (middlegame, endgame) score pair is assigned. Table
- // is defined for files A..D and white side: it is symmetric for black side and
- // second half of the files.
+ namespace
+ {
+ auto constexpr S = make_score;
+ // 'Bonus' contains Piece-Square parameters.
+ // Scores are explicit for files A to D, implicitly mirrored for E to H.
 -constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
 +constexpr Score Bonus[PIECE_TYPE_NB][RANK_NB][int(FILE_NB) / 2] = {
    { },
    { },
    { // Knight
@@@ -103,9 -98,13 +108,13 @@@ constexpr Score PBonus[RANK_NB][FILE_NB
     { S( -7,  0), S(  7,-11), S( -3, 12), S(-13, 21), S(  5, 25), S(-16, 19), S( 10,  4), S( -8,  7) }
    };
  
- #undef S
+ } // namespace
+ namespace PSQT
+ {
  
 -Score psq[PIECE_NB][SQUARE_NB];
 +Score psq[PIECE_NB][SQUARE_NB + 1];
  
  // PSQT::init() initializes piece-square tables: the white halves of the tables are
  // copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
diff --cc src/psqt.h
index 0000000,8b4fd6e..c0030de
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,38 +1,39 @@@
+ /*
+   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/>.
+ */
+ #ifndef PSQT_H_INCLUDED
+ #define PSQT_H_INCLUDED
+ #include "types.h"
++#include "variant.h"
+ namespace 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
+ #endif // PSQT_H_INCLUDED
diff --cc src/search.cpp
@@@ -913,15 -829,10 +903,15 @@@ namespace 
                 ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE
                 : ss->staticEval > (ss-2)->staticEval;
  
 +    // Skip early pruning in case of mandatory capture
 +    if (pos.must_capture() && pos.has_capture())
 +        goto moves_loop;
 +
-     // Step 8. Futility pruning: child node (~50 Elo)
+     // Step 7. Futility pruning: child node (~50 Elo)
      if (   !PvNode
-         &&  depth < 8
+         &&  depth < 9
 -        &&  eval - futility_margin(depth, improving) >= beta
 +        && !(pos.capture_the_flag_piece() && !pos.checking_permitted())
 +        &&  eval - futility_margin(depth, improving) * (1 + pos.check_counting() + 2 * pos.must_capture() + pos.extinction_single_piece()) >= beta
          &&  eval < VALUE_KNOWN_WIN) // Do not return unproven wins
          return eval;
  
          }
      }
  
-     probCutBeta = beta + (183 + 20 * !!pos.capture_the_flag_piece() + 50 * pos.captures_to_hand()) * (1 + pos.check_counting() + pos.extinction_single_piece()) - 49 * improving;
 -    probCutBeta = beta + 194 - 49 * improving;
++    probCutBeta = beta + (194 + 20 * !!pos.capture_the_flag_piece() + 50 * pos.captures_to_hand()) * (1 + pos.check_counting() + pos.extinction_single_piece()) - 49 * improving;
  
-     // Step 10. ProbCut (~10 Elo)
+     // Step 9. ProbCut (~10 Elo)
      // If we have a good enough capture and a reduced search returns a value
      // much above beta, we can (almost) safely prune the previous move.
      if (   !PvNode
@@@ -1119,9 -1028,9 +1109,9 @@@ moves_loop: // When in check, search st
        // Calculate new depth for this move
        newDepth = depth - 1;
  
-       // Step 13. Pruning at shallow depth (~200 Elo)
+       // Step 12. Pruning at shallow depth (~200 Elo)
        if (  !rootNode
 -          && pos.non_pawn_material(us)
 +          && (pos.non_pawn_material(us) || pos.count<ALL_PIECES>(us) == pos.count<PAWN>(us))
            && bestValue > VALUE_TB_LOSS_IN_MAX_PLY)
        {
            // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold
            // Reduced depth of the next LMR search
            int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0);
  
-           if (   !captureOrPromotion
-               && !givesCheck
-               && !(pos.must_capture() && pos.attackers_to(to_sq(move), ~us)))
++          if (pos.must_capture() && (captureOrPromotion || givesCheck || pos.attackers_to(to_sq(move), ~us)))
++          {}
++          else
++
+           if (   captureOrPromotion
+               || givesCheck)
+           {
+               // Capture history based pruning when the move doesn't give check
+               if (   !givesCheck
+                   && lmrDepth < 1
+                   && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
+                   continue;
+               // SEE based pruning
 -              if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo)
++              if (!pos.see_ge(move, Value(-218 - 120 * pos.captures_to_hand()) * depth)) // (~25 Elo)
+                   continue;
+           }
+           else
            {
                // Countermoves based pruning (~20 Elo)
                if (   lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1)
                // Futility pruning: parent node (~5 Elo)
                if (   lmrDepth < 7
                    && !ss->inCheck
 -                  && ss->staticEval + 254 + 159 * lmrDepth <= alpha
 -                  &&  (*contHist[0])[movedPiece][to_sq(move)]
 -                    + (*contHist[1])[movedPiece][to_sq(move)]
 -                    + (*contHist[3])[movedPiece][to_sq(move)]
 -                    + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 26394)
 +                  && !(   pos.extinction_value() == -VALUE_MATE
 +                       && pos.extinction_piece_types().find(ALL_PIECES) == pos.extinction_piece_types().end())
-                   && ss->staticEval + (266 + 170 * lmrDepth) * (1 + pos.check_counting()) <= alpha
++                  && ss->staticEval + (254 + 159 * lmrDepth) * (1 + pos.check_counting()) <= alpha
 +                  &&  (*contHist[0])[history_slot(movedPiece)][to_sq(move)]
 +                    + (*contHist[1])[history_slot(movedPiece)][to_sq(move)]
 +                    + (*contHist[3])[history_slot(movedPiece)][to_sq(move)]
-                     + (*contHist[5])[history_slot(movedPiece)][to_sq(move)] / 2 < 27376)
++                    + (*contHist[5])[history_slot(movedPiece)][to_sq(move)] / 2 < 26394)
                    continue;
  
                // Prune moves with negative SEE (~20 Elo)
 -              if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth)))
 +              if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18) + 10 * !!pos.capture_the_flag_piece()) * lmrDepth * lmrDepth)))
                    continue;
            }
-           else if (!pos.must_capture())
-           {
-               // Capture history based pruning when the move doesn't give check
-               if (   !givesCheck
-                   && lmrDepth < 1
-                   && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
-                   continue;
-               // SEE based pruning
-               if (!pos.see_ge(move, Value(-213 - 120 * pos.captures_to_hand()) * depth)) // (~25 Elo)
-                   continue;
-           }
        }
  
-       // Step 14. Extensions (~75 Elo)
+       // Step 13. Extensions (~75 Elo)
  
        // Singular extension search (~70 Elo). If all moves but one fail low on a
        // search of (alpha-s, beta-s), and just one fails high on (alpha, beta),
                 && pos.non_pawn_material() <= 2 * RookValueMg)
            extension = 1;
  
-       // Late irreversible move extension
-       if (   move == ttMove
-           && pos.rule50_count() > 80
-           && (captureOrPromotion || type_of(movedPiece) == PAWN))
-           extension = 2;
 +      // Losing chess capture extension
 +      else if (    pos.must_capture()
 +               &&  pos.capture(move)
 +               &&  (ss->inCheck || MoveList<CAPTURES>(pos).size() == 1))
 +          extension = 1;
 +
        // Add extension to new depth
        newDepth += extension;
  
        // Speculative prefetch as early as possible
--      prefetch(TT.first_entry(pos.key_after(move)));
--
        // Update the current move (this must be done after singular extension search)
        ss->currentMove = move;
        ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
                                                                  [captureOrPromotion]
 -                                                                [movedPiece]
 +                                                                [history_slot(movedPiece)]
                                                                  [to_sq(move)];
  
-       // Step 15. Make the move
+       // Step 14. Make the move
        pos.do_move(move, st, givesCheck);
  
-       // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be
+       // Step 15. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be
        // re-searched at full depth.
        if (    depth >= 3
            &&  moveCount > 1 + 2 * rootNode
++          && !(pos.must_capture() && (givesCheck || pos.has_capture()))
            && (  !captureOrPromotion
                || moveCountPruning
                || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
                    r++;
  
                // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
-               r -= ss->statScore / (14884 - 4434 * pos.captures_to_hand());
-           }
-           else
-           {
-               // Unless giving check, this capture is likely bad
-               if (   !givesCheck
-                   && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha)
-                   r++;
+               // If we are not in check use statScore, if we are in check
+               // use sum of main history and first continuation history with an offset
+               if (ss->inCheck)
+                   r -= (thisThread->mainHistory[us][from_to(move)]
 -                     + (*contHist[0])[movedPiece][to_sq(move)] - 4333) / 16384;
++                     + (*contHist[0])[history_slot(movedPiece)][to_sq(move)] - 4333) / 16384;
+               else
 -                  r -= ss->statScore / 14884;
++                  r -= ss->statScore / (14884 - 4434 * pos.captures_to_hand());
            }
  
            Depth d = std::clamp(newDepth - r, 1, newDepth);
diff --cc src/search.h
Simple merge
Simple merge
diff --cc src/thread.cpp
Simple merge
diff --cc src/thread.h
Simple merge
Simple merge
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
Simple merge
diff --cc src/types.h
@@@ -242,22 -121,16 +242,22 @@@ constexpr int MAX_PLY   = 246
  
  enum Move : int {
    MOVE_NONE,
 -  MOVE_NULL = 65
 +  MOVE_NULL = 1 + (1 << SQUARE_BITS)
  };
  
 -enum MoveType {
 +enum MoveType : int {
    NORMAL,
-   ENPASSANT          = 1 << (2 * SQUARE_BITS),
 -  PROMOTION = 1 << 14,
 -  EN_PASSANT = 2 << 14,
 -  CASTLING  = 3 << 14
++  EN_PASSANT          = 1 << (2 * SQUARE_BITS),
 +  CASTLING           = 2 << (2 * SQUARE_BITS),
 +  PROMOTION          = 3 << (2 * SQUARE_BITS),
 +  DROP               = 4 << (2 * SQUARE_BITS),
 +  PIECE_PROMOTION    = 5 << (2 * SQUARE_BITS),
 +  PIECE_DEMOTION     = 6 << (2 * SQUARE_BITS),
 +  SPECIAL            = 7 << (2 * SQUARE_BITS),
  };
  
 +constexpr int MOVE_TYPE_BITS = 4;
 +
  enum Color {
    WHITE, BLACK, COLOR_NB = 2
  };
diff --cc src/uci.cpp
Simple merge
diff --cc src/uci.h
Simple merge
Simple merge
diff --cc src/variant.cpp
index aef593d,0000000..1909e5b
mode 100644,000000..100644
--- /dev/null
@@@ -1,1170 -1,0 +1,1170 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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;
 +
 +VariantMap variants; // Global object
 +
 +namespace {
 +    // Define variant rules
 +    Variant* fairy_variant_base() {
 +        Variant* v = new Variant();
 +        v->pieceToCharTable = "PNBRQ................Kpnbrq................k";
 +        v->endgameEval = false;
 +        return v;
 +    }
 +    Variant* chess_variant() {
 +        Variant* v = fairy_variant_base();
 +        v->endgameEval = true;
 +        return v;
 +    }
 +    Variant* chess960_variant() {
 +        Variant* v = chess_variant();
 +        v->chess960 = true;
 +        return v;
 +    }
 +    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 = fairy_variant_base();
 +        v->materialCounting = BLACK_DRAW_ODDS;
 +        return v;
 +    }
 +    Variant* fairy_variant() {
 +        Variant* v = chess_variant();
 +        v->add_piece(SILVER, 's');
 +        v->add_piece(FERS, 'f');
 +        return v;
 +    }
 +    // Makruk (Thai Chess)
 +    Variant* makruk_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    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;
 +    }
 +    Variant* karouk_variant() {
 +        Variant* v = cambodian_variant();
 +        v->checkCounting = true;
 +        return v;
 +    }
 +    Variant* asean_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    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;
 +    }
 +    Variant* shatranj_variant() {
 +        Variant* v = fairy_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
 +    // https://en.wikipedia.org/wiki/Chaturanga
 +    // Rules as used on chess.com
 +    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;
 +    }
 +    Variant* amazon_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* hoppelpoppel_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* newzealand_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* kingofthehill_variant() {
 +        Variant* v = fairy_variant_base();
 +        v->flagPiece = KING;
 +        v->whiteFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
 +        v->blackFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
 +        v->flagMove = false;
 +        return v;
 +    }
 +    Variant* racingkings_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* knightmate_variant() {
 +        Variant* v = fairy_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->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP};
 +        return v;
 +    }
 +    Variant* losers_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* giveaway_variant() {
 +        Variant* v = fairy_variant_base();
 +        v->variantTemplate = "giveaway";
 +        v->remove_piece(KING);
 +        v->add_piece(COMMONER, 'k');
 +        v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
 +        v->stalemateValue = VALUE_MATE;
 +        v->extinctionValue = VALUE_MATE;
 +        v->extinctionPieceTypes = {ALL_PIECES};
 +        v->mustCapture = true;
 +        return v;
 +    }
 +    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;
 +    }
 +    Variant* suicide_variant() {
 +        Variant* v = antichess_variant();
 +        v->stalematePieceCount = true;
 +        return v;
 +    }
 +    Variant* codrus_variant() {
 +        Variant* v = giveaway_variant();
 +        v->promotionPieceTypes = {QUEEN, ROOK, BISHOP, KNIGHT};
 +        v->extinctionPieceTypes = {COMMONER};
 +        return v;
 +    }
 +    Variant* extinction_variant() {
 +        Variant* v = fairy_variant_base();
 +        v->remove_piece(KING);
 +        v->add_piece(COMMONER, 'k');
 +        v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
 +        v->extinctionValue = -VALUE_MATE;
 +        v->extinctionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT, PAWN};
 +        return v;
 +    }
 +    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 = fairy_variant_base();
 +        v->remove_piece(KING);
 +        v->add_piece(COMMONER, 'k');
 +        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 = fairy_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 = fairy_variant_base();
 +        v->variantTemplate = "atomic";
 +        v->remove_piece(KING);
 +        v->add_piece(COMMONER, 'k');
 +        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();
 +        // TODO: castling, check(-mate), stalemate are not yet properly implemented
 +        v->extinctionPseudoRoyal = true;
 +        return v;
 +    }
 +    Variant* threecheck_variant() {
 +        Variant* v = fairy_variant_base();
 +        v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 3+3 0 1";
 +        v->checkCounting = true;
 +        return v;
 +    }
 +    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;
 +    }
 +    Variant* crazyhouse_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* loop_variant() {
 +        Variant* v = crazyhouse_variant();
 +        v->dropLoop = true;
 +        return v;
 +    }
 +    Variant* chessgi_variant() {
 +        Variant* v = loop_variant();
 +        v->firstRankPawnDrops = true;
 +        return v;
 +    }
 +    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->mustDrop = true;
 +        v->mustDropType = COMMONER;
 +        v->extinctionValue = -VALUE_MATE;
 +        v->extinctionPieceTypes = {COMMONER};
 +        v->extinctionOpponentPieceCount = 2; // own all kings/commoners
 +        return v;
 +    }
 +    Variant* pocketknight_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* placement_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    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;
 +    }
 +    Variant* seirawan_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* shouse_variant() {
 +        Variant* v = seirawan_variant();
 +        v->variantTemplate = "crazyhouse";
 +        v->pieceDrops = true;
 +        v->capturesToHand = true;
 +        return v;
 +    }
 +    Variant* minishogi_variant_base() {
 +        Variant* v = fairy_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->shogiDoubledPawn = false;
 +        v->immobilityIllegal = true;
 +        v->shogiPawnDropMateIllegal = true;
 +        v->stalemateValue = -VALUE_MATE;
 +        v->nFoldRule = 4;
 +        v->nMoveRule = 0;
 +        v->perpetualCheckIllegal = true;
 +        return v;
 +    }
 +    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;
 +    }
 +    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->shogiDoubledPawn = true;
 +        return v;
 +    }
 +    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;
 +    }
 +    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->shogiDoubledPawn = true;
 +        return v;
 +    }
 +    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;
 +    }
 +    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;
 +    }
 +    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(EUROSHOGI_KNIGHT, 'n');
 +        v->startFen = "1nbgkgn1/1r4b1/pppppppp/8/8/PPPPPPPP/1B4R1/1NGKGBN1[-] w 0 1";
 +        v->promotionRank = RANK_6;
 +        v->promotedPieceType[EUROSHOGI_KNIGHT] = GOLD;
 +        v->mandatoryPiecePromotion = true;
 +        return v;
 +    }
 +    Variant* losalamos_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* gardner_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* almost_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* chigorin_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* shatar_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* clobber_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* breakthrough_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* ataxx_variant() {
 +        Variant* v = fairy_variant_base();
 +        v->pieceToCharTable = "P.................p.................";
 +        v->maxRank = RANK_7;
 +        v->maxFile = FILE_G;
 +        v->reset_pieces();
 +        v->add_piece(ATAXX_PIECE, 'p');
 +        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;
 +    }
 +    Variant* minixiangqi_variant() {
 +        Variant* v = fairy_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
 +    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;
 +        return v;
 +    }
 +    Variant* capablanca_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    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;
 +        v->endgameEval = false;
 +        return v;
 +    }
 +    Variant* caparandom_variant() {
 +        Variant* v = capablanca_variant();
 +        v->chess960 = true;
 +        return v;
 +    }
 +    Variant* gothic_variant() {
 +        Variant* v = capablanca_variant();
 +        v->startFen = "rnbqckabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBQCKABNR w KQkq - 0 1";
 +        return v;
 +    }
 +    Variant* janus_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* modern_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* chancellor_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    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;
 +    }
 +    Variant* centaur_variant() {
 +        Variant* v = chess_variant();
 +        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;
 +    }
 +    Variant* jesonmor_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* courier_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* grand_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    Variant* shako_variant() {
 +        Variant* v = fairy_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;
 +    }
 +    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 = fairy_variant_base();
 +        v->pieceToCharTable = "P...Q.................p...q.................";
 +        v->maxRank = RANK_10;
 +        v->maxFile = FILE_J;
 +        v->reset_pieces();
 +        v->add_piece(QUIET_QUEEN, 'q');
 +        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
 +    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
 +    // 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("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("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("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("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("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;
 +}
diff --cc src/variant.h
index f6da799,0000000..539f627
mode 100644,000000..100644
--- /dev/null
@@@ -1,180 -1,0 +1,180 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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"
 +
 +
 +/// 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;
 +  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 endgameEval = 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;
 +  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;
 +  bool shogiDoubledPawn = true;
 +  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; // TODO: implementation incomplete
 +  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;
 +
 +  // Derived properties
 +  bool isFairy = true;
 +  bool isRestricted = true;
 +
 +  void add_piece(PieceType pt, char c, 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);
 +  }
 +
 +  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() {
 +      isFairy = std::any_of(pieceTypes.begin(), pieceTypes.end(), [](PieceType pt) { return pt >= FAIRY_PIECES && pt < KING; });
 +      isRestricted = std::any_of(pieceTypes.begin(), pieceTypes.end(),
 +                                 [this](PieceType pt) {
 +                                     return mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt];
 +                                 });
 +      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;
 +
 +#endif // #ifndef VARIANT_H_INCLUDED
index 7375492,0000000..5087ec3
mode 100644,000000..100644
--- /dev/null
@@@ -1,405 -1,0 +1,405 @@@
 +# Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
- # Copyright (C) 2018-2020 Fabian Fichter
++# 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/>.
 +
 +# This is a configuration file to add user-defined variants to Fairy-Stockfish.
 +
 +################################################
 +### Usage:
 +# Add "load" and the file path to the SF call (e.g., "./stockfish load variants.ini")
 +# or set the UCI option "VariantPath" to the path of this file in order to load it.
 +# In order to validate the configuration without actually loading the variants
 +# run "./stockfish check variants.ini", which reports potential config errors.
 +
 +################################################
 +### Variant configuration:
 +# The variant name needs to be specified as a section in square brackets,
 +# followed by its rule configurations as key-value pairs as described below.
 +# If you encounter problems configuring variants, please report them at:
 +# https://github.com/ianfab/Fairy-Stockfish/issues
 +
 +### Inheritance
 +# If a variant is similar to a previously defined variant,
 +# inheritance can be used to simplify the definition. To inherit from the
 +# configuration of an existing variant, specify the parent variant after the child
 +# variant name separated by a colon, e.g., [gothic:capablanca].
 +# When inheritance is used, only the differences to the parent variant need to be defined,
 +# see the examples in this file, e.g., 3check-crazyhouse.
 +# When no inheritance is used, the default template applies,
 +# which is basically standard chess but without any predefined pieces.
 +
 +### Piece types
 +# Firstly, the piece types for a variant need to be defined.
 +# For that, specify the letter used for each piece type, e.g.:
 +# pawn = p
 +#
 +# See the list below for all available piece types:
 +# pawn
 +# knight
 +# bishop
 +# rook
 +# queen
 +# fers
 +# alfil
 +# fersAlfil (=fers+alfil)
 +# silver
 +# aiwok (=rook+knight+fers)
 +# bers (=rook+fers)
 +# archbishop (=bishop+knight)
 +# chancellor (=rook+knight)
 +# amazon (=queen+knight)
 +# knibis
 +# biskni
 +# kniroo
 +# rookni
 +# shogiPawn
 +# lance
 +# shogiKnight
 +# euroshogiKnight
 +# gold
 +# dragonHorse
 +# clobber
 +# breakthrough
 +# immobile (piece without moves)
 +# ataxx (mDNA)
 +# quietQueen
 +# cannon
 +# janggiCannon
 +# soldier
 +# horse
 +# elephant
 +# janggiElephant
 +# banner (=rook+cannon+horse)
 +# wazir
 +# commoner (non-royal king)
 +# centaur (=knight+commoner)
 +# king
 +
 +### Option types
 +# [bool]: boolean flag to enable/disable a feature [true, false]
 +# [Rank]: denotes a rank of the board [1-10]
 +# [File]: denotes a file of the board [1-12, a-i]
 +# [int]: any natural number [0, 1, ...]
 +# [PieceType]: a piece type [letters defined for pieces, e.g., p]
 +# [Bitboard]: list of squares [e.g., d4 e4 d5 e5]. * can be used as wildcard for files (e.g., *1 is the first rank)
 +# [Value]: game result for the side to move [win, loss, draw]
 +# [MaterialCounting]: material couting rules for adjudication [janggi, unweighted, whitedrawodds, blackdrawodds, none]
 +# [CountingRule]: makruk or ASEAN counting rules [makruk, asean, none]
 +# [EnclosingRule]: reversi or ataxx enclosing rules [reversi, ataxx, none]
 +
 +### Rule definition options
 +# variantTemplate: only relevant for usage in XBoard/WinBoard GUI [values: fairy, shogi] (default: fairy)
 +# pieceToCharTable: mapping of piece characters to images for XBoard/WinBoard GUI (default: -)
 +# pocketSize: number of pockets shown by XBoard/WinBoard for drop variants [int] (default: 0)
 +# maxRank: maximum rank [Rank] (default: 8)
 +# maxFile: maximum file [File] (default: 8)
 +# chess960: allow chess960 castling [bool] (default: false)
 +# twoBoards: the game is influenced by a second board (e.g., bughouse) [bool] (default: false)
 +# startFen: FEN of starting position (default: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1)
 +# mobilityRegion: the mobility area can be defined via options specific to color and piece,
 +#                 .e.g., mobilityRegionWhiteRook, mobilityRegionBlackJanggiElephant, etc. [Bitboard]
 +# promotionRank: relative rank required to reach for promotion [Rank] (default: 8)
 +# promotionPieceTypes: pawn promotion options using their one-letter representations (default: nbrq)
 +# sittuyinPromotion: enable Sittuyin-style pawn promotion [bool] (default: false)
 +# promotionLimit: maximum number of pieces of a type, e.g., q:1 r:2 (default: )
 +# promotedPieceType: mapping between unpromoted and promoted non-pawn piece types, e.g., p:g s:g (default: )
 +# piecePromotionOnCapture: piece promotion only allowed on captures (e.g., micro shogi) [bool] (default: false)
 +# mandatoryPawnPromotion: pawn promotion is mandatory [bool] (default: true)
 +# mandatoryPiecePromotion: piece promotion (and demotion if enabled) is mandatory [bool] (default: false)
 +# pieceDemotion: enable demotion of pieces (e.g., Kyoto shogi) [bool] (default: false)
 +# blastOnCapture: captures explode all adjacent non-pawn pieces (e.g., atomic chess) [bool] (default: false)
 +# endgameEval: enable special endgame evaluation (for very chess-like variants only) [bool] (default: false)
 +# doubleStep: enable pawn double step [bool] (default: true)
 +# doubleStepRank: relative rank from where pawn double steps are allowed [Rank] (default: 2)
 +# doubleStepRankMin: earlist relative rank from where pawn double steps are allowed [Rank] (default: 2)
 +# enPassantRegion: define region (target squares) where en passant is allowed after double steps [Bitboard]
 +# castling: enable castling [bool] (default: true)
 +# castlingDroppedPiece: enable castling with dropped rooks/kings [bool] (default: false)
 +# castlingKingsideFile: destination file of king after kingside castling [File] (default: g)
 +# castlingQueensideFile: destination file of king after queenside castling [File] (default: c)
 +# castlingRank: relative rank of castling [Rank] (default: 1)
 +# castlingRookPiece: piece type that participates in castling [PieceType] (default: r)
 +# kingType: piece type defining moves of king/royal piece (default: k)
 +# checking: allow checks [bool] (default: true)
 +# dropChecks: allow checks by piece drops [bool] (default: true)
 +# mustCapture: captures are mandatory (check evasion still takes precedence) [bool] (default: false)
 +# mustDrop: drops are mandatory (e.g., for Sittuyin setup phase) [bool] (default: false)
 +# mustDropType: piece type for which piece drops are mandatory [PieceType] (default: *)
 +# pieceDrops: enable piece drops [bool] (default: false)
 +# dropLoop: captures promoted pieces are not demoted [bool] (default: false)
 +# capturesToHand: captured pieces are go to opponent's hand [bool] (default: false)
 +# firstRankPawnDrops: allow pawn drops to first rank [bool] (default: false)
 +# promotionZonePawnDrops: allow pawn drops in promotion zone  [bool] (default: false)
 +# dropOnTop: piece drops need to be on top of pieces on board (e.g., for connect4) [bool] (default: false)
 +# enclosingDrop: require piece drop to enclose pieces [EnclosingRule] (default: none)
 +# enclosingDropStart: drop region for starting phase disregarding enclosingDrop (e.g., for reversi) [Bitboard]
 +# whiteDropRegion: restrict region for piece drops of all white pieces [Bitboard]
 +# blackDropRegion: restrict region for piece drops of all black pieces [Bitboard]
 +# sittuyinRookDrop: restrict region of rook drops to first rank [bool] (default: false)
 +# dropOppositeColoredBishop: dropped bishops have to be on opposite-colored squares [bool] (default: false)
 +# dropPromoted: pieces may be dropped in promoted state [bool] (default: false)
 +# shogiDoubledPawn: allow shogi pawns to be doubled [bool] (default: true)
 +# immobilityIllegal: pieces may not move to squares where they can never move from [bool] (default: false)
 +# gating: maintain squares on backrank with extra rights in castling field of FEN [bool] (default: false)
 +# arrowGating: allow gating in Game of the Amazons style [bool] (default: false)
 +# seirawanGating: allow gating of pieces in hand like in S-Chess, requires "gating = true" [bool] (default: false)
 +# cambodianMoves: enable special moves of cambodian chess, requires "gating = true" [bool] (default: false)
 +# diagonalLines: enable special moves along diagonal for specific squares (Janggi) [Bitboard]
 +# pass: allow passing [bool] (default: false)
 +# passOnStalemate: allow passing in case of stalemate [bool] (default: false)
 +# makpongRule: the king may not move away from check [bool] (default: false)
 +# flyingGeneral: disallow general face-off like in xiangqi [bool] (default: false)
 +# soldierPromotionRank: restrict soldier to shogi pawn movements until reaching n-th rank [bool] (default: 1)
 +# flipEnclosedPieces: change color of pieces that are enclosed by a drop [EnclosingRule] (default: none)
 +# nMoveRule: move count for 50/n-move rule [int] (default: 50)
 +# nFoldRule: move count for 3/n-fold repetition rule [int] (default: 3)
 +# nFoldValue: result in case of 3/n-fold repetition [Value] (default: draw)
 +# nFoldValueAbsolute: result in case of 3/n-fold repetition is from white's point of view [bool] (default: false)
 +# perpetualCheckIllegal: prohibit perpetual checks [bool] (default: false)
 +# moveRepetitionIllegal: prohibit moving back and forth with the same piece nFoldRule-1 times [bool] (default: false)
 +# stalemateValue: result in case of stalemate [Value] (default: draw)
 +# stalematePieceCount: count material in case of stalemate [bool] (default: false)
 +# checkmateValue: result in case of checkmate [Value] (default: loss)
 +# shogiPawnDropMateIllegal: prohibit checkmate via shogi pawn drops [bool] (default: false)
 +# shatarMateRule: enable shatar mating rules [bool] (default: false)
 +# bikjangRule: consider Janggi bikjang (facing kings) rule [bool] (default: false)
 +# extinctionValue: result when one of extinctionPieceTypes is extinct [Value] (default: none)
 +# extinctionClaim: extinction of opponent pieces can only be claimed as side to move [bool] (default: false)
 +# extinctionPieceTypes: list of piece types for extinction rules, e.g., pnbrq (* means all) (default: )
 +# extinctionPieceCount: piece count at which the game is decided by extinction rule (default: 0)
 +# extinctionOpponentPieceCount: opponent piece count required to adjudicate by extinction rule (default: 0)
 +# flagPiece: piece type for capture the flag win rule [PieceType] (default: -)
 +# whiteFlag: white's target region for capture the flag win rule [Bitboard] (default: )
 +# blackFlag: black's target region for capture the flag win rule [Bitboard] (default: )
 +# flagMove: black gets one more move after white captures the flag [bool] (default: false)
 +# checkCounting: enable check count win rule (check count is communicated via FEN, see 3check) [bool] (default: false)
 +# connectN: number of aligned pieces for win [int] (default: 0)
 +# materialCounting: enable material counting rules [MaterialCounting] (default: none)
 +# countingRule: enable counting rules [CountingRule] (default: none)
 +
 +################################################
 +### Example for minishogi configuration that would be equivalent to the built-in variant:
 +
 +# [minishogi]
 +# variantTemplate = shogi
 +# maxRank = 5
 +# maxFile = 5
 +# shogiPawn = p
 +# silver = s
 +# gold = g
 +# bishop = b
 +# dragonHorse = h
 +# rook = r
 +# bers = d
 +# king = k
 +# startFen = rbsgk/4p/5/P4/KGSBR[-] w 0 1
 +# pieceDrops = true
 +# capturesToHand = true
 +# promotionRank = 5
 +# doubleStep = false
 +# castling = false
 +# promotedPieceType = p:g s:g b:h r:d
 +# shogiDoubledPawn = false
 +# immobilityIllegal = true
 +# shogiPawnDropMateIllegal = true
 +# stalemateValue = loss
 +# nFoldRule = 4
 +# nMoveRule = 0
 +# perpetualCheckIllegal = true
 +# pocketSize = 5
 +# nFoldValue = loss
 +# nFoldValueAbsolute = true
 +
 +# Hybrid variant of three-check chess and crazyhouse, using crazyhouse as a template
 +[3check-crazyhouse:crazyhouse]
 +startFen = rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[] w KQkq - 3+3 0 1
 +checkCounting = true
 +
 +# Crazyhouse with mandatory captures, using crazyhouse as a template
 +[coffeehouse:crazyhouse]
 +mustCapture = true
 +
 +# Hybrid variant of makruk and crazyhouse
 +[makrukhouse:makruk]
 +startFen = rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR[] w - - 0 1
 +pieceDrops = true
 +capturesToHand = true
 +
 +# Hybrid variant of xiangqi and crazyhouse
 +[xiangqihouse:xiangqi]
 +startFen = rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR[] w - - 0 1
 +pieceDrops = true
 +capturesToHand = true
 +dropChecks = false
 +whiteDropRegion = *1 *2 *3 *4 *5
 +blackDropRegion = *6 *7 *8 *9 *10
 +
 +# Hybrid variant of janggi and crazyhouse
 +[janggihouse:janggi]
 +startFen = rnba1abnr/4k4/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/4K4/RNBA1ABNR[] w - - 0 1
 +pieceDrops = true
 +capturesToHand = true
 +
 +# Hybrid variant of antichess and losalamos
 +[anti-losalamos:losalamos]
 +king = -
 +commoner = k
 +promotionPieceTypes = nrqk
 +mustCapture = true
 +stalemateValue = win
 +extinctionValue = win
 +extinctionPieceTypes = *
 +
 +# Indian great chess
 +# https://www.chessvariants.com/historic.dir/indiangr1.html
 +[indiangreat]
 +pieceToCharTable = PNBRQ..VW.........G..Kpnbrq..vw.........g..k
 +pawn = p
 +knight = n
 +bishop = b
 +rook = r
 +queen = q
 +king = k
 +archbishop = v
 +chancellor = w
 +amazon = g
 +maxRank = 10
 +maxFile = 10
 +startFen = rnbqkgvbnr/ppppwwpppp/4pp4/10/10/10/10/4PP4/PPPPWWPPPP/RNBVGKQBNR w - - 0 1
 +promotionRank = 10
 +promotionPieceTypes = q
 +doubleStep = false
 +castling = false
 +
 +# Upside-down
 +[upsidedown:chess]
 +startFen = RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr w - - 0 1
 +
 +# Peasant revolt
 +# https://www.chessvariants.com/large.dir/peasantrevolt.html
 +[peasant:chess]
 +startFen = 1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1
 +
 +# https://www.chessvariants.com/unequal.dir/weak.html
 +[weak:chess]
 +startFen = nnnnknnn/pppppppp/2p2p2/1pppppp1/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1
 +
 +[semitorpedo:chess]
 +doubleStepRank = 3
 +
 +# This variant is similar to Capablanca Chess, but with two archbishops and no chancellor piece.
 +[gemini:janus]
 +startFen = rnbaqkabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBAQKABNR w KQkq - 0 1
 +archbishop = a
 +pieceToCharTable = PNBRQ............A...Kpnbrq............a...k
 +castlingKingsideFile = i
 +castlingQueensideFile = c
 +
 +# https://www.chessvariants.com/diffsetup.dir/pawnsonly.html
 +[pawnsonly]
 +pawn = p
 +queen = q
 +startFen = 8/pppppppp/8/8/8/8/PPPPPPPP/8 w - - 0 1
 +promotionPieceTypes = q
 +castling = false
 +stalemateValue = loss
 +flagPiece = q
 +whiteFlag = *8
 +blackFlag = *1
 +
 +[tictactoe]
 +maxRank = 3
 +maxFile = 3
 +immobile = p
 +startFen = 3/3/3[PPPPPpppp] w - - 0 1
 +pieceDrops = true
 +doubleStep = false
 +castling = false
 +stalemateValue = draw
 +immobilityIllegal = false
 +connectN = 3
 +
 +[cfour]
 +maxRank = 6
 +maxFile = 7
 +immobile = p
 +startFen = 7/7/7/7/7/7[PPPPPPPPPPPPPPPPPPPPPppppppppppppppppppppp] w - - 0 1
 +pieceDrops = true
 +dropOnTop = true
 +doubleStep = false
 +castling = false
 +stalemateValue = draw
 +immobilityIllegal = false
 +connectN = 4
 +nMoveRule = 0
 +
 +[flipersi]
 +immobile = p
 +startFen = 8/8/8/8/8/8/8/8[PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPpppppppppppppppppppppppppppppppp] w 0 1
 +pieceDrops = true
 +promotionPieceTypes = -
 +doubleStep = false
 +castling = false
 +stalemateValue = loss
 +stalematePieceCount = true
 +materialCounting = unweighted
 +enclosingDrop = reversi
 +enclosingDropStart = d4 e4 d5 e5
 +immobilityIllegal = false
 +flipEnclosedPieces = reversi
 +passOnStalemate = false
 +
 +[flipello:flipersi]
 +startFen = 8/8/8/3pP3/3Pp3/8/8/8[PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPpppppppppppppppppppppppppppppppp] w 0 1
 +passOnStalemate = true
 +
 +[grandhouse:grand]
 +startFen = r8r/1nbqkcabn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKCABN1/R8R[] w - - 0 1
 +pieceDrops = true
 +capturesToHand = true
 +
 +[shogun:crazyhouse]
 +variantTemplate = shogi
 +pieceToCharTable = PNBR.F.....++++.+Kpnbr.f.....++++.+k
 +pocketSize = 8
 +startFen = rnb+fkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB+FKBNR[] w KQkq - 0 1
 +commoner = c
 +centaur = g
 +archbishop = a
 +chancellor = m
 +fers = f
 +promotionRank = 6
 +promotionLimit = g:1 a:1 m:1 q:1
 +promotionPieceTypes = -
 +promotedPieceType = p:c n:g b:a r:m f:q
 +mandatoryPawnPromotion = false
 +firstRankPawnDrops = true
 +promotionZonePawnDrops = true
 +whiteDropRegion = *1 *2 *3 *4 *5
 +blackDropRegion = *4 *5 *6 *7 *8
 +immobilityIllegal = true
 +
 +# Asymmetric variant with one army using pieces that move like knights but attack like other pieces (kniroo and knibis)
 +[orda:chess]
 +pieceToCharTable = PNBRQ..AH...........LKp...q..ah.y.........lk
 +centaur = h
 +knibis = a
 +kniroo = l
 +silver = y
 +promotionPieceTypes = qh
 +startFen = lhaykahl/8/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1
 +flagPiece = k
 +whiteFlag = *8
 +blackFlag = *1
diff --cc src/xboard.cpp
index 4f9619a,0000000..f88ce58
mode 100644,000000..100644
--- /dev/null
@@@ -1,481 -1,0 +1,481 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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 {
 +
 +  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
diff --cc src/xboard.h
index db312ce,0000000..48f7679
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
 +/*
 +  Fairy-Stockfish, a UCI chess variant playing engine derived from Stockfish
-   Copyright (C) 2018-2020 Fabian Fichter
++  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"
 +
 +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
 +
 +#endif // #ifndef XBOARD_H_INCLUDED