Sashité for Developers
  1. Sashité for Developers
  2. Specifications
  3. SEI
  4. 1.0.0

Sashité Engine Interface (SEI) Specification


1. Status of this document

This document is a release candidate: its normative text is complete, and only errata are expected before 1.0.0. The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as normative requirements. Passages marked informative are not normative.

Capitalized terms (Active Player, Initial Position, Legal Move, Move, Pass Move, Piece, Position, Rule System, Style) are defined in the Glossary. This document does not redefine those terms.


2. Overview

SEI is a communication protocol between a host program (user interface, server, bot, test tool) and a game engine, for games of the chess family. It fills the role of UCI without being tied to any particular game.

2.1 Principles


3. Dependencies

Specification Version Purpose
FEEN 1.x Positions (host to engine)
PMN 1.x Moves
SIN 1.x Styles
I-JSON (RFC 7493) — Serialization
JSON Pointer (RFC 6901) — Error locations

SEI 1 depends on major version 1 of FEEN, PMN, and SIN. The engine emits only PMN and SIN.

Rules identifiers. A rules identifier is a non-empty, opaque string. It designates a rules document, which fixes everything SEI leaves to the game:

Engine and host both conform to the rules document. A canonical Move or Position is one written as the rules document prescribes; SEI itself adds one rule, which every rules document inherits: a drop names its Piece (F*e5, never *e5). An engine MAY announce several identifiers that designate the same rules. The identifiers of Sashité’s games are listed in the companion SEI — Rules registry (§15); a vendor SHOULD name its own after a name it controls, such as a domain or project name.


4. Terminology

Term Definition
Host Program that launches the engine and sends it requests
Engine Program that responds according to SEI; it computes Moves, and is not a rules library
Session Lifetime of the engine process, from launch until its standard output closes
Request Message from the host
Event Message from the engine
Terminal event done or error attached to a request
Receipt Instant at which the engine’s reader has read the complete line of a request (§5)
Active search Validated search whose terminal event has not yet been emitted
Searched position Position obtained by applying moves to position
Variation Legal sequence of Moves from the searched position, together with its score
first, second Player seats; first is the player whose pieces are uppercase in FEEN
Pairing Ordered pair of styles (style of first, style of second), written as two uppercase SIN letters ("WJ")
Rules identifier Non-empty, opaque string designating a rules document (§3), and thereby the game

5. Transport

Channel. The host launches the engine as a child process. The protocol uses the engine’s standard input and standard output. Its standard error is free-form and serves diagnostics.

Launch.

Lines.

Streams.

End of session.


6. Messages

6.1 Forms

{"id":1,"op":"…", …}    request (host → engine)
{"re":1,"ev":"…", …}    event   (engine → host)

In examples, messages are indented and prefixed with → or ←. On the wire, each message fits on one line.

6.2 Request envelope

A request carries:

6.3 Numbers

Every number has an integer value, with an absolute value of at most 2^53 − 1. Durations are in milliseconds, and each field specifies its domain.

6.4 Tolerance

Strict requests. Any field defined neither by the negotiated version nor by an announced feature causes invalid, including within a nested object. An unknown operation also causes invalid.

The hello exception. hello ignores its unknown fields.

Tolerant events. The host MUST ignore unknown fields and events of unknown type (ev), and apply the fallbacks of §9.5. An engine MAY add fields named x_<vendor>_<name> to its events.

Evolution. The rules of evolution are given in §13.

6.5 Envelope errors

Blank lines aside (§5), the following are envelope errors:

The engine then emits an invalid error without re, which is fatal (§7). All other errors concern content and are attached to their request.

6.6 Permanent core

The transport (§5), the envelope (§6.1 to §6.5), hello, ping, and the error without re are identical in every version of SEI. Everything else is versioned: the descriptive fields of the done of hello (§8.1) refer to the version it returns.


