testsAndMisc/C/opening_learner/engine.c

142 lines
5.2 KiB
C

#include "engine.h"
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
static bool spawn_process(const char *path, Engine *e){
int inpipe[2], outpipe[2];
if (pipe(inpipe)<0 || pipe(outpipe)<0) return false;
pid_t pid = fork();
if (pid < 0) return false;
if (pid == 0){
dup2(inpipe[0], STDIN_FILENO); // child stdin from inpipe[0]
dup2(outpipe[1], STDOUT_FILENO); // child stdout to outpipe[1]
dup2(outpipe[1], STDERR_FILENO);
close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]);
execlp(path, path, (char*)NULL);
_exit(127);
}
// parent
close(inpipe[0]); close(outpipe[1]);
e->pid = pid; e->in_fd = inpipe[1]; e->out_fd = outpipe[0]; e->ready=false;
// make out_fd non-blocking for reads with polling
int flags = fcntl(e->out_fd, F_GETFL, 0); fcntl(e->out_fd, F_SETFL, flags | O_NONBLOCK);
return true;
}
static bool try_start(Engine *e, const char *name){
if (!spawn_process(name, e)) return false;
// send UCI init
engine_cmd(e, "uci\n");
char buf[4096]; int attempts=50; // ~5s total
while (attempts--){
usleep(100000);
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
if (n>0){
buf[n]='\0';
if (strstr(buf, "uciok")) { e->ready=true; break; }
}
}
if (!e->ready){ engine_stop(e); return false; }
engine_cmd(e, "isready\n");
attempts=50;
while (attempts--){
usleep(100000);
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
if (n>0){ buf[n]='\0'; if (strstr(buf, "readyok")) break; }
}
return e->ready;
}
bool engine_start(Engine *e){
memset(e, 0, sizeof(*e)); e->pid=-1; e->in_fd=-1; e->out_fd=-1; e->ready=false;
if (try_start(e, "stockfish")) return true;
if (try_start(e, "asmfish")) return true;
return false;
}
void engine_stop(Engine *e){
if (e->in_fd!=-1){ write(e->in_fd, "quit\n", 5); close(e->in_fd); }
if (e->out_fd!=-1) close(e->out_fd);
if (e->pid>0){ int status; waitpid(e->pid, &status, 0); }
e->pid=-1; e->in_fd=e->out_fd=-1; e->ready=false;
}
bool engine_cmd(Engine *e, const char *cmd){
if (e->in_fd==-1) return false;
size_t len = strlen(cmd);
ssize_t n = write(e->in_fd, cmd, len);
if (n < 0) {
return false;
}
return n == (ssize_t)len;
}
static void position_to_uci(const Position *pos, char *out, size_t outsz){
// Send starting position and moves list by comparing with startpos; for simplicity, use FEN always.
char fen[256]; chess_to_fen(pos, fen, sizeof(fen));
snprintf(out, outsz, "position fen %s\n", fen);
}
size_t engine_get_top_moves(Engine *e, const Position *pos, EngineMove *out, size_t max){
if (!e->ready) return 0;
char cmd[512]; position_to_uci(pos, cmd, sizeof(cmd)); engine_cmd(e, cmd);
// ask multiPV up to max (cap at 5 as requested)
size_t req = max; if (req>5) req=5; char go[128]; snprintf(go, sizeof(go), "setoption name MultiPV value %zu\n", req); engine_cmd(e, go);
engine_cmd(e, "go movetime 400\n");
char buf[8192]; size_t count=0; int attempts=50;
while (attempts--){
usleep(100000);
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
if (n<=0) continue;
buf[n]='\0';
char *line = strtok(buf, "\n");
while (line){
if (strncmp(line, "info ", 5)==0){
// parse "info ... multipv X score cp Y ... pv <uci>"
char *mpv = strstr(line, " multipv "); char *score = strstr(line, " score "); char *pv = strstr(line, " pv ");
if (mpv && score && pv){
int idx = atoi(mpv+9); if (idx>=1 && (size_t)idx<=req){
int cp=0; if (strstr(score, "cp ")) cp = atoi(strstr(score, "cp ")+3);
char mv[8]={0};
sscanf(pv+4, "%7s", mv);
size_t i = (size_t)idx-1; if (i<req){ out[i].score_cp = cp; snprintf(out[i].uci, sizeof(out[i].uci), "%s", mv); if (i+1>count) count=i+1; }
}
}
} else if (strncmp(line, "bestmove ", 9)==0){ attempts=0; break; }
line = strtok(NULL, "\n");
}
}
// simple sort by score descending (best to worst), keeping empties at end
for (size_t i=0;i<count;i++){
for (size_t j=i+1;j<count;j++){
if (out[j].score_cp > out[i].score_cp){ EngineMove tmp=out[i]; out[i]=out[j]; out[j]=tmp; }
}
}
return count;
}
bool engine_get_best_move(Engine *e, const Position *pos, char out_uci[8]){
if (!e->ready) return false;
char cmd[512]; position_to_uci(pos, cmd, sizeof(cmd)); engine_cmd(e, cmd);
engine_cmd(e, "go movetime 300\n");
char buf[4096]; int attempts=50;
while (attempts--){
usleep(100000);
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
if (n<=0) continue;
buf[n]='\0';
char *line = strtok(buf, "\n");
while (line){
if (strncmp(line, "bestmove ", 9)==0){ sscanf(line+9, "%7s", out_uci); return true; }
line = strtok(NULL, "\n");
}
}
return false;
}