Handling xboard new game race condition in protover 1 engine

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Handling xboard new game race condition in protover 1 en

Post by bob »

matthewlai wrote:
bob wrote:
matthewlai wrote:
hgm wrote:How do you solve the problem of multiple input sources and timer events? Create a separate input thread for every engine, and let it do blocking input? And use SIGALRM to set up the next timer event?

If you have solved these problems, it might be easier to just hack the GUI-dependence out of XBoard. By making all non-static routines in gtk/xoptions.c into dummies (and deleting the static ones). Basically you would only have to provide a new routines AddInputSource and RemoveInputSource, and rewite the timer handling in gtk/xtimer.c. And delete all the gtk-referring code out of gtk/xboard.c.

The timer handling seems the most tricky, as there can be several timeouts pending simultaneously in XBoard. (A dedicated one for making the clock tick, and a general one for all other events.)
For multiple input sources I am using select(). It's easier for me because I don't need to support Windows.

I am doing select() with timeouts, and check the clocks at each timeout.

I think the main difference is that I am not making it event driven. So my main loop looks like this (pseudo code) -

Code: Select all

while (game_still_ongoing)
{
	select(both_engine’s_input, timeout = 100ms);

	// these are asynchronous - they will only process what’s available already
	// in the pipes, and not block
	engine1.process_pending_inputs();
	engine2.process_pending_inputs();

	if (either_engine_made_a_move_or_made_draw_offer)
	{
		pass_it_on_to_the_other_engine();
	}

	check_clocks();
}
It's very different from how xboard is organized (since xboard is event driven and need to handle user input).
One other note. I decided to give up on passing draw offers between programs. Too many do it wrong, so I just gave up. My referee ends a game when a draw state is seen (50 moves, 3-fold rep, stalemate, insufficient material) otherwise "draw" is ignored, which solved a lot of problems. I can disable this, but it only seems to be safe with Crafty vs Crafty matches, to specifically test accepting draw offers which I do a bit differently from some.
How do engines do it wrong? I thought all engines have to do is "offer draw" when they see a draw offer and want to take it.

Do they assume the game is over?

I am hesitant to ignore draw offers, because I am very interested in saving time (to play more games), and if engines agree to draw, it would save a lot of time in some cases.
They can make a move, then send "draw" but it is no longer their move. And they can claim draws when they do not exist. I just disabled it and had no further problems.
matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

Re: Handling xboard new game race condition in protover 1 en

Post by matthewlai »

bob wrote:
matthewlai wrote:
bob wrote:
matthewlai wrote:
hgm wrote:How do you solve the problem of multiple input sources and timer events? Create a separate input thread for every engine, and let it do blocking input? And use SIGALRM to set up the next timer event?

If you have solved these problems, it might be easier to just hack the GUI-dependence out of XBoard. By making all non-static routines in gtk/xoptions.c into dummies (and deleting the static ones). Basically you would only have to provide a new routines AddInputSource and RemoveInputSource, and rewite the timer handling in gtk/xtimer.c. And delete all the gtk-referring code out of gtk/xboard.c.

The timer handling seems the most tricky, as there can be several timeouts pending simultaneously in XBoard. (A dedicated one for making the clock tick, and a general one for all other events.)
For multiple input sources I am using select(). It's easier for me because I don't need to support Windows.

I am doing select() with timeouts, and check the clocks at each timeout.

I think the main difference is that I am not making it event driven. So my main loop looks like this (pseudo code) -

Code: Select all

while (game_still_ongoing)
{
	select(both_engine’s_input, timeout = 100ms);

	// these are asynchronous - they will only process what’s available already
	// in the pipes, and not block
	engine1.process_pending_inputs();
	engine2.process_pending_inputs();

	if (either_engine_made_a_move_or_made_draw_offer)
	{
		pass_it_on_to_the_other_engine();
	}

	check_clocks();
}
It's very different from how xboard is organized (since xboard is event driven and need to handle user input).
One other note. I decided to give up on passing draw offers between programs. Too many do it wrong, so I just gave up. My referee ends a game when a draw state is seen (50 moves, 3-fold rep, stalemate, insufficient material) otherwise "draw" is ignored, which solved a lot of problems. I can disable this, but it only seems to be safe with Crafty vs Crafty matches, to specifically test accepting draw offers which I do a bit differently from some.
How do engines do it wrong? I thought all engines have to do is "offer draw" when they see a draw offer and want to take it.

Do they assume the game is over?

I am hesitant to ignore draw offers, because I am very interested in saving time (to play more games), and if engines agree to draw, it would save a lot of time in some cases.
They can make a move, then send "draw" but it is no longer their move. And they can claim draws when they do not exist. I just disabled it and had no further problems.
I'll keep that in mind.

