147 lines
3.9 KiB
C++
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();
|
|
}
|