UCI Programming
UCI Programming
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).
-
- Posts: 616
- Joined: Thu May 19, 2011 1:35 am
Re: UCI Programming
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/
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/
Re: UCI Programming
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.
-
- Posts: 616
- Joined: Thu May 19, 2011 1:35 am
Re: UCI Programming
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:
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);
}
-
- Posts: 616
- Joined: Thu May 19, 2011 1:35 am
Re: UCI Programming
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.
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.
-
- Posts: 616
- Joined: Thu May 19, 2011 1:35 am
Re: UCI Programming
The UCI specification is here:
http://www.shredderchess.com/chess-info ... rface.html
http://www.shredderchess.com/chess-info ... rface.html
Re: UCI Programming
Cool, looks like it won't be as hard as I thought. Thanks!
Re: UCI Programming
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.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.
But you do not have to set buffering off. In plain C, you can simply do:
Code: Select all
printf(...);
fflush(stdout);
In C++, is seems to be enough to do:
Code: Select all
std::cout << ... << std::endl;
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
"Talk is cheap. Show me the code." -- Linus Torvalds.
-
- Posts: 616
- Joined: Thu May 19, 2011 1:35 am
Re: UCI Programming
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.
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.
Re: UCI Programming
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.