The idea
Both players know the position. So instead of sending "e2 to e4", a player sends which of the legal moves it played. List every legal move, sort the list the same way on every device, and the move is its index. No position in chess has more than 218 legal moves, so the code always fits a byte (0 to 217), and the bytes above 217 are free for commands.
Read the numbers on the board: the knights come first (b1 is square 1, g1 is square 6), then the pawns from a2 to h2, each with its one-step move before its two-step move.
White's 1. e4 is 13, so its record's op byte is 0d. That one byte and the signature are the whole move.
The sort key
key = from * 320 + to * 5 + promo sorted ascending
square = rank * 8 + file a1 = 0, h1 = 7, a8 = 56, h8 = 63
promo = none 0, n 1, b 2, r 3, q 4
Castling is the king's two-square move (e1g1, e1c1), en passant is the pawn's diagonal move. Promotions are separate moves with separate codes: e7e8n, e7e8b, e7e8r, e7e8q.
Worked out
| Code | Move | Key |
|---|---|---|
0 | b1a3 | 1 × 320 + 16 × 5 = 400 |
1 | b1c3 | 1 × 320 + 18 × 5 = 410 |
2 | g1f3 | 6 × 320 + 21 × 5 = 2025 |
3 | g1h3 | 6 × 320 + 23 × 5 = 2035 |
4 | a2a3 | 8 × 320 + 16 × 5 = 2640 |
5 | a2a4 | 8 × 320 + 24 × 5 = 2680 |
6 | b2b3 | 9 × 320 + 17 × 5 = 2965 |
7 | b2b4 | 9 × 320 + 25 × 5 = 3005 |
8 | c2c3 | 10 × 320 + 18 × 5 = 3290 |
9 | c2c4 | 10 × 320 + 26 × 5 = 3330 |
10 | d2d3 | 11 × 320 + 19 × 5 = 3615 |
11 | d2d4 | 11 × 320 + 27 × 5 = 3655 |
12 | e2e3 | 12 × 320 + 20 × 5 = 3940 |
13 | e2e4 | 12 × 320 + 28 × 5 = 3980 |
14 | f2f3 | 13 × 320 + 21 × 5 = 4265 |
15 | f2f4 | 13 × 320 + 29 × 5 = 4305 |
16 | g2g3 | 14 × 320 + 22 × 5 = 4590 |
17 | g2g4 | 14 × 320 + 30 × 5 = 4630 |
18 | h2h3 | 15 × 320 + 23 × 5 = 4915 |
19 | h2h4 | 15 × 320 + 31 × 5 = 4955 |
Guarding against engine bugs
If two engines ever disagreed about the legal moves in a position, the same byte would mean different moves on the two boards and the game would silently split. Pawnshake prevents that: every move record's signed body includes legal, the number of legal moves in the position, and ply. A disagreement shows up as a signature failure the moment the record arrives, not ten moves later.
Conformance
vectors/movecode.json lists 13 positions (the start, castling on both sides, en passant, promotion order, a capture promotion, a check with one reply, the famous 218-move position and six positions from random games) with the full sorted legal list for each. Every port must reproduce them exactly.