stellar

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

engine.cpp (3325B)


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