UCI Programming

Code, algorithms, languages, construction...
CDaley11
Posts: 42
Joined: Thu Jan 10, 2013 6:23 am
Real Name: Christian Daley

UCI Programming

Post by CDaley11 » Tue Jan 15, 2013 2:53 am

Well I've pretty much finished my java chess game, thanks for the help you guys have given me. But I've become interested in programming an engine for UCI. Right now my java program has its own UI which is very limited. I would like to program an engine in C/C++. Does anyone know how I would get started doing that? Does anyone know any good UCI user interface programs for mac that I could test my engine on? (sigma chess doesn't work on the OSX I'm running).

User923005
Posts: 616
Joined: Thu May 19, 2011 1:35 am

Re: UCI Programming

Post by User923005 » Tue Jan 15, 2013 3:45 am

It would probably be easier to modify your java program for UCI or Winboard interface.
Jose chess gui has a nice interface for both UCI and Winboard/Xboard:
http://jose-chess.sourceforge.net/

A C++ version will be faster, but if you are a lot more familiar with Java, I would do a conversion of the Java engine first.
Then, you could do a conversion of your Java engine to C++.

If you want to write a C++ engine, the Chess Programming Wiki is probably a good place to start.
http://chessprogramming.wikispaces.com/

CDaley11
Posts: 42
Joined: Thu Jan 10, 2013 6:23 am
Real Name: Christian Daley

Re: UCI Programming

Post by CDaley11 » Tue Jan 15, 2013 7:02 am

Thank you for your help! The reason I am wanting to make it in C/C++ is because java is so slow. I am just as comfortable in C/C++ as I am in java, the UCI thing just confused me because I'm not sure how to set up my program to play with a UCI interface. Do you think it would be a good idea to first make my engine just a command line program where a user has to type in a move (e.g e2e4) and the engine will just spit a move back out? After I at least have a bare bones evaluation and search function I could add in UCI support.

User923005
Posts: 616
Joined: Thu May 19, 2011 1:35 am

Re: UCI Programming

Post by User923005 » Tue Jan 15, 2013 9:37 pm

UCI and Winboard are really very simple.
There is a published grammar for both interfaces.
The program and a server of some kind just exchange character string messages.
You don't need a complicated interface to start with.
Look at some simple examples in C++ or C to see how other people have done it.
Later on, you can get cute with it (e.g. use a hash table for look-ups and other fancy stuff).

This is the UCI interface for Sungorus chess program, written in C. As you can see, it's not rocket science:

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sungorus.h"

void ReadLine(char *str, int n)
{
    char *ptr;

    if (fgets(str, n, stdin) == NULL)
        exit(0);
    if ((ptr = strchr(str, '\n')) != NULL)
        *ptr = '\0';
}

char *ParseToken(char *string, char *token)
{
    while (*string == ' ')
        string++;
    while (*string != ' ' && *string != '\0')
        *token++ = *string++;
    *token = '\0';
    return string;
}

void UciLoop(void)
{
    char command[4096], token[80], *ptr;
    POS p[1];

    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    SetPosition(p, START_POS);
    AllocTrans(16);
    for (;;) {
        ReadLine(command, sizeof(command));
        ptr = ParseToken(command, token);
        if (strcmp(token, "uci") == 0) {
            printf("id name Sungorus 1.4\n");
            printf("id author Pablo Vazquez\n");
            printf("option name Hash type spin default 16 min 1 max 4096\n");
            printf("option name Clear Hash type button\n");
            printf("uciok\n");
        } else if (strcmp(token, "isready") == 0) {
            printf("readyok\n");
        } else if (strcmp(token, "setoption") == 0) {
            ParseSetoption(ptr);
        } else if (strcmp(token, "position") == 0) {
            ParsePosition(p, ptr);
        } else if (strcmp(token, "go") == 0) {
            ParseGo(p, ptr);
        } else if (strcmp(token, "quit") == 0) {
            exit(0);
        }
    }
}

