C/C++ client UCI example

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

nnnnnnnn
Posts: 37
Joined: Mon Nov 18, 2019 2:36 pm
Full name: Mark Thellen

C/C++ client UCI example

Post by nnnnnnnn »

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.
User avatar
xr_a_y
Posts: 1871
Joined: Sat Nov 25, 2017 2:28 pm
Location: France

Re: C/C++ client UCI example

Post by xr_a_y »

nnnnnnnn
Posts: 37
Joined: Mon Nov 18, 2019 2:36 pm
Full name: Mark Thellen

Re: C/C++ client UCI example

Post by nnnnnnnn »

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.
User avatar
xr_a_y
Posts: 1871
Joined: Sat Nov 25, 2017 2:28 pm
Location: France

Re: C/C++ client UCI example

Post by xr_a_y »

nnnnnnnn wrote: Fri May 01, 2020 9:54 am 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.
Banksia ?

https://github.com/nguyenpham/Banksia/b ... engine.cpp
https://github.com/nguyenpham/Banksia/b ... ciengine.h
User avatar
Deberger
Posts: 91
Joined: Sat Nov 02, 2019 6:42 pm
Full name: ɹǝƃɹǝqǝᗡ ǝɔnɹꓭ

Re: C/C++ client UCI example

Post by Deberger »

nnnnnnnn wrote: Fri May 01, 2020 9:54 am 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.
https://www.chessprogramming.org/PolyGlot
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: C/C++ client UCI example

Post by lucasart »

nnnnnnnn wrote: Fri May 01, 2020 7:27 am 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.
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;
}
This can be extented of course. In particular:
- 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.
User avatar
hgm
Posts: 27790
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: C/C++ client UCI example

Post by hgm »

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.
nnnnnnnn
Posts: 37
Joined: Mon Nov 18, 2019 2:36 pm
Full name: Mark Thellen

Re: C/C++ client UCI example

Post by nnnnnnnn »

lucasart wrote: Fri May 01, 2020 1:39 pm
Boiler-plate code for POSIX systems (eg. Linux, MacOSX, Android, FreeBSD, basically everything except Windows):
Thanks, this is a big time-saver for me.
nnnnnnnn
Posts: 37
Joined: Mon Nov 18, 2019 2:36 pm
Full name: Mark Thellen

Re: C/C++ client UCI example

Post by nnnnnnnn »

xr_a_y wrote: Fri May 01, 2020 11:09 am Banksia ?
Thank you. That looks like clear, well-written C++ code. I had not seen the override keyword used in the wild before but I now see where it is helpful for reading class hierarchies.

Also thanks to everyone for all the pointers.