Skip to content

Arimaa Game Server JSON API Draft

lightvector edited this page Aug 9, 2015 · 3 revisions

Arimaa Game Server JSON API Draft

The below is the original draft of the API that was proposed for the back-end of the site.

See Arimaa Game Server JSON API for the current actual implemented API as well planned changes.


All requests and responses are in JSON.

All paths are relative to the subpath under which the server is running, for example:
http://playarimaa.org/api/

Common Data Types

These are various common types of data used in the communication to and from the server:

  • <Username> = <String> - Login name of a user
  • <GameID> = <String> - Alphanumeric hash used as an identifier for a game
  • <Auth> = <String> - Authorization token returned from logging in
  • <GameAuth> = <String> - Authorization token returned from joining a game
  • <Timestamp> = <Float> - Seconds since the epoch (1970-01-01 00:00 UTC). Includes fractional seconds. The precision is unspecified by this API.

Common response types from the server for various queries:

  • <Message> = {"message":<String>} - Ex: "Ok", "Move 3g accepted",...
  • <Error> = {"error":<String>} - Ex: "Missing field 'field'", "Illegal move", ...

Account-related Queries

/accounts/register

To register an account:
POST /accounts/register {"username":<Username>,"email":<String>,"password":<String>,"isBot":<Bool>}
If successful: {"username":<Username>,"auth":<Auth>}
If not successful: <Error>

/accounts/forgotpassword

To request a password reset via email:
POST /accounts/forgotpassword {"username":<Username>,"email":<String>}
If successful: <Message>
If not successful: <Error>

/accounts/login

To log in:
POST /accounts/login {"username":<Username>,"password":<String>}
If successful: {"username":<Username>,"auth":<Auth>}
If not successful: <Error>

/accounts/logout

To log out:
POST /accounts/logout {"auth":<Auth>}
If successful: <Message>
If not successful: <Error>

Gameroom Queries