7. Execution model (normative)

  1. Opening. Until a hello has succeeded, only hello and ping are accepted. A hello succeeds when its done carries a version. A hello that did not succeed MAY be sent again.
  2. One thing at a time. The engine processes requests other than ping one by one, in order of receipt. Processing a request begins with its validation, which is not interruptible and whose cost is bounded by the line (§5). Before processing one, it ends the active search, if any, by emitting its done with the best available result (§8.4).
  3. ping stands apart. The engine answers ping at its receipt, whatever processing is under way. ping never changes state.
  4. One request, one end. During the session, every request with a valid envelope receives exactly one terminal event carrying its id. No event is attached to it afterwards.
  5. Order. Terminal events follow the order of requests. The only exception: the done of a ping may precede that of any request received before it and still in progress.
  6. Framing. Every non-terminal event attached to a request appears between the receipt of that request and its terminal event.
  7. Fatality. The presence of re alone determines what follows.
    • An error carrying re is never fatal. The configuration and the presence of an active search are those that a request without effect would have left, apart from the interruption required by rule 2.
    • An error without re is always fatal (§5). An engine that can no longer guarantee its correct operation MUST emit one, then terminate.

7.1 Transitions (summary)

From Event To
Any live state ping Unchanged
Awaiting hello done of hello carrying version Ready
Awaiting hello done of hello without version, or error with re Awaiting hello
Ready Valid search on a non-terminal position Searching
Ready Any other request, or error with re Ready
Searching End of the search Ready
Searching Any other request Ready, then processing of the request
Any live state Error without re, closing of input or output End of session

8. Requests

8.1 hello

→ {"id":1,"op":"hello","versions":[1],"host":{"name":"Example host","version":"2.0"}}
← {"re":1,"ev":"done","version":1,"versions":[1],
   "engine":{"name":"Example","version":"0.1.0"},
   "rules":{"sashite.sanki.kernel/1":{"pairings":["WW","WJ","JW","JJ"]}},
   "features":{"lines":{"max":64},"roots":{}},
   "options":{
     "threads":{"type":"int","default":1,"min":1,"max":64},
     "hash":{"type":"int","default":16,"min":1,"max":4096}}}

The request carries:

Field Required Content
versions Yes Non-empty array of integers greater than or equal to 1: the major versions the host speaks
host No name (required), version: how the engine’s diagnostics name the host

A hello whose versions is missing or malformed receives invalid (with re); a malformed host is ignored. Any other hello answers with a done that describes the engine:

Field Content
version Highest common version; absent if there is none
versions Major versions the engine speaks
engine name (required), version, author, about
rules Implemented rules identifiers (§3), at least one, each with an object that MAY carry its pairings
features Features and their parameters (§10)
options Configurable options (§8.3)

8.2 ping

→ {"id":9,"op":"ping"}
← {"re":9,"ev":"done"}

ping is accepted in every state of the session. The engine answers it at its receipt, without waiting for the end of the processing under way (opening, configuration, or search), and SHOULD do so within 50 ms. Its form is frozen: it carries no field other than id and op.

Monitoring. The host SHOULD monitor the engine’s liveness with ping, especially during long operations. Once the engine has answered a first ping, a ping left unanswered beyond a grace period much longer than 50 ms, for example one second, indicates a hung engine: the host SHOULD then close the session. The duration of hello and configure is not timed; that of a search is bounded by its budget (§8.4).

Informative. ping attests the engine’s reading loop, not its search: an engine whose search threads spin answers ping and never emits done. The budget of §8.4 is what protects the host from that; the done of hello signals availability.

8.3 configure

→ {"id":2,"op":"configure","options":{"threads":4,"hash":256}}
← {"re":2,"ev":"done"}

configure declares the complete configuration of the engine.

Option schema.

type Descriptor Constraints
bool default —
int default, min, max min ≤ default ≤ max
string default, values (optional) If values is present, it is non-empty and contains default, and every configured value MUST belong to it

Every descriptor MAY carry about, a string intended for humans. Option names SHOULD be in snake_case. Two names are reserved, and an engine that exposes these notions MUST use them:

→ {"id":3,"op":"search",
   "rules":"sashite.sanki.kernel/1",
   "position":"4k^3/8/8/8/8/8/8/R3K^3 / W/w",
   "moves":["a1-a4","e8-d8"],
   "clock":{"own":{"deadline":59000,"remaining":60000,"inc":1000},
            "opp":{"remaining":58000,"inc":1000},
            "overhead":400}}

