Skip to content

Game Move History API

This document outlines the reverse-engineered specification for the Game Move History API in Dice Chess. Unlike standard PGNs, this API does not just return a flat list of chess moves. Instead, it returns a massive State Machine History, where every small action (a roll of the dice, proposing a double, or moving a piece) is recorded as a complete snapshot (state) of the game.

GET /api/game-move-history?gameId=<UUID>
Authorization: Bearer <token>

The trainer backend exposes an admin-only endpoint to batch-import game metadata before move history is fetched.

For the full admin-side synchronization story — including the metadata list, the missing-history batch job, and the timeout/concurrency behavior — see Move Synchronization Lifecycle.

POST /api/admin/sync/games?limit=50&skip=0&player_id=<PLAYER_ID>&start_date_ms=<EPOCH_MS>
ParamTypeRequiredDescription
limitintNoBatch size (1..100).
skipintNoPagination offset for Dice Chess history API.
player_idstringNoOverrides configured default sync player id.
start_date_msintNoInclusive lower bound (Unix epoch in milliseconds) for history filtering.

When start_date_ms is provided, the history request includes filters.START_DATE and uses ascending date sort (isAsc=true) to backfill from older games forward. Without it, sort stays descending (isAsc=false).


The API returns a JSON object where each key represents a sequential Step number (starting from "0"), and the value is the complete game state at that exact moment.

Because a single “Turn” in Dice Chess consists of a dice roll plus up to three micro-moves, you will see many Steps for just one actual Turn changing hands.

The "0" step is crucial for bootstrapping the game view:

  • Time: The leftTime object shows the starting time for both players in milliseconds.
  • Bank: The bank property represents the total combined bank. To find out how much a single player has wagered, you must divide this number by 2 (e.g., if bank is 600, each player bet 300).

Deducing Hidden Actions (Resign, Draw, Double)

Section titled “Deducing Hidden Actions (Resign, Draw, Double)”

A major quirk of this external API is that it does not provide explicit flags for actions like “Player Resigned”, “Player Offered Draw”, or “Player Offered Double”. These actions must be deduced purely by analyzing the transitions between states.

If the game ends, the times stop ticking, and the position is not checkmate, it means a player resigned or disconnected.

A draw offer is inferred heuristically from no-op transition states and clock behavior while one side decides. This can happen before a roll or during the opponent’s unfinished micro-turn. For a documented real-world interrupt sequence, see Draw Offer During Opponent Micro-Turn.

In X2 mode, offering and accepting a double is the most complex sequence to parse. Below is an exhaustive breakdown of how to identify a Double action from the raw JSON state, using an actual game log from Steps 12 to 17.

// Black finishes their move.
"12": {
"fen": "r1bqkbnr/pppppppp/2n5/1N6/7P/7R/PPPPPPP1/R1BQKBN1 b Qkq - 4 1",
...
"gameMoveHistoryMove": { "from": "B8", "to": "C6", "promotion": "NONE" }
}
  • Status: Black made a move. gameMoveHistoryMove is populated, so the B8 to C6 squares are highlighted green on the board.
// Highlights are cleared. Turn switches to White.
"13": {
"fen": "r1bqkbnr/pppppppp/2n5/1N6/7P/7R/PPPPPPP1/R1BQKBN1 w Qkq - 4 1",
...
"gameMoveHistoryMove": null
}
  • Status: gameMoveHistoryMove becomes null, clearing board highlights. The FEN active color is now w (White’s turn).
"14": {
"fen": "r1bqkbnr/pppppppp/2n5/1N6/7P/7R/PPPPPPP1/R1BQKBN1 w Qkq - 4 1",
"bank": 600,
"leftTime": { "303082" (White): 296734, "782580" (Black): 304599 },
"gameMoveHistoryMove": null
}
  • Status: White’s time has decreased (from 298286 in Step 13 to 296734 here), meaning White was thinking. However, White has not rolled the dice yet (the dices array still contains Black’s old roll). During this step, White clicked the X2 Double button.
  • Effect: Black’s time will now start ticking as Black decides whether to accept or fold.
"15": {
"bank": 1200,
"leftTime": { "303082" (White): 296734, "782580" (Black): 304899 }
}
  • Status: The bank instantly jumps from 600 to 1200. This is the definitive proof that Black accepted the Double.
"16": {
"bank": 1200,
"dices": [
{ "value": "B", "allowed": false, "used": false },
...
]
}
  • Status: White rolls the dice resulting in fresh dice values.
"17": {
"gameMoveHistoryMove": { "from": "E2", "to": "E4", "promotion": "NONE" }
}
  • Status: White begins using the dice to make physical moves on the board.

Every numbered step in the JSON object respects the following schema.

FieldTypeDescription
fenStringThe standard FEN string representing the exact board configuration (including the active color).
bankNumberThe total combined bank for the match at this specific moment.
dicesObject[]Array of exactly 3 dice objects representing the most recent roll.
dices[].valueStringThe piece rolled (e.g., "P", "K", "R", "n", "q"). Case denotes color.
dices[].allowedBooleantrue if this die can be legally played on the board.
dices[].usedBooleantrue if the player has already executed the move associated with this die.
leftTimeRecord<string, number>A dictionary mapping the player_id (String) to their remaining time in milliseconds.
gameMoveHistoryMoveObject | nullContains {from, to, promotion}. If not null, it represents the Delta physical piece move that just happened, which is used to render green square highlights.