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