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