void ParseSetoption(char *ptr)
{
    char token[80], name[80], value[80];

    ptr = ParseToken(ptr, token);
    name[0] = '\0';
    for (;;) {
        ptr = ParseToken(ptr, token);
        if (*token == '\0' || strcmp(token, "value") == 0)
            break;
        strcat(name, token);
        strcat(name, " ");
    }
    name[strlen(name) - 1] = '\0';
    if (strcmp(token, "value") == 0) {
        value[0] = '\0';
        for (;;) {
            ptr = ParseToken(ptr, token);
            if (*token == '\0')
                break;
            strcat(value, token);
            strcat(value, " ");
        }
        value[strlen(value) - 1] = '\0';
    }
    if (strcmp(name, "Hash") == 0) {
        AllocTrans(atoi(value));
    } else if (strcmp(name, "Clear Hash") == 0) {
        ClearTrans();
    }
}

void ParsePosition(POS *p, char *ptr)
{
    char token[80], fen[80];
    UNDO u[1];

    ptr = ParseToken(ptr, token);
    if (strcmp(token, "fen") == 0) {
        fen[0] = '\0';
        for (;;) {
            ptr = ParseToken(ptr, token);
            if (*token == '\0' || strcmp(token, "moves") == 0)
                break;
            strcat(fen, token);
            strcat(fen, " ");
        }
        SetPosition(p, fen);
    } else {
        ptr = ParseToken(ptr, token);
        SetPosition(p, START_POS);
    }
    if (strcmp(token, "moves") == 0)
        for (;;) {
            ptr = ParseToken(ptr, token);
            if (*token == '\0')
                break;
            DoMove(p, StrToMove(p, token), u);
            if (p->rev_moves == 0)
                p->head = 0;
        }
}

void ParseGo(POS *p, char *ptr)
{
    char token[80], bestmove_str[6], ponder_str[6];
    int wtime, btime, winc, binc, movestogo, time, inc, pv[MAX_PLY];

    move_time = -1;
    pondering = 0;
    wtime = -1;
    btime = -1;
    winc = 0;
    binc = 0;
    movestogo = 40;
    for (;;) {
        ptr = ParseToken(ptr, token);
        if (*token == '\0')
            break;
        if (strcmp(token, "ponder") == 0) {
            pondering = 1;
        } else if (strcmp(token, "wtime") == 0) {
            ptr = ParseToken(ptr, token);
            wtime = atoi(token);
        } else if (strcmp(token, "btime") == 0) {
            ptr = ParseToken(ptr, token);
            btime = atoi(token);
        } else if (strcmp(token, "winc") == 0) {
            ptr = ParseToken(ptr, token);
            winc = atoi(token);
        } else if (strcmp(token, "binc") == 0) {
            ptr = ParseToken(ptr, token);
            binc = atoi(token);
        } else if (strcmp(token, "movestogo") == 0) {
            ptr = ParseToken(ptr, token);
            movestogo = atoi(token);
        }
    }
    time = p->side == WC ? wtime : btime;
    inc = p->side == WC ? winc : binc;
    if (time >= 0) {
        if (movestogo == 1) time -= Min(1000, time / 10);
        move_time = (time + inc * (movestogo - 1)) / movestogo;
        if (move_time > time)
            move_time = time;
        move_time -= 10;
        if (move_time < 0)
            move_time = 0;
    }
    Think(p, pv);
    MoveToStr(pv[0], bestmove_str);
    if (pv[1]) {
        MoveToStr(pv[1], ponder_str);
        printf("bestmove %s ponder %s\n", bestmove_str, ponder_str);
    } else
        printf("bestmove %s\n", bestmove_str);
}

User923005
Posts: 616
Joined: Thu May 19, 2011 1:35 am

Re: UCI Programming

Post by User923005 » Tue Jan 15, 2013 9:42 pm

I should mention one important detail. It is a very good idea to turn off all buffering for your interface for standard input and standard output.
The speed of the text transactions is not important, and buffering can cause all sorts of subtle problems.
It is also true that Posix and Windows {sometimes} buffer I/O differently, so if you have buffering enabled, your program will behave differently in different environments.
For open source programs that fail to do this, I almost always make my own version that has buffering turned off.
Otherwise you will see strange effects like programs becoming disconnected in some way from the GUI or waiting for input when none is due or not delivering output when they ought to.
If you run your program at very fast time control (as is often the case with testing) the problem will be enlarged.