Fields

Field Required Content
rules Yes Announced rules identifier
position Yes FEEN
moves No ([]) PMN Moves played from position
counters No ({"halfmove":0,"ply":1}) Counters at position
clock No Clock
limits No Caps
fresh No (false) Answer as a fresh process would

Clock

Field Required Content
own Yes Side of the Active Player of the searched position
opp No Side of the other player
overhead No (0) Latency outside the search: from the emission of the request to its receipt, and from the emission of done until the player’s clock actually stops (transmission, validation, publication…)

A side describes the current period of the player’s clock, and what follows it.

Side field Required Domain Meaning
deadline own: yes; opp: forbidden ≥ 0 Time left, at the emission of the request, before the player loses on time — or less, if the host wishes to cap the search. The engine’s share of it is deadline − overhead, counted from receipt (§Stopping)
remaining Yes ≥ 0 Budget left in the current period
inc No (0) ≥ 0 Time granted for each Move of the current period, whether credited before or after the Move
togo No ≥ 1 Moves remaining in the current period, including the current one; present if and only if the period has a quota of Moves
time With togo ≥ 0 Nominal duration of the current period: the budget it grants each time it begins
carry No (true) Boolean Whether the budget left when the quota is reached is carried into the next period (true) or discarded (false); requires togo
next No ([]) Array The periods that follow the current one, in order; each an object {"time","inc","moves","carry"} with the meanings above: time required, inc optional (0), moves (≥ 1) optional, carry only with moves (true)

The host measures all durations at the moment of sending, according to the time rules it applies, and folds into remaining the time already charged to the player for this Move. The engine counts them down from receipt. deadline protects against losing on time; the other side fields serve to plan the use of time, under the following model.

Periods. After the Move that reaches the quota of a period, the clock enters the first period of next — or the current period again, if next is empty — with that period’s time, plus the budget left if carry. When a period without a quota is exhausted, the overspend is charged to the next period, and the player loses on time when no period remains. A period with a quota cannot be overspent: exceeding its budget loses on time. A host whose time rules differ maps them onto these fields as closely as it can, and deadline remains exact: an engine that respects deadline never loses on time, whatever the planning fields say.

Informative. Mapping of common time controls, for the Active Player’s side:

Time control Expression
Fixed budget, or Fischer with the increment credited after the Move remaining, inc; deadline = remaining
Fischer with the increment credited before the Move, Bronstein or US delay remaining, inc (the delay); deadline = remaining + inc
Time per Move (Sanki byōyomi) time = 0, remaining = 0, inc, togo = 1, carry: false; deadline = inc
Japanese byōyomi with n periods left as above; deadline = inc, or up to n × inc if the host lets the engine spend periods, which the planning fields do not model
Canadian (n Moves per period) time, remaining, togo, carry: false; deadline = remaining
Classical, 40 Moves then a second period time, remaining, inc, togo, carry: true, next = [{"time": second, "inc": inc}]; deadline = remaining
Main budget then overtime per Move remaining, next = [{"time": 0, "inc": per_move, "moves": 1, "carry": false}]; deadline = remaining + per_move

Limits

limits MUST be non-empty.

Field Domain Cap
depth ≥ 1 Depth, in the sense of the engine’s algorithm
nodes ≥ 1 Amount of work since receipt, in engine-specific units (positions visited, simulations…)
movetime ≥ 0 Duration from receipt; overhead does not apply to it. It is a maximum, and exact in the absence of clock, subject to the exceptions of §Stopping
mate ≥ 1 Mate by the Active Player, proven by an exact mate score of k half-moves, with k ≤ mate

Informative. nodes is the most portable limit across engines. On its own, mate may never be reached: the host SHOULD combine it with another limit. A mate delivered by the Active Player always takes an odd number of half-moves.

Stopping

Guaranteed result

For an engine that honors the bounded stop, the host therefore always obtains a legal Move within a bounded time by sending cancel.

State and reproducibility

Informative. To think on the opponent’s time, the host starts an infinite search on the predicted position, obtained by playing the second Move of the first variation. It then sends the real search once the opponent has moved: if the prediction was right, it is a continuation. For the continuation to be recognized, the host SHOULD keep a stable representation: the same starting position, and the full history in moves.

