Paul wrote (on the computer-go list):
About asynchronous move generation.
I'd propose something like this. Add some form for asynchronous responses. E.g. '=[id] ...' means success, '?[id] ...' means error (this is as now) and '%[id] ...' means asynchronous response. Maybe for asynchronous commands it makes sense to make id mandatory. Responses must not be mixed, i.e. you cannot start a synchronous response inside asynchronous or vice versa (else they will be impossible to parse properly.)
This is in the same direction I've been thinking. To make it consistent with GTP version 2 and fully backwards compatible it can be designed like this:
* Asynchronous responses from the engine are encoded like Paul proposes but with "!" as first character (there is precedence in a protocol GTP was originally inspired by). Naturally one response must be complete before starting on another, whether synchronous or asynchronous.
* Asynchronous messages may only be sent as responses to asynchronous commands. Thus a GTP version 2 controller would never become confused by asynchronous responses it knows nothing about since it would only send synchronous commands.
* Asynchronous responses have the same id as the command initiating them. The id may as usual be omitted by the controller if it is confident that it don't need it.
* An asynchronous command must first be acknowledged by a synchronous response (would usually be an empty response if there are no problems and otherwise an error response).
* An asynchronous command may additionally have zero, one, or multiple asynchronous responses, depending on the nature of the command and the situation. For example an asynchronous genmove command would normally have one asynchronous response, but zero if the command is aborted. A command requesting debug output could produce any number of asynchronous responses.
* Whether an engine is capable of doing asynchronous responses can as usual be tested by known_command or list_commands to see whether it supports specific (asynchronous) commands.
* The asynchronous genmove command would be named async_genmove. In contrast to the normal genmove command it would only generate a move, not actually play it. (To keep sanity and avoid undos when the ordering of move generation and abort command depends on race conditions.)
* To abort the asynchronous genmove, the controller should send the (synchronous) command abort_async_genmove. If the engine has not returned the asynchronous genmove response before responding to the abort command, it is no longer allowed to return a move. I'm open for discussion on the exact name and whether it should return an error if the move had already been sent (I don't think it should).
Example 1: Asynchronous genmove with an intervening synchronous command. Command 3 is sent after receiving the asynchronous move. Controller: 1 async_genmove black 2 version 3 play black E5
Engine: =1
=2 4.0
!1 E5
=3
Example 2: Asynchronous genmove, aborted. The controller plays an own move instead. Controller: 1 async_genmove black 2 abort_async_genmove 3 play black C3
Engine: =1
=2
=3
Example 2b (BAD): Like example 2, but the engine makes an incorrect response. Controller: 1 async_genmove black 2 abort_async_genmove 3 play black C3
Engine: =1
=2
!1 E5
=3
Example 3: Like example 2, but abort command comes too late. Controller: 1 async_genmove black 2 abort_async_genmove 3 play black C3
Engine: =1
!1 E5
=2
=3
Example 4: Asynchronous genmove sent to an engine not supporting it. Controller: 1 async_genmove black
Engine: ?1 unknown command
/Gunnar
Gunnar Farneback wrote:
Example 3: Like example 2, but abort command comes too late. Controller: 1 async_genmove black 2 abort_async_genmove 3 play black C3
Engine: =1
!1 E5
=2
=3
Maybe it should then read
?2 not in progress
It also makes sense to forbid an async_genmove (or simple genmove for that matter) until the previous genmove/async_genmove has finished. The engine should just fail with a predefined error string; I think it is really cumbersome for the engine to try synchronization here and serves little purpose.
Paul
On Wed, 2007-03-07 at 22:53 +0100, Gunnar Farneback wrote:
- To abort the asynchronous genmove, the controller should send the (synchronous) command abort_async_genmove. If the engine has not returned the asynchronous genmove response before responding to the abort command, it is no longer allowed to return a move. I'm open
for discussion on the exact name and whether it should return an error if the move had already been sent (I don't think it should).
But what about race conditions here? The engine may be responding to the async_genmove command an instant before it realizes an abort command just arrived. In this case it would be violating your rule but it wouldn't be anyone's fault.
I agree that this should not be considered an error. It should simply be ignored by the controller.
I also agree that genmove should not expect the engine to make the move. Even though it's not a a big deal I think the original GTP protocol should also have specified it this way. A specific command should be sent to actually apply the move to the game board. Of course this is more important with the asynchronous version.
- Don
Don Dailey wrote:
On Wed, 2007-03-07 at 22:53 +0100, Gunnar Farneback wrote:
- To abort the asynchronous genmove, the controller should send the (synchronous) command abort_async_genmove. If the engine has not returned the asynchronous genmove response before responding to the abort command, it is no longer allowed to return a move. I'm open
for discussion on the exact name and whether it should return an error if the move had already been sent (I don't think it should).
But what about race conditions here? The engine may be responding to the async_genmove command an instant before it realizes an abort command just arrived. In this case it would be violating your rule but it wouldn't be anyone's fault.
There is no race condition because commands _are_ read synchronously. But responses _may be_ sent asynchronously.
Paul
I am still frightened by your plans, how to permit asynchronous commands in GTP. Here are some remarks and questions:
genmove is only one of many commands that the user might want to abort. We use GTP extension commands for starting life and death searches or other lengthy computations and many of them can be aborted at any time. Do you really want a sync and async version and a different abort command for each of them? Potentially every GTP command can be a candidate for aborting. If a controller provides support for engine-specific extension commands without specifically knowing what they do (like GoGui's Analyze Commands), there should still be a way to let the user send an abort request and let those commands abort that are able to.
Why is it necessary that the async genmove returns an immediate response?
- Markus