User923005
Posts: 616
Joined: Thu May 19, 2011 1:35 am

Re: UCI Programming

Post by User923005 » Tue Jan 15, 2013 9:45 pm


CDaley11
Posts: 42
Joined: Thu Jan 10, 2013 6:23 am
Real Name: Christian Daley

Re: UCI Programming

Post by CDaley11 » Tue Jan 15, 2013 10:51 pm

Cool, looks like it won't be as hard as I thought. Thanks!

lucasart
Posts: 201
Joined: Mon Dec 17, 2012 1:09 pm
Contact:

Re: UCI Programming

Post by lucasart » Wed Jan 16, 2013 1:58 am

User923005 wrote:I should mention one important detail. It is a very good idea to turn off all buffering for your interface for standard input and standard output.
The speed of the text transactions is not important, and buffering can cause all sorts of subtle problems.
It is also true that Posix and Windows {sometimes} buffer I/O differently, so if you have buffering enabled, your program will behave differently in different environments.
For open source programs that fail to do this, I almost always make my own version that has buffering turned off.
Otherwise you will see strange effects like programs becoming disconnected in some way from the GUI or waiting for input when none is due or not delivering output when they ought to.
If you run your program at very fast time control (as is often the case with testing) the problem will be enlarged.
That is a good point. I remember hours of headache trying to figure out why my code (in plain C back then) didn't work with a GUI, but did work in command line when typing the commands myself.
But you do not have to set buffering off. In plain C, you can simply do:

Code: Select all

printf(...);
fflush(stdout);
And you can code something like my_printf() that does it (though you will have to handle va_list stuff), so that you never forget to the fflush().

In C++, is seems to be enough to do:

Code: Select all

std::cout << ... << std::endl;
It seems (at least on my Linux machine) that std::endl flushes stdout.

If I understand correctly, he uses Java, so none of our C or C++ code will be of any help. I don't know how to read a line from stdin or write a line to stdout in Java, but I'm sure there are simple functions for that. The only thing to watch for (in your Java manual or whatever) is when they talk about I/O buffering.

Conclusion: Regardless of the language you use, and how you choose to do it DON'T FORGET TO FLUSH !

PS: That's what she always says :lol:
"Talk is cheap. Show me the code." -- Linus Torvalds.

User923005
Posts: 616
Joined: Thu May 19, 2011 1:35 am

Re: UCI Programming

Post by User923005 » Wed Jan 16, 2013 2:54 am

You are right that std::endl flushes the buffer (it is demanded by the ISO C++ standard that endl flushes a buffered stream of any kind). Hence for any system output will be flushed (unless the compiler or library is broken and in that case you can contain to your vendor).

Hence, that approach is slightly more efficient than turning off buffering (since you can still use buffered output and yet I/O will be correct).
Both fflush(stdout) and std::endl require discipline on the part of the user (unless they are encapsulated in some generic ChessWrite() routine) and so for a beginner, I think it is better to just turn it off. I would be very surprised if you could measure any performance difference in terms of Elo. And it is easy to forget just one time to do it.

With stockfish, which is carefully programmed, I found that EPD analysis at high speed would become disconnected from Arena when analyzing problem sets containing thousands of EPD problems. So I turned off buffering even for a carefully programmed engine like stockfish. I don't know if it is still important, since I only saw a big problem with it some time ago. But I always make my own version with buffering turned off anyway.

CDaley11
Posts: 42
Joined: Thu Jan 10, 2013 6:23 am
Real Name: Christian Daley

Re: UCI Programming

Post by CDaley11 » Wed Jan 16, 2013 3:06 am

Also, could someone please recommend a good UCI interface for use with my engine? Jose chess, just like sigma chess, was written for a power pc mac which does not work on OSX Lion.

Post Reply