8.5 cancel

→ {"id":7,"op":"cancel"}
← {"re":3,"ev":"done","best":"a4-a7"}
← {"re":7,"ev":"done"}

cancel has no effect other than the interruption common to every request (§7).


9. Events

9.1 Variations and scores

These definitions apply to info and done alike.

Variation. A variation is an object {"pv", "score"}. pv is a legal, non-empty sequence of canonical PMN Moves from the searched position; score is its evaluation.

List of variations. A list of variations is sorted from best to worst, and the first Moves of its variations are distinct. It contains at most search.lines variations (only one without the lines feature), and never more than the number of legal Moves (or of search.roots).

Score. A score contains exactly one of the following fields:

Field Content
cp Evaluation on the engine’s own scale, positive if the Active Player has the advantage; an engine SHOULD scale it so that 100 is roughly the value of the game’s least valuable Piece. Its magnitude is meaningful for a given engine, but not comparable across engines
mate Non-zero integer, in half-moves; positive if the Active Player mates, negative if the Active Player is mated

Two optional fields complement it:

Field Content
bound "lower" or "upper", when the score is only a bound
wdl Three integers between 0 and 1000 (win, draw, loss), summing to 1000; a common scale, with an engine-specific calibration

Every score is expressed from the Active Player’s point of view. A positive mate score implies wdl = [1000, 0, 0], and a negative mate score implies [0, 0, 1000].

Informative. wdl is the only score a host can compare across engines; a host that decides on scores — to resign, to offer a draw — SHOULD decide on wdl and mate, never on cp.

9.2 info

info is a provisional result. It is entirely optional: an engine MAY emit none, and MAY drop any of them. Each field of an info supersedes the same field of the previous info.

← {"re":3,"ev":"info","depth":12,"seldepth":19,
   "nodes":84211,"elapsed":40,
   "variations":[{"pv":["a4-a7","d8-e8","e1-e2"],
                  "score":{"mate":17,"wdl":[1000,0,0]}}]}
Field Domain Content
depth ≥ 1 Last completed iteration, in the sense of the engine’s algorithm
seldepth ≥ 1 Selective depth, in the sense of the engine’s algorithm
nodes ≥ 0 Work done since receipt
elapsed ≥ 0 Time elapsed since receipt
hashfull 0 to 1000 Table occupancy, in per mille
variations — Complete list of current variations (§9.1)

An info contains at least one field. The engine SHOULD limit the rate to about ten info per second, the safety net of §8.4 excepted.

9.3 done

For ping, configure, and cancel, done is empty. For hello, see §8.1. For search, done carries the final result:

← {"re":3,"ev":"done","best":"a4-a7","depth":21,"nodes":1804211,"elapsed":2310,
   "variations":[{"pv":["a4-a7","d8-e8","e1-e2"],
                  "score":{"mate":17,"wdl":[1000,0,0]}}]}
Field Content
best Legal Move in PMN; null if and only if the searched position is terminal
variations List of final variations (§9.1), present as soon as the engine has at least one evaluation; the first one begins with best
depth, seldepth, nodes, elapsed, hashfull As in info, optional: the state of the search at its end

9.4 error

← {"re":8,"ev":"error","code":"illegal","path":"/moves/0","message":"…"}
Code Meaning
invalid Request malformed, out of domain, or not negotiated; any envelope error (§6.5)
illegal Illegal or non-canonical position or Move — a drop without its Piece excepted, which is invalid (§8.4)
unsupported Request well-formed, but naming rules or a pairing the engine does not implement
internal Engine failure: with re, the request failed for an internal cause before its search started; without re, the failure is fatal

Informative. Action expected from the host for each attached code:

Code Host action
invalid Fix the request
illegal Check the data against the rules document; on data the host has validated itself, conclude that the rules diverge
unsupported Use another engine for this game
internal Retry once, then restart the engine

9.5 Unknown values

Unknown value Host fallback
ev Event ignored
code internal
bound Bound ignored
Option type Option left at its default value

10. Features

The engine announces its features in the done of hello. The host MUST NOT use a feature that has not been announced.

Feature Parameters Adds
lines max search.lines: integer between 1 and max (default 1)
strength min, max search.strength: {"elo":n}, with n between min and max
roots — search.roots: non-empty array of distinct Moves of the searched position, which restricts the search to them; best and the first Move of every variation belong to it. Its Moves are validated like moves (§8.4); an empty array, or a repeated Move, causes invalid. A host does not send roots on a terminal position, where no Move is legal
advice — done.advice: "resign" or "draw", when the engine judges its position lost, or judges a draw the best outcome; absent otherwise. It binds the host to nothing
x_<vendor>_<name> Free Defined by the vendor

Informative. roots serves analysis (the exclusion of a book Move, the verification of a puzzle) and the simplest engines: a host that passes every legal Move lets an engine without a rules library play.


11. Safety

Engine.

Host.

Untrusted users. The host MUST NOT let them set options, and SHOULD bound the searches it relays on their behalf.


12. Conformance

Conforming engine. It MUST:

Conforming host. It MUST:

Conformance vectors and probe hosts and engines are described in the implementation guide (§15).


13. Evolution

13.1 Numbering

The specification follows SemVer.

13.2 Additivity

13.3 Stability

13.4 Future events

An event type defined after version 1.0 is never terminal.

13.5 Standard options

Reserved option names are frozen at version 1.0 (threads, hash). Any later standard option goes through a feature, whose announcement guarantees that the engine honors its meaning.

13.6 Vendors

Vendor-specific extensions, whether features or event fields, are named x_<vendor>_<name>. The vendor SHOULD choose for <vendor> a name it controls, such as a domain or project name.


14. Example session (informative)

→ {"id":0,"op":"ping"}
← {"re":0,"ev":"done"}
→ {"id":1,"op":"hello","versions":[1]}
← {"re":1,"ev":"done","version":1,"versions":[1], …}
→ {"id":2,"op":"configure","options":{"threads":4,"hash":256}}
← {"re":2,"ev":"done"}
→ {"id":3,"op":"search",
   "rules":"sashite.sanki.kernel/1",
   "position":"4k^3/8/8/8/8/8/8/R3K^3 / W/w",
   "moves":["a1-a4","e8-d8"],"fresh":true,
   "clock":{"own":{"deadline":60000,"remaining":60000,"inc":1000},"overhead":400}}
← {"re":3,"ev":"info","variations":[{"pv":["e1-e2"],"score":{"cp":0}}]}
→ {"id":4,"op":"ping"}
← {"re":4,"ev":"done"}
← {"re":3,"ev":"info","depth":12,"nodes":84211,
   "variations":[{"pv":["a4-a7","d8-e8"],"score":{"mate":17}}]}
← {"re":3,"ev":"done","best":"a4-a7",
   "variations":[{"pv":["a4-a7","d8-e8","e1-e2"],"score":{"mate":17}}]}

The first info is the safety net: the first legal Move the engine found. The host plays a4-a7 and lets the engine think on d8-e8. It then sends the real search, which is a continuation:

→ {"id":5,"op":"search","rules":"…","position":"…",
   "moves":["a1-a4","e8-d8","a4-a7","d8-e8"]}
→ {"id":6,"op":"search","rules":"…","position":"…",
   "moves":["a1-a4","e8-d8","a4-a7","d8-e8"],
   "clock":{…}}
← {"re":5,"ev":"done","best":"e1-e2", …}
← {"re":6,"ev":"done","best":"e1-e2", …}

An illegal Move, then the end of the session:

→ {"id":7,"op":"search",
   "rules":"sashite.sanki.kernel/1",
   "position":"4k^3/8/8/8/8/8/8/R3K^3 / W/w",
   "moves":["a1-h8"]}
← {"re":7,"ev":"error","code":"illegal","path":"/moves/0"}

The host then closes the engine’s standard input, and the engine exits with code 0.


15. Companion documents (informative)

Three documents complement this specification. Each states the version of SEI it covers.


16. Rationale (informative)


17. License

This specification is made available under the terms of the Open Web Foundation Agreement 1.0 (OWFa 1.0).

The authoritative legal text is the OWF “Final Specification Agreement (OWFa 1.0)”.