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"
3 #include <sstream>
5 #include <sys/wait.h>
6 #include <unistd.h>
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 }
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 }
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 }
32 send("uci");
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 }
48 Engine::~Engine() {
49 send("quit");
50 waitpid(engine_pid, nullptr, 0);
51 // kill(engine_pid, SIGKILL);
53 pipeFrom.close();
54 pripeTo.close();
55 logger::log("Engine: destroyed", logger::Debug);
56 }
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 }
74 std::string Engine::receive() {
75 int size = 0;
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 }
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 }
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];
102 rb_size += size;
103 if (last) {
104 rb_size -= last + 1;
105 memcpy(rb, rb + last + 1, rb_size);
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");
116 pipeFrom.close();
117 pripeTo.close();
119 execl(file, file, (char *)nullptr);
120 logger::error("execl");
121 throw std::runtime_error("execl failed");