Why an order is needed
Records from two players can be in flight at the same time. White resigns while Black's move is on its way. Black accepts a draw at the instant White moves. Without a rule, the two phones could end up with different results. Pawnshake applies records one at a time in a canonical order that every device, the server and any later checker compute the same way.
The three rules
- Causality first. A record comes after its own
prevand after the opponent record itseen. - Ties by class, then colour. When a white and a black record are both ready (neither had seen the other), the earlier class goes first: MOVE, then END, then everything else. Same class: White first. Two crossing RESIGNs make a draw.
- Verdicts where they belong. A server verdict comes right after the player records it had seen, and before anything it had not.
Two moves are never concurrent: a move is only valid on its signer's turn, and the turn only passes through the opponent's move, which the signer must have seen.
Example: the resignation race
Putting moves first in a concurrent pair means a player's move always counts in the position that player actually saw. The same holds for a move against a draw acceptance or a takeback acceptance: the client that signed the move used exactly the position the canonical order gives it.
Checked where it lands
At its place in the order, each record is checked against the game state there. Not valid there? It is void and changes nothing.
| Record | Valid when | Effect |
|---|---|---|
| MOVE | signer's turn, game running, code legal, clock not out | plays the move; cancels standing takeback offers and the opponent's draw offer |
| RESIGN | game running | signer loses |
| DRAW_ACCEPT | the opponent's offer stands and is in the signer's seen | draw |
| TAKEBACK_ACCEPT | offer stands and is in seen | back to the position before the offerer's last move |
| ABORT | fewer than 2 moves | aborted, never rated |
| FLAGGED | game running | signer loses on time (draw if the opponent cannot mate) |
| FLAG_CLAIM | opponent to move | peer clocks: valid if the opponent's clock was out at the claim's time; anchored: the server decides |
| DISPUTE | signer has not signed END | game ends disputed, unrated |
| CANCEL | game running | leave (profile decides) or opponent gone (canceled) |
| LINK | game running, peer clocks, no settle open | restarts the transit estimate; nothing on the board |
| END | after the end, with the computed result | sign-off; both ENDs close the game for good |
Optimistic, then corrected
Clients apply records as they arrive and re-run the order when a concurrent record shows up later. The board then corrects itself, identically on both sides. In practice that only happens in races like the one above.
How a game ends
The game ends at the first terminating record in the order: a mating or drawing move, RESIGN, DRAW_ACCEPT, ABORT, FLAGGED, a valid FLAG_CLAIM, a verdict or CANCEL; or when it turns disputed or canceled. Then both clients sign END with the result they computed.
Next: how the result reaches the server, on Reports and storage.