That is correct! There are intentional differences – the Revision Notes section mentions some of these.I don't think this is a correct description of UCI as it is in common use.
The current draft will require most clients to change, which may be too much to ask. My rationale was roughly:
- Despite having read HMK, I've never managed to get the UCI interface quite right. Every time I've tried to get Expositor to work with a different client (cutechess, lichess-bot, external-engine, liground, ...), I've needed to fix something or introduce a hack, and I've seen this happen to another engine dev as well. This suggested the need for a more comprehensive specification.
- Unifying the behavior of clients and engines may mean that some clients and engines will need to change (this is unavoidable). We may also want to change the behavior of clients or engines to have tidier solutions for some questions (this is merely convenient).
- Since there are many more engines than clients, it'd perhaps be better to change existing clients than to change existing engines.
The problem I see with HMK is that it leaves so many questions unanswered (which I've always found to be rather frustrating). Most of the ambiguity has to do with mixing synchronous and asynchronous interaction. Here a few questions, for example:The 'isready' command exists purely for the convenience of the client; [...] the engine doesn't derive any rights from it (and in particular not the right to receive no input).
- What should happen when the client sends "isready\n" twice in a single write, or twice before the engine has a chance to read the pipe? is one `readyok` sufficient, or does the engine need to send two replies? (Perhaps the underlying question is, are some messages meant to be handled sequentially, in a blocking manner, and some messages are meant to be handled as soon as they are read, skipping ahead of "sequential" messages?)
- HMK states that `isready` is used both "to wait for the engine to be ready again" and "to ping the engine to find out if it is still alive". When is it the former and when is it the latter? If the client sends `stop` and then `isready` in immediate succession, and the engine is still calculating, once the engine reads the message should it wait to answer with `readyok` until after it has stopped (because `isready` was sent after `stop`) or immediately (because "when the engine is calculating [...] the engine should immediately answer with `readyok` without stopping the search")?
- Can the engine assume that the client will never send `setoption` while it is calculating? That seems like a reasonable assumption, but HMK doesn't mention it. If it does (suppose the client sends `stop` and `setoption` in immediate succession, so that the engine recieves `setoption` while it is still calculating, before it has had time to stop, or perhaps the client simply sends `setoption` in the middle of a search), should the engine ignore the `setoption` or apply the change once it has stopped?
- HMK does not require the client to send `isready` before a search (only once at the beginning to "wait for the engine to finish initializing"). HMK also states that "if the engine receives a command which is not supposed to come, for example `stop` when the engine is not calculating, it should also just ignore it." By the same token, one might expect that the engine should ignore `go` if it receives `go` during a search. If the engine recieves a go message while it is still calculating (because the client sent `stop` and `go` in immediate succession and the engine read the go message before it had halted), should it ignore the go message? Or should it queue up an indefinite number of go messages? Or just one? (Perhaps the underlying question is, for the purposes of UCI, is the engine's current state defined by client messages (either defined by client messages being sent or defined by client messages being read), or defined by both client and engine messages, or is the engine's state defined by unobservable, internal variables?)
I had tried modeling the interaction between client and engine as a collection of events (client writes to a pipe, engine writes to a pipe, clients reads from a pipe, engine reads from a pipe) and happens-before relationships, but the diagram got rather ugly rather quickly.
I usually think of message-passing between the client and engine as happening more or less instantaneously, but that's not actually the case, so the current model used by in draft was an attempt to present the client-engine system as having a single, coherent global state (rather than a disjoint pair of client and engine states) while still being consistent and technically correct.
There is probably a better way to model it, though.
This had crossed my mind, and one of the Outstanding Issues is the question Should the client be allowed to queue up messages in the halt state? I'm not entirely sure how to reconcile this with the current model, where transitions occur when messages are sent, which is perhaps another indication that it needs some amount of rethinking.Some existing clients in fact send stop + position + go ponder simultaneously.
And I still need to get around to incorporating pondering :/
This was one of the intentional changes, meant primarily to simplify the state+transition model (and secondarily to reduce the surface area of undefined behavior). In such cases, I generally tried to match the behavior of common engines (Stockfish and Komodo, for example).Your state diagram also ignores that 'go' is illegal without a preceding 'position'.