tensor_predictors/dataAnalysis/chess/Rchess/src/uci.cpp

147 lines
3.9 KiB
C++

// UCI (Univeral Chess Interface) `R` binding to the `SchachHoernchen` engine
#include <string>
#include <vector>
#include <sstream>
#include <iterator>
#include <Rcpp.h>
#include "SchachHoernchen/Move.h"
#include "SchachHoernchen/Board.h"
#include "SchachHoernchen/uci.h"
#include "SchachHoernchen/search.h"
namespace UCI_State {
std::vector<Board> 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<std::string>& 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<std::string>(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<Board> 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();
}