I am seeking simple sample code for accessing and controlling a UCI chess program (Stockfish) from C or C++ on Unix/Linux/MacOS.
I realize this code is somewhere in the big chess GUIs but I was hoping for a smaller and more self-contained example.
C/C++ client UCI example
Moderators: hgm, Rebel, chrisw
-
- Posts: 37
- Joined: Mon Nov 18, 2019 2:36 pm
- Full name: Mark Thellen
-
- Posts: 1871
- Joined: Sat Nov 25, 2017 2:28 pm
- Location: France
Re: C/C++ client UCI example
cutechess might be a good entry point
https://github.com/cutechess/cutechess/ ... ciengine.h
https://github.com/cutechess/cutechess/ ... engine.cpp
https://github.com/cutechess/cutechess/ ... ciengine.h
https://github.com/cutechess/cutechess/ ... engine.cpp
-
- Posts: 37
- Joined: Mon Nov 18, 2019 2:36 pm
- Full name: Mark Thellen
Re: C/C++ client UCI example
Thank, the cutechess code is helpful. I believe it is using Qt for some of the process stuff, perhaps there is something even smaller and more self-contained somewhere.
-
- Posts: 1871
- Joined: Sat Nov 25, 2017 2:28 pm
- Location: France
-
- Posts: 91
- Joined: Sat Nov 02, 2019 6:42 pm
- Full name: ɹǝƃɹǝqǝᗡ ǝɔnɹꓭ
-
- Posts: 3232
- Joined: Mon May 31, 2010 1:29 pm
- Full name: lucasart
Re: C/C++ client UCI example
Boiler-plate code for POSIX systems (eg. Linux, MacOSX, Android, FreeBSD, basically everything except Windows):
Code: Select all
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "engine.h"
// Engine process
typedef struct {
pid_t pid;
FILE *in, *out, *log;
char name[64];
} Engine;
bool engine_start(Engine *e, const char *cmd, FILE *log)
{
// Pipe diagram: Parent -> [1]into[0] -> Child -> [1]outof[0] -> Parent
// 'into' and 'outof' are pipes, each with 2 ends: read=0, write=1
int outof[2], into[2];
if (pipe(outof) < 0 || pipe(into) < 0)
return false;
e->pid = fork();
if (e->pid == 0) {
// in the child process
close(into[1]);
close(outof[0]);
if (dup2(into[0], STDIN_FILENO) == -1)
return false;
close(into[0]);
if (dup2(outof[1], STDOUT_FILENO) == -1)
return false;
close(outof[1]);
if (execlp(cmd, cmd, NULL) == -1)
return false;
} else if (e->pid > 0) {
// in the parent process
close(into[0]);
close(outof[1]);
if (!(e->in = fdopen(outof[0], "r")))
return false;
if (!(e->out = fdopen(into[1], "w")))
return false;
// FIXME: doesn't work on Windows
setvbuf(e->in, NULL, _IONBF, 0);
setvbuf(e->out, NULL, _IONBF, 0);
} else
// fork failed
return false;
e->log = log;
return true;
}
bool engine_stop(Engine *e)
{
// close the parent side of the pipes
fclose(e->in);
fclose(e->out);
// terminate the child process
return kill(e->pid, SIGTERM) >= 0;
}
bool engine_readln(const Engine *e, char *buf, size_t n)
{
if (fgets(buf, n, e->in)) {
if (e->log)
fprintf(e->log, "%s -> %s", e->name, buf);
return true;
}
return false;
}
bool engine_writeln(const Engine *e, char *buf)
{
if (fputs(buf, e->out) >= 0) {
if (e->log)
fprintf(e->log, "%s <- %s", e->name, buf);
return true;
}
return false;
}
- use poll() to do timeout reads. As stated above engine_readln() will block until a full line is received. You want to detect time out (or the engine crashing which has the same observable effect that you have nothing to read from the pipe).
- use dynamic allocation strategy for readln, to avoid using a fixed size array and have flexibility, as implemented in getline(). In combination with poll() this is going to be a bit of work...
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
-
- Posts: 27790
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: C/C++ client UCI example
It depends what you mean by 'controlling'. If you are just interested on how to launch the engine and communicate with it, UCI2WB would be a good (and quite small) example. If you want to see what command you have to send it... Well, that would depend on what you want the engine to do, obviously.
-
- Posts: 37
- Joined: Mon Nov 18, 2019 2:36 pm
- Full name: Mark Thellen
-
- Posts: 37
- Joined: Mon Nov 18, 2019 2:36 pm
- Full name: Mark Thellen