- 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
--- /dev/null
+# 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)
--- /dev/null
+/*
+ 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;
+}
+}
+
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
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]);
{
// 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
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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
#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;
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 } )
/// 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
/// 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 };
--- /dev/null
+/*
+ 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);
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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&);
--- /dev/null
+/*
+ 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
#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)
--- /dev/null
+/*
+ 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();
+}
--- /dev/null
+/*
+ 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
// 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;
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);
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);
#include "bitboard.h"
#include "evaluate.h"
+ #include "psqt.h"
#include "types.h"
+#include "variant.h"
+#include "movegen.h"
#include "nnue/nnue_accumulator.h"
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;
}
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 {
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 {
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
{ 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
--- /dev/null
+ /*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ #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
? 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
// 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);
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
};
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+# 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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