// UCI (Univeral Chess Interface) `R` binding to the `SchachHoernchen` engine #include #include #include #include #include #include "SchachHoernchen/Move.h" #include "SchachHoernchen/Board.h" #include "SchachHoernchen/uci.h" #include "SchachHoernchen/search.h" namespace UCI_State { std::vector game(1); }; //' Converts a FEN string to a Board (position) or return the current internal state // [[Rcpp::export(rng = false)]] Board board(const std::string& fen = "") { if (fen == "") { return UCI_State::game.back(); } else if (fen == "startpos") { return Board(); } Board pos; bool parseError = false; pos.init(fen, parseError); if (parseError) { Rcpp::stop("Parse parsing FEN"); } return pos; } // [[Rcpp::export(name = "print.board", rng = false)]] void print_board(const std::string& fen = "") { UCI::printBoard(board(fen)); } // [[Rcpp::export(name = "print.moves", rng = false)]] void print_moves(const std::string& fen = "") { UCI::printMoves(board(fen)); } // [[Rcpp::export(name = "print.bitboards", rng = false)]] void print_bitboards(const std::string& fen = "") { UCI::printBitBoards(board(fen)); } // [[Rcpp::export(rng = false)]] Board position( const Board& pos, const std::vector& moves, const bool san = false ) { // Build UCI command (without "position") std::stringstream cmd; cmd << "fen " << pos.fen() << " "; if (moves.size()) { cmd << "moves "; std::copy(moves.begin(), moves.end(), std::ostream_iterator(cmd, " ")); } // set UCI internal flag to interprate move input in from-to format or SAN UCI::readSAN = san; // and invoke UCI position command handler on the internal game state bool parseError = false; UCI::position(UCI_State::game, cmd, parseError); if (parseError) { Rcpp::stop("Parse Error"); } return UCI_State::game.back(); } // [[Rcpp::export(rng = false)]] void perft(const int depth = 6) { // Enforce a depth limit, this is very restrictive but we only want to // use it as a toolbox and _not_ as a strong chess engine if (8 < depth) { Rcpp::stop("In `R` search is limited to depth 8"); } else if (depth <= 0) { cout << "Nodes searched: 0" << std::endl; return; } // Get current set position Board pos(UCI_State::game.back()); // Get all legal moves MoveList moves; pos.moves(moves); // Setup counter for total nr. of moves Index totalCount = 0; Board copy(pos); for (Move move : moves) { // continue traversing pos.make(move); Index nodeCount = Search::perft_subroutine(pos, depth - 1); totalCount += nodeCount; pos = copy; // unmake move // report moves of node cout << move << ": " << nodeCount << std::endl; } cout << std::endl << "Nodes searched: " << totalCount << std::endl; } // [[Rcpp::export(rng = false)]] Move go( const int depth = 6 ) { // Enforce a depth limit, this is very restrictive but we only want to // use it as a toolbox and _not_ as a strong chess engine if (8 < depth) { Rcpp::stop("In `R` search is limited to depth 8"); } // Setup search configuration Search::State config; config.depth = depth < 1 ? 1 : depth; // sets worker thread stop condition to false (before dispatch) which in this // context is the main thread. Only need to ensure its running. Search::isRunning.store(true, std::memory_order_release); // Construct a search object Search::PVS search(UCI_State::game, config); // and start the search search(); // return best move return search.bestMove(); } // [[Rcpp::export(rng = false)]] void ucinewgame() { Search::newgame(); UCI_State::game.clear(); UCI_State::game.emplace_back(); }