In my program right now I only pass on the draw offer when it's their turn to move again.

They may not actually want a draw after seeing the opponent's move, but that's their fault!
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Handling xboard new game race condition in protover 1 en

Post by bob »

matthewlai wrote:
bob wrote:
matthewlai wrote:
bob wrote:
matthewlai wrote:
hgm wrote:How do you solve the problem of multiple input sources and timer events? Create a separate input thread for every engine, and let it do blocking input? And use SIGALRM to set up the next timer event?

If you have solved these problems, it might be easier to just hack the GUI-dependence out of XBoard. By making all non-static routines in gtk/xoptions.c into dummies (and deleting the static ones). Basically you would only have to provide a new routines AddInputSource and RemoveInputSource, and rewite the timer handling in gtk/xtimer.c. And delete all the gtk-referring code out of gtk/xboard.c.

The timer handling seems the most tricky, as there can be several timeouts pending simultaneously in XBoard. (A dedicated one for making the clock tick, and a general one for all other events.)
For multiple input sources I am using select(). It's easier for me because I don't need to support Windows.

I am doing select() with timeouts, and check the clocks at each timeout.

I think the main difference is that I am not making it event driven. So my main loop looks like this (pseudo code) -

Code: Select all

while (game_still_ongoing)
{
	select(both_engine’s_input, timeout = 100ms);

	// these are asynchronous - they will only process what’s available already
	// in the pipes, and not block
	engine1.process_pending_inputs();
	engine2.process_pending_inputs();

	if (either_engine_made_a_move_or_made_draw_offer)
	{
		pass_it_on_to_the_other_engine();
	}

	check_clocks();
}
It's very different from how xboard is organized (since xboard is event driven and need to handle user input).
One other note. I decided to give up on passing draw offers between programs. Too many do it wrong, so I just gave up. My referee ends a game when a draw state is seen (50 moves, 3-fold rep, stalemate, insufficient material) otherwise "draw" is ignored, which solved a lot of problems. I can disable this, but it only seems to be safe with Crafty vs Crafty matches, to specifically test accepting draw offers which I do a bit differently from some.
How do engines do it wrong? I thought all engines have to do is "offer draw" when they see a draw offer and want to take it.

Do they assume the game is over?

I am hesitant to ignore draw offers, because I am very interested in saving time (to play more games), and if engines agree to draw, it would save a lot of time in some cases.
They can make a move, then send "draw" but it is no longer their move. And they can claim draws when they do not exist. I just disabled it and had no further problems.
I'll keep that in mind.

In my program right now I only pass on the draw offer when it's their turn to move again.

They may not actually want a draw after seeing the opponent's move, but that's their fault!
I tried several "fixes" but the basic issue is, if the program doesn't do it right, it is not worth your time and effort to try to develop a kludge that covers up a bug.
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Handling xboard new game race condition in protover 1 en

Post by hgm »

matthewlai wrote:In my program right now I only pass on the draw offer when it's their turn to move again.

They may not actually want a draw after seeing the opponent's move, but that's their fault!
The correct way to do it is to pass on a draw offer of player A just before you pass the move of player A to player B. Doing it the other way around invites a race condition, where B might reply immediately to the move before he has even seen the draw offer. While FIDE rules would consider a move in reply to the offer a rejection.

But you should also not pass it on earlier, because any input could disturb the engine's pondering, and it would become possible to cheat by flooding your opponent with draw offers. So just backlog the offer until the GUI receives the move from the same player, and then send offer + move to the opponent.

A good implementation in the engine would not accept the offer until he has seen the following move, and make the decision based on the position after that move.
matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

Re: Handling xboard new game race condition in protover 1 en

Post by matthewlai »

hgm wrote:
matthewlai wrote:In my program right now I only pass on the draw offer when it's their turn to move again.

They may not actually want a draw after seeing the opponent's move, but that's their fault!
The correct way to do it is to pass on a draw offer of player A just before you pass the move of player A to player B. Doing it the other way around invites a race condition, where B might reply immediately to the move before he has even seen the draw offer. While FIDE rules would consider a move in reply to the offer a rejection.

But you should also not pass it on earlier, because any input could disturb the engine's pondering, and it would become possible to cheat by flooding your opponent with draw offers. So just backlog the offer until the GUI receives the move from the same player, and then send offer + move to the opponent.

A good implementation in the engine would not accept the offer until he has seen the following move, and make the decision based on the position after that move.
Thanks! That is very good to know.
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.