How the rules were pieced together, and how positions and games get written down once you have them.
Every field in the ruleset carries a tag saying where it came from. Where two sources disagree the higher one wins, and the disagreement gets recorded rather than smoothed over.
Primary text gets read directly, never through a summary. That rule exists because an automated summary of one rules page asserted that capture "applies orthogonally and diagonally". That wording does not appear anywhere on the page it claimed to be quoting. It was caught by opening the page.
Everything above is either stated or close to obvious, so it gets a line each. These are the ones where the sources are silent and we picked a value so the engine would run. Each shows what we chose and what it means. They are the ones to argue with.
Generated from the ruleset when the site is built, so it cannot drift from what the engine actually loads, and the build fails if a guess is added without a description.
draw.repetitionNothing ends a game for repeating a position. The search avoids repetition internally, but two players could shuffle forever.not_modelleddraw.halfmove_limit600 moves without a capture ends the game. Invented so that a search terminates, and set above the longest capture-free run we have measured (405) so it does not decide games.600stalemateA player with no legal move loses. Chess calls that a draw; nothing says this game does.losswin_resolution.simultaneousIf one move would win for both sides at once, Ivory's conditions are checked first.first_listedChess has FEN, a single line that names every piece and whose turn it is. SE!ZE needs that plus the power tokens, which chess has no equivalent of. A piece can be carrying an orthogonal power, a diagonal one, or both, and any unclaimed tokens are still sitting on the board.
So SFEN carries the eight ranks, the side to move, the halfmove clock, and four token lists. It is meant to be read by a person, for a bug report or a test fixture you want to eyeball. There is a binary form too, eighteen bytes and twenty-four base64 characters, for corpora with millions of rows and for URLs.
The binary form quietly had a bug worth mentioning: the halfmove clock was stored in seven bits, because the draw limit was 100 and 127 seemed like plenty of room. When the limit moved to 600, positions started coming back with the wrong clock.
A position is not a game. Chess uses PGN for that, and this works the same way: a few headers, then the moves.
The Fingerprint header is the one that earns its place. A move list
means nothing without the rules it was played under, because the same moves
can be legal under one version and illegal under the next. A record whose fingerprint does not match gets refused rather than replayed.
Replaying it would produce an answer that looks fine and is wrong.
This has already cost us. Three things in this project forgot to record their ruleset and had to be thrown away: a corpus of self-play games, an opening book, and two sets of fitted weights.
c2-c3 names both squares and is never ambiguous, which makes it
right for storage and wrong for reading. Chess writes the destination and
adds only enough of the origin to tell two pieces apart. Nf3,
not Ng1-f3.
SE!ZE has no piece letters, so a move is written as its destination plus whatever is needed to say which piece went there. Measured over 1,500 moves from played games: 31% need nothing but the square, 62% need one extra character, and 7% need writing out in full.
Those numbers used to read 55/44/1. They moved when we learned that guards step one square in any direction rather than orthogonally, which roughly doubles the pieces that can reach a given square. Short notation survived the correction, it just needs a disambiguator more often than not, which is the same position chess is in. All four forms are accepted, and an ambiguous one is refused rather than guessed, since guessing would silently analyse a different game than the one played.