Data Types

  • Short publicly viewable information returned when querying about a user:

      <ShortUserInfo> = {
        "username":<Username>,
        "isBot"<Bool>
      }
    
  • The manner in which a game ended (others may be added in the future):

      <EndingReason> = 
        "g" - If game ended by goal
        "e" - If game ended by elimination
        "m" - If game ended by immobilization
        "t" - If game ended by one player running out of time
        "r" - If game ended by resignation
        "f" - If game ended by forfeit
        "i" - If game ended by submission of an illegal move (applies to bots only)
        "s" - If game ended by total game time limit
    
  • Specifies alternative types of games that could be played:

      <GameType> =
        "standard" - Standard Arimaa game
        "handicap" - Players can set up with a partial set of pieces or have differing time controls
        "directsetup" - Game begins with a specific predetermined position
    
  • For games that have ended, information about the ending:

      <GameResult> = {
        "winner":<String>, #"g" or "s", or "n" if there was no winner
        "reason":<EndingReason>,
        "endTime":<Timestamp>
      }
    
  • Board positions:

      #FEN-style string, ex:"rrrrrrrr/chdemdhc/8/8/4E/1H4H/C1DM1D1C/RRRRRRRR"
      <BoardPosition> = <String> 
    
  • Information about the time control for a player in a game:

      <TimeControl> = {
        #All values are in seconds
        "initialTime":<Int>,  #The initial amount of time a player begins with on the clock
        "increment":<Int>,    #OPTIONAL - Time added to a player's clock at the beginning of each move.
        "delay":<Int>,        #OPTIONAL - Length of time into a move before a player's clock actually begins decrementing.
        "maxReserve":<Int>,   #OPTIONAL - if present, cap player's clock to this amount at each end of turn.
        "maxMoveTime":<Int>,  #OPTIONAL - if present, the maximum allowed time for a player to make one move.
    
        #Option for soft-capping the maximum total length of a game.
        #If present, after this many full turns pass, go into overtime and thereafter decrease "increment" and "delay" 
        #exponentially, multiplying by (1-1/30) each turn (on average, halving approximately every 20 full turns).
        "overtimeAfter":<Int> #OPTIONAL
      }
    

    In the event that a time control is displayed to the user as a single string, the following is a recommended format for display:
    <initialTime>[+<increment>][~<delay>][(<maxReserve> maxresv)]/[(<maxMoveTime> max/mv)][(max <overtimeAfter>t)]
    where times are displayed using suffixes in {s,m,h,d} for seconds, minutes, hours, days, respectively.

    For example, a game with:
    initialTime=930, delay=15, maxMoveTime=60, overtimeAfter=80
    would be:
    15m30s~15s(1m max/mv)(max 80t)
    A postal game with:
    initialTime=1209600,increment=129600,maxReserve=1814400
    would be:
    14d+1d12h(21d maxresv)

  • Information about an open game that players can join:

      <OpenGameData> = {
        "creator":<ShortUserInfo>, #OPTIONAL - present if the game was user-created (versus automatically as part of an event)
        "joined":[<ShortUserInfo>,...] #Users *other* than the creator requesting to join the game
      }
    
  • Information about an active game being played right now:

      <ActiveGameData> = {
        "moveStartTime":<Timestamp>, #Time that clock started running for the current move
        "timeSpent":<Float>, #Seconds since the start of the current move
        "gTimeThisMove":<Float>, #Seconds on gold's clock at the start of the current move
        "sTimeThisMove":<Float>, #Seconds on silver's clock at the start of the current move
        "gPresent":<Bool>, #Whether gold is present and connected to the game
        "sPresent":<Bool>  #Whether silver is present and connected to the game
      }
    
  • Surface-level information returned when querying about a game or games:

      <GameMetadata> = {
        "id":<GameID>,
        "numPly":<Int>, #Total number of half-moves made in the game (ex: {1g, 1s, 2g, 2s, 3g} = 5 ply)
        "startTime":<Timestamp>, #OPTIONAL - Time that the game (and the game clock) was started 
        "gUser":<ShortUserInfo>, #OPTIONAL - absent for open games where the gold player is not yet determined
        "sUser":<ShortUserInfo>, #OPTIONAL - absent for open games where the silver player is not yet determined
        "gTC":<TimeControl>, #Time control for Gold
        "sTC":<TimeControl>, #Time control for Silver
        "rated":<Bool>, #Whether the game is used for rating calculations
        "gameType":<GameType>,
        "tags":[<String>,<String>,...], #Tags indicating games that belong to various events, etc.
        "openGameData":<OpenGameData>, #OPTIONAL - present only for open games
        "activeGameData":<ActiveGameData>, #OPTIONAL - present only for games being played right now
        "result":<GameResult>, #OPTIONAL - present if the game is finished
        "now":<Timestamp>  #Server timestamp as of the reporting of this metadata
      }
    
  • Information about the time a move was played:

      <MoveTime> = {
        "start":<Timestamp>, #Time that the clock (or delay) started running for this move
        "time":<Timestamp> #Time that this move was received and played
      }  
    

    Most of the time, start will be the same as time of the previous move, but in the event of a game resumption beginning on that move, it will be the time that the game was resumed.

  • Detailed information returned when querying about a game:

      <GameState> = {
        "history":[<String>,<String>,...], #Move history in the order [1g,1s,2g,2s...] ex: ["Ra1","Ra7","Ra1n Ra2n Ra3n Ra4n"]
        "moveTimes":[<MoveTime>,<MoveTime>,...], #Time that each move was received and played
        "toMove":<String>,  #Next player to move, either "g" or "s"
        "position":<BoardPosition>,
        "meta":<GameMetadata>,
        #Sequence number for polling queries, incremented on each update, starts at 0.
        "sequence":<Int> #OPTIONAL - present for open or active games. 
      }
    

Queries for Retrieving Games

/games/<GameID>/metadata

To get the metadata about a single game:
GET /games/<GameID>/metadata
(will also accept old arimaa.com numeric game ids in place of <GameID>)

If successful: <GameMetadata>
If not successful: <Error>

/games/search

To get the metadata about a set of games:
GET /games/search?key=value&key=value&...

Legal keys and values:

player1=<UserName>  #Involves the given user
player2=<UserName>  #Involves the given user
gplayer=<UserName>  #Gold player is the given user
splayer=<UserName>  #Silver player is the given user
rated=<Bool>        #Require games to be rated or unrated
experimental=<Bool> #Require games to be experimental or not
open=<Bool>         #Require games to be open or not
active=<Bool>       #Require games to be active or not
limit=<Int>         #Limit the number of returned games to this many (default 100, max 1000)
mintime=<Int>       #Game ends on or after this timestamp
maxtime=<Int>       #Game ends on or before this timestamp
mindate=<String>    #ex: 2015-01-01, game ends on or after this date in the given timezone
maxdate=<String>    #ex: 2015-01-01, game ends on or before this date in the given timezone
zone=<String>       #Timezone for mindate and maxdate, default UTC

If successful: [<GameMetadata>,<GameMetadata>,...]
If not successful: <Error>

/games/state

To get the full state of a game:
GET /games/<GameID>/state
GET /games/<GameID>/state?minsequence=<INT>
GET /games/<GameID>/state?minsequence=<INT>&timeout=<INT>
(will also accept old arimaa.com numeric game ids in place of <GameID>)

If successful: <GameState>
If not successful: <Error>

If minsequence is specified then blocks so long as:

  • Fewer than "timeout" seconds have passed (default 15, max 120).
  • The game remains open and/or active.
  • The latest gamestate has a sequence number less than or equal to minsequence.

(returning immediately if any of these conditions are not true at the time of the query).

Queries for Creating, Joining, and Playing Games

/games/actions/create

To create games:

#Standard game
POST /games/actions/create { 
  "auth":<Auth>,
  "tc":<TimeControl>,
  "rated":<Bool>,
  "gameType":"standard"
}

#Handicap game
POST /games/actions/create { 
  "auth":<Auth>,
  "gTC":<TimeControl>,
  "sTC":<TimeControl>,
  "gameType":"handicap"
}

#Game starting at a specific position
POST /games/actions/create { 
  "auth":<Auth>,
  "gTC":<TimeControl>,
  "sTC":<TimeControl>,
  "position":<BoardPosition>,
  "toMove":<String>,  #Next player to move, either "g" or "s"
  "gameType":"directsetup"
}

If successful: {"state":<GameState>,"gameAuth"<GameAuth>}
If not successful: <Error>

Generally, clients should follow a game creation immediately with a GET /games/<GameID>/state?minsequence=<INT> query in order to watch for other users joining. The creator of the game need not join the game, and the open game will be cleaned up if the creator leaves.

Additionally, the creator of a game should begin providing heartbeats via the heartbeat query below. Failing to send heartbeats and timing out will result in the open game being cleaned up.

/games/<GameID>/actions/join

To join an open game:
POST /games/<GameID>/actions/join {"auth":<Auth>}
If successful: {"gameAuth":<GameAuth>}
If not successful: <Error>

Generally, clients should follow a game joining immediately with a GET /games/<GameID>/state and/or GET /games/<GameID>/state?minsequence=<INT> query in order to watch for the game starting, or to watch for a decline of the join.

For a user-created game, the game will immediately start upon the other side sending a accept query, and the joining user does not need to send a accept query (although sending it anyways is harmless).
For a non-user-created game (the creator field is absent from <OpenGameData>), both joining users must send a accept query to start the game.

After joining, a client should begin providing heartbeats via the heartbeat query below. Failing to send heartbeats and timing out will result in leaving the game.

/games/<GameID>/actions/leave

To leave a game:
POST /games/<GameID>/actions/leave {"gameAuth":<GameAuth>}
If successful: <Message>
If not successful: <Error>

/games/<GameID>/actions/accept

To accept and begin a game with another player that has joined:
POST /games/<GameID>/actions/accept {"gameAuth":<GameAuth>, "opponent":<Username>}
If successful: <Message>
If not successful: <Error>

The "opponent" field should be the opponent that the current user is accepting to play.

For a user-created game, only the creator of the game needs to accept to start the game.
For a non-user-created game, both joining users must send accept query to start the game.

/games/<GameID>/actions/decline

To decline a join of a game from another player:
POST /games/<GameID>/actions/decline {"gameAuth":<GameAuth>, "opponent":<Username>}
If successful: <Message>
If not successful: <Error>

The "opponent" field should be the opponent that the creator of the game is declining to play.

/games/<GameID>/actions/heartbeat

To send a heartbeat:
POST /games/<GameID>/actions/heartbeat {"gameAuth":<GameAuth>}
If successful: <Message>
If not successful: <Error>

NOTE: When the user is present in an active or open game, clients should send a heartbeat more frequently than every 15 seconds to indicate the presence of the user. This is used for the "gPresent" and "sPresent" fields in an active game, as well as for cleaning up open games or join requests upon timeout.

/games/<GameID>/actions/resign

To resign a game:
POST /games/<GameID>/actions/resign {"gameAuth":<GameAuth>}
If successful: <Message>
If not successful: <Error>

/games/<GameID>/actions/move

To make a move:

POST /games/<GameID>/actions/move {
  "gameAuth":<GameAuth>,
  "move":<String>, #ex: "Ra1n Ra2n Ra3e Rb3e Rc3x"
  "plyNum:<Int>   #1g = 0, 1s = 1, 2g = 2, 2s = 3, ...
}

If successful: <Message>
If not successful: <Error>

To Do

  • How do we prevent spamming of registration of new accounts?
  • Should we add a country field to user information for registration? What format should countries or country codes be in?
  • Add queries for finding and viewing stats about users
  • General query throttling in appropriate spots