how to make a chess interface

Discussion of chess software programming and technical issues.

Moderator: Ras

kbhearn
Posts: 411
Joined: Thu Dec 30, 2010 4:48 am

Re: how to make a chess interface

Post by kbhearn »


Not that I've looked too closely (preferring commandline arguments because I can specify them from a script), but my impression is that with recent versions of XBoard, you can.
Well after hg mentioned that i should consider selecting a different font to fix the menus, i did go looking for such an option, perhaps he meant it was an athena option, but i have no clue where to go looking for that (nor do i care enough, i don't really use xboard all that much).

So tell me, what does "native" mean for an X11 application? GTK? Qt? Motif? When you get down to it the answer depends on your preferred window manager. They're all native. As is Athena (although I agree it's quite dated now).
Fair point, there's not one standard native dialog for opening a file. But the modern toolkits tend to use your chosen native window manager to make sure that windows fit the style of your system, and basic behaviour is well defined.
User avatar
ilari
Posts: 750
Joined: Mon Mar 27, 2006 7:45 pm
Location: Finland

Re: how to make a chess interface

Post by ilari »

hgm wrote:
ilari wrote:When it comes to usability and UI design, the customer is always right. It's extremely difficult to know what the users find intuitive or counter-intuitive without actually listening to them.
Sure. But if people are stuck in the stone age, there is just no improvement that will satisfy them. It is like someone saying "I don't like these Ferrari cars. It is very difficult to tie my horses to the front bumper, and the windscreen gets in the way of my whip, so that I have to stand up all the time to flog my horses over it, when I want to make any speed".

I don't think Mr. Ferrari would take such complaints seriously, and consider to drop the windscreen from the design. In the same spirit, I don't think it would be a good idea to listen to people that complain XBoard is user-unfriendly "because you need to edit ini files".

I happen to think XBoard does configure 'like a breeze', and am open to suggestions from people that have felt the breeze, and have ideas to make that breeze blow even smoother. But criticism from people that are using version 4.6.2 like it was 4.2.7, and then criticize its 4.2.7-ness, is really not relevant.
Well that's the thing right there: even if the user's troubles seem completely self-inflicted, we (the authors/developers) have to be almost stupidly humble and take full responsibility. If a user can't accomplish something with my software there could be many reasons for it: maybe the widget layouts are non-standard and thus confusing, maybe the widget/feature names are not what the user expects, perhaps the documentation is lacking or misleading, etc. If long-time XBoard users can't use the new versions properly it could be because the changes between versions haven't been communicated to the users well enough, not documented well enough, or the new or changed features do not manifest themselves in an obvious way when using the GUI.

I've attended a few university courses on UI design, user-centric software development and testing, and got to do a lot of interviews and prototype testing with focus groups. Each time it surprised me how difficult it was for the focus group to complete the seemingly simple tasks that we asked them to do. Things that seemed obvious to us were definitely not obvious to the focus groups.
User avatar
hgm
Posts: 28464
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: how to make a chess interface

Post by hgm »

kbhearn wrote:Well after hg mentioned that i should consider selecting a different font to fix the menus, i did go looking for such an option, perhaps he meant it was an athena option, but i have no clue where to go looking for that (nor do i care enough, i don't really use xboard all that much).
XBoard has zillions of options, amongst which some that I don't consider suitable for general use. As XBoard 4.2.7 virtually did not have any menu dialogs at all (the few on/off options that could be set through the menu all were toggled by individual items in the main menu), unlike WinBoard, which had many, my policy has been to define a set of 'core features' that users are likely to need (e.g. because they are used to finding that functionality in other GUIs), and put those in menu dialogs. For the distribution over dialogs I tried to conform to the design of WinBoard, where possible, so that WinBoard users would find things where they expect them.

I am reasonably satisfied with the latest version (as now in the master branch of the GNU repository); almost everything I consider useful can be controlled from the menu dialogs. Otherwise I would have added more, because it is in most cases totally trivial to add controls for existing command-line options. (Like adding a single line in the initializer for the array defining the dialog, and occasionally adding a call to an existing routine to initialize something based on the new value of the option in the existing OK handler for the dialog.)

Selecting fonts is indeed one exception, which has always been nagging me, as WinBoard does have a dialog for this. I don't know enough about X11 font handling to pull this off in a way that could satisfy the user, however. The arguments to font command-line options (-messageFont would control the menu texts) are absolutely horrible strings of 30 characters or more, that seem to be used as matching patterns for fonts in th X-server. To provide a dialog where the user could enter such strings duing the session would obviously be completely pointless. To make it useful, the user should be allowed to selet the separate parts of the string (point side, typeface, boldness etc.) separately, and the X-font string should be built from it. And the typeface should be selectable from a combobox, to indicate what fonts are available. But I have no idea how to interrogate the X-server for available fonts, or even what the approximately 20 fields in the font-string indicate. So I sort so gave up on this. It is the major 'black mark' left on XBoard, however. But of course I never use XBoard myself.
User avatar
hgm
Posts: 28464
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: how to make a chess interface

Post by hgm »

ilari wrote:Well that's the thing right there: even if the user's troubles seem completely self-inflicted, we (the authors/developers) have to be almost stupidly humble and take full responsibility. If a user can't accomplish something with my software there could be many reasons for it: maybe the widget layouts are non-standard and thus confusing, maybe the widget/feature names are not what the user expects, perhaps the documentation is lacking or misleading, etc. If long-time XBoard users can't use the new versions properly it could be because the changes between versions haven't been communicated to the users well enough, not documented well enough, or the new or changed features do not manifest themselves in an obvious way when using the GUI.
In principle I gree with everything you say, but I am just out of ideas on how to communicate things to these long-term XBoard users. People would rather die than read a README file or manual, and What's New pages going with the release announcement they will of course never read. I could try very intrusive things, like for every new game pop up a dialog with a short hint on how to use new features, (which they would almost certainly close without reading), and hide very deep in the README file how to switch it off, so they could not possibly find it without reading the entire page. Or block usage of XBoard ater install untill the can answer a long list of multiple-choice questions on the working of new features, and only unlock it when they get all the questions right. But of course that would simply make them switch to another interface where they don't have to do that...

Even breaking backward compatibility of the UI, so that the old ways of doing things no longer work, is not an option: the old ways are essentially work-arounds that bypass XBoard. If people specify Polyglot as an engine, there is little I can do against that. They could continue to do that even if XBoard would support UCI directly.

As this seems an unsolvable problem, I decided it would be better to ignore it completely.
kbhearn
Posts: 411
Joined: Thu Dec 30, 2010 4:48 am

Re: how to make a chess interface

Post by kbhearn »

Indeed, support for fonts in plain ol' x11 is appalling. Which is another strongpoint of using a modern gui toolkit, they come with more useful ways to access fonts. Please don't take my complaints about xboard personally, it is what it is and its longevity is shocking. For it to be more would be a major project. My point has mainly been that the gui toolkit underlying xboard is its biggest problem and that it'd be simpler imo to just write a new interface using a modern gui toolkit than to retool xboard to use gtk, qt, xul, or whatever else the author finds familiar to work with. There really shouldn't be that much 'business code' - well except for the monstrosity that is chess engine protocol parsers.
User avatar
hgm
Posts: 28464
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: how to make a chess interface

Post by hgm »

kbhearn wrote:There really shouldn't be that much 'business code' - well except for the monstrosity that is chess engine protocol parsers.
That really is a total misjudgement. It is virtually all business code (> 95%?). And the engine protocol parser is only a very small part. The ICS protocol parser is already a lot larger, and is the true monstrosity.

Add to that that the busines code is very complex and bug prone, while most of the interface code does a straightforward low-level job that either works or doesn't (in which case you would notice it immediately, as that code is invoked for almost anything you do).

In my estimate it would require about 100 times more effort to rewrite from scratch than to retool.
lucasart
Posts: 3243
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: how to make a chess interface

Post by lucasart »

Although the below code works, I think I'll need to introduce a non blocking read and timeout mechanism. It seems that fgets is a typical blocking IO function. If an engine hangs, and I'm just waiting with my fgets for something that I never receive, my interface would hand forever, and a better approach would be to have a non blocking IO read, and a timeout mechanism to detect a time loss.

The usual input_available() function that engine use should do the trick (using the select() kernel call on POSIX). But is there no way of doing this with the standard C library ?
lucasart wrote:OK, I think I now have the first stone. A Process "class" (written in C99, because C++ is already irritating me now)

process.h

Code: Select all

#pragma once
#include <stdbool.h>
#include <stdio.h>
#include "unistd.h"

typedef struct {
	pid_t pid;
	FILE *in, *out;
} Process;

bool process_create(Process *p, const char *cmd);
bool process_destroy(Process *p);

bool process_readln(const Process *p, char *buf, size_t n);
bool process_writeln(const Process *p, char *buf);
process.c

Code: Select all

#include "process.h"
#include <sys/types.h>
#include <signal.h>

bool process_create(Process *p, const char *cmd)
{
	int readpipe[2], writepipe[2];
	#define PARENT_READ		readpipe[0]
	#define CHILD_WRITE		readpipe[1]
	#define CHILD_READ		writepipe[0]
	#define PARENT_WRITE	writepipe[1]

	if (pipe(readpipe) < 0 || pipe(writepipe) < 0)
		return false;

	p->pid = fork();
	
	if (p->pid == 0) {
		// in the child process
		close(PARENT_WRITE);
		close(PARENT_READ);

		if (dup2(CHILD_READ, STDIN_FILENO) == -1)
			return false;
		close(CHILD_READ);

		if (dup2(CHILD_WRITE, STDOUT_FILENO) == -1)
			return false;
		close(CHILD_WRITE);

		if (execlp(cmd, cmd, NULL) == -1)
			return false;
	}
	else if (p->pid > 0) {
		// in the parent process
		close(CHILD_READ);
		close(CHILD_WRITE);

		if (!(p->in = fdopen(PARENT_READ, "r")))
			return false;

		if (!(p->out = fdopen(PARENT_WRITE, "w")))
			return false;
		
		setvbuf(p->in, NULL, _IONBF, 0);
		setvbuf(p->out, NULL, _IONBF, 0);
	} else
		// fork failed
		return false;
	
	return true;
}

bool process_destroy(Process *p)
{
	// close the parent side of the pipes
	fclose(p->in);
	fclose(p->out);
	
	// kill the child process
	return kill(p->pid, SIGKILL) >= 0;
}

bool process_readln(const Process *p, char *buf, size_t n)
{
	return fgets(buf, n, p->in) >= 0;
}

bool process_writeln(const Process *p, char *buf)
{
	return fputs(buf, p->out) >= 0;
}
Now we can spawn and kill processes and talk to them in a high level manner:

Code: Select all

#include <process.h>
#include <string.h>

int main(int argc, char **argv)
{
	Process p;
	process_create(&p, "/home/lucas/Chess/DC/DC");
	
	char buf[0x100];
	process_writeln(&p, "uci\n");
	
	while (true) {
		process_readln(&p, buf, sizeof(buf));
		printf(buf);
		if (!strcmp(buf, "uciok\n"))
			break;
	}
	
	process_destroy(&p);
}
And the output is just as expected:

Code: Select all

id name DiscoCheck 3.7.1
id author Lucas Braesch
option name Hash type spin default 32 min 1 max 8192
option name Verbose type check default true
option name AutoClearHash type check default false
option name TimeBuffer type spin default 100 min 0 max 5000
uciok
PS: functions returning a bool return false on error. I don't think any calling function of the process machinery needs to have more info on why the pipes could not be created, the process couldn't be forked etc. they just need to know if all is good or not.
User avatar
ilari
Posts: 750
Joined: Mon Mar 27, 2006 7:45 pm
Location: Finland

Re: how to make a chess interface

Post by ilari »

lucasart wrote:Although the below code works, I think I'll need to introduce a non blocking read and timeout mechanism. It seems that fgets is a typical blocking IO function. If an engine hangs, and I'm just waiting with my fgets for something that I never receive, my interface would hand forever, and a better approach would be to have a non blocking IO read, and a timeout mechanism to detect a time loss.

The usual input_available() function that engine use should do the trick (using the select() kernel call on POSIX). But is there no way of doing this with the standard C library ?
You do NOT want to use non-blocking read calls, because then you'll have to poll for input, which is inefficient and laggy. If you poll for input with a non-blocking read call every 100 msec, then you may have a delay of 100 msec, resulting in time losses. If you poll more frequently (eg. every 2 msec) then you'll be wasting a lot of resources on polling.

You should create a separate thread whose sole purpose is to read input with blocking read calls. When the thread receives input it can call a callback function, send a signal or use some other way to notify the main thread that a new line of input is available.

Of course if the engine becomes unresponsive you need a way to cancel the blocking read calls in the reader thread. But fortunately that will happen automatically once you close the pipes or at least once you terminate the child process.
lucasart
Posts: 3243
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: how to make a chess interface

Post by lucasart »

ilari wrote:
lucasart wrote:Although the below code works, I think I'll need to introduce a non blocking read and timeout mechanism. It seems that fgets is a typical blocking IO function. If an engine hangs, and I'm just waiting with my fgets for something that I never receive, my interface would hand forever, and a better approach would be to have a non blocking IO read, and a timeout mechanism to detect a time loss.

The usual input_available() function that engine use should do the trick (using the select() kernel call on POSIX). But is there no way of doing this with the standard C library ?
You do NOT want to use non-blocking read calls, because then you'll have to poll for input, which is inefficient and laggy. If you poll for input with a non-blocking read call every 100 msec, then you may have a delay of 100 msec, resulting in time losses. If you poll more frequently (eg. every 2 msec) then you'll be wasting a lot of resources on polling.

You should create a separate thread whose sole purpose is to read input with blocking read calls. When the thread receives input it can call a callback function, send a signal or use some other way to notify the main thread that a new line of input is available.

Of course if the engine becomes unresponsive you need a way to cancel the blocking read calls in the reader thread. But fortunately that will happen automatically once you close the pipes or at least once you terminate the child process.
Thank you for the explanation. Although this is the right way of doing it, I will go for a simpler implementation as a first step:
* no threads
* play one game at a time
* use blocking I/O. if an engine hangs, then my interface will consequently hang as well.
Your post is on my to do list, but before I venture there I need to learn more about multi-threaded programming.

Another thing I will need is to block/unblock processes. Do you know how to do that (in POSIX) ? When I play A vs. B, if it's A's turn to play, I want to block B, let A calculate, and when it's B's turn I unblock it. The point is that B could be in a polling loop: if B uses blocking I/O (which is the correct way and I suppose any normal engine does that) then there's no real issue, but if it uses a loop with non blocking I/O it steals CPU ressources from A. Also I want to disable pondering in a forceful way, so as to control it.
kbhearn
Posts: 411
Joined: Thu Dec 30, 2010 4:48 am

Re: how to make a chess interface

Post by kbhearn »

Threads in their most basic form aren't that difficult. In this case, the gui library most likely provides the facilities needed to write it without any headache at all.

In Qt, you'd merely subclass a QThread, replace the virtual run() function with your blocking read loop and add a signal to the class that you would emit whenever you get a line of input, Qt signals being by default threadsafe (if you force direct connection then they stop being threadsafe). Then you connect that signal to whatever wants lines of input, pass a reference to the pipe to a variable in the thread object, and call start(). Threaded programming done, no locks needed on your part (they're hidden inside the signal/slot implementation). I'd assume gtk would provide similar facilities for a simple task like this.