stellar

Stellar - UCI Chess engine written in C++20
git clone git://git.dimitrijedobrota.com/stellar.git
Log | Files | Refs | README | LICENSE

engine.cpp (3325B)


      1 #include "engine.hpp"
      2 #include "logger.hpp"
      3 
      4 #include <sstream>
      5 
      6 #include <sys/wait.h>
      7 #include <unistd.h>
      8 
      9 Engine::Pipes::Pipes() {
     10     if (pipe(fd) < 0 || pipe(fd) < 0) {
     11         logger::error("pipe");
     12         throw std::runtime_error("pipe failed");
     13     }
     14 }
     15 
     16 void Engine::Pipes::close() {
     17     if (::close(fd[0]) < 0 || ::close(fd[1])) {
     18         logger::error("close");
     19         throw std::runtime_error("close failed");
     20     }
     21     closed = true;
     22 }
     23 
     24 uint16_t Engine::id_t = 0;
     25 Engine::Engine(const char *file) : file(file) {
     26     if ((engine_pid = fork()) == 0) {
     27         start_engine();
     28     } else if (engine_pid < 0) {
     29         logger::error("fork");
     30         throw std::runtime_error("fork failed");
     31     }
     32 
     33     send("uci");
     34 
     35     logger::log(std::format("Engine {}: waiting for uciok from {}", id, file));
     36     while (true) {
     37         std::string tmp, response = receive();
     38         if (response == "uciok") break;
     39         std::stringstream ss(response);
     40         ss >> tmp >> tmp;
     41         ss.ignore(1);
     42         if (tmp == "name") getline(ss, name);
     43         if (tmp == "author") getline(ss, author);
     44     }
     45     logger::log(std::format("Engine {}: {} is {} by {}", id, file, name, author));
     46     logger::log(std::format("Engine {}: created", id), logger::Debug);
     47 }
     48 
     49 Engine::~Engine() {
     50     send("quit");
     51     waitpid(engine_pid, nullptr, 0);
     52     // kill(engine_pid, SIGKILL);
     53 
     54     pipeFrom.close();
     55     pripeTo.close();
     56     logger::log("Engine: destroyed", logger::Debug);
     57 }
     58 
     59 void Engine::send(std::string &&command) {
     60     command.push_back('\n');
     61     const char *buffer = command.data();
     62     size_t to_write = command.size();
     63     while (to_write) {
     64         ssize_t size = write(pripeTo[1], buffer, to_write);
     65         if (size == -1) {
     66             logger::error("write");
     67             throw std::runtime_error("write failed");
     68         }
     69         buffer += size, to_write -= size;
     70     }
     71     command.pop_back();
     72     logger::log(std::format("Engine {}: TO {}: {}", id, name.size() ? name : file, command), logger::Info);
     73 }
     74 
     75 std::string Engine::receive() {
     76     int size = 0;
     77 
     78     while (true) {
     79         if (!q.empty()) {
     80             std::string response = q.front();
     81             logger::log(std::format("Engine {}: FROM {}: {}", id, name.size() ? name : file, response),
     82                         logger::Info);
     83             q.pop();
     84             return response;
     85         }
     86 
     87         if ((size = read(pipeFrom[0], rb + rb_size, sizeof(rb) - rb_size)) == -1) {
     88             logger::error("read");
     89             throw std::runtime_error("read failed");
     90         }
     91 
     92         int last = 0;
     93         for (int i = rb_size; i < rb_size + size; i++) {
     94             if (rb[i] == '\n') {
     95                 q.push(q_buffer);
     96                 q_buffer.clear();
     97                 last = i;
     98                 continue;
     99             }
    100             q_buffer += rb[i];
    101         }
    102 
    103         rb_size += size;
    104         if (last) {
    105             rb_size -= last + 1;
    106             memcpy(rb, rb + last + 1, rb_size);
    107         }
    108     }
    109 }
    110 
    111 [[noreturn]] void Engine::start_engine() {
    112     if (dup2(pripeTo[0], 0) < 0 || dup2(pipeFrom[1], 1) < 0) {
    113         logger::error("dup2");
    114         throw std::runtime_error("dup2 failed");
    115     }
    116 
    117     pipeFrom.close();
    118     pripeTo.close();
    119 
    120     execl(file, file, (char *)nullptr);
    121     logger::error("execl");
    122     throw std::runtime_error("execl failed");
    123 }