Question

How would you model a turn-based game server as a RESTful API? For example, a chess server, where you could play a game of chess against another client of the same API. You would need some way of requesting and negotiating a game with the other client, and some way of playing the individual moves of the game.

Is this a good candidate for a REST (RESTful) API? Or should this be modelled a different way?

Was it helpful?

Solution

What are the resources you're trying to model? I would seem to have four: You, your opponent, the particular game (session, instance) and the game board state. So it would start with something like

/game
/game/gameID/gamer/gamerID
/game/gameID/board

we've got a good intro/overview on InfoQ.

OTHER TIPS

I'm thinking something like:

/game/<gameID>/move/<moveID>

as far as the basic resources are concerned. I'm not sure how to handle the "has the other player moved yet?" idea, though. I've thought about simply having the GET request block until the move is played--i.e. my client would PUT the coordinates of my move to

/game/13/move/1

and then would GET

/game/13/move/2

The server would not respond immediately, but keep the connection open until the other player moved (i.e. PUT to that location). Is this what nakajima is referring to as "comet-esque"?

Charlie, I'm not quite sure what you meant by the "token" for whose turn it is--does this solve the same problem without the need for polling or a blocking connection?

For player IDs, does it make sense to model those as a resource in part of the URL? I was planning to simply use HTTP user authentication (where the user/pass is sent as part of every request). You could still GET most resources without authentication, but if you tried to, say,

PUT /game/13/move/2

it would give you a permission denied error if you didn't have the correct credentials for that game.

OKay, the basic idea of REST is that you're transferring the state; you want to have little or no "session state" on the server. So you wouldn't want to use session state and a keepalive, which is what Comet does. But think about the example of a play-by-mail game: You both have a copy of the board, and you exchange moves. The Post Office doesn't know about the game.

Now, I'll admit this is growing in my mind as I think about it -- in fact, I may write an article based on this question -- but here's the idea, as some stories:

  1. You want to play a game of chess on line, so you go to a known URI to get one. You get back a page showing who, if anyone, is waiting to start a game.
  2. You pick one of the people waiting to play, and click the appropriate link. You get a new display (ajax magic in here if you want) with the board set up. One of you is white, white moves first.
  3. Whoever has the right to move enters a move, and commits (like taking your hand off the piece in a game.) The board updates and the right to move goes to the other player.

You don't need anything much in terms of server state --- although you might want to extend this by keeping track of moves etc, say for ranking -- and the question of who has the right to move can be computed entirely from the board page: if you have the right, you have a form for entering a move; when you send the form return back, the response returns a page to you with no slot for entering a move.

By "the token" I just mean some arbitrary representation of that one bit of state "My move"/"your move".

Seems as if the resources you need are

  • A "find a game" home page
  • A user page if you're tracking stats and such
  • a unique URI for every active game.

Thanks, Charlie. I'm still not clear how you get notified of the opponent's move in your scheme. Of course the question of who has the right to move can be computed simply--either from the board resource, or by using a separate resource that explicitly states whose turn it is to move. But how does a client know that this resource has changed? Does it have to simply continually poll, remembering the previous state, until it notices something has changed? In the Post Office model, the Post Office "pushes" a message to the client (your mailbox), which is not possible in HTTP.

I suppose this is part of a more general question: if there is a REST resource that I want to monitor for changes or modifications, what is the best way to do so? And is there something the server can do to make this easier for the client?

I think I'll actually post this as a separate question since I think it's interesting by itself.

Edited to add: What is a RESTful way of monitoring a REST resource for changes?

I don't think REST is a good choice for such an application. The transformations and operations you need to do (make move, view move history, undo, get suggestion, turn notification) do not map neatly to REST's concept of resources. (The difficulties are perhaps more obvious if one considers how a RESTful API for more complicated turn-based games such as Scrabble or Monopoly might look.)

I think any sensible REST API would probably end up being a wrapper around something non-RESTful, such as a stateful protocol that sent portable game notation back and forth.

I think you could model it RESTfully. Implementing it will be more difficult, because you'd either need a comet)-esque solution or you'd have to be polling the server at a relatively short interval via AJAX.

In terms of how you'd expose a RESTful interface, I'd say that you need a playing board with coordinates, pieces that can occupy those coordinates, and actions that alter those coordinates.

When a player makes a move, a new action would be created. After validating to make sure it's allowed, you'd update the status of the game, then render whatever response is needed to update the UI.

So that's basically how I'd model it. The implementation side is what I'd consider the bigger difficulty here though.

I don't think it's all that complciated, Nakajima. You'd pass data, say in JSON, for the board position, for the moves, and with a token for who has the next move. It'd be exactly like playing by mail.

You start by going to the game and looking for a partner, so

/game/

gives you a list of people waiting. when you come in, if there is no one waiting you get a game ID; otherwise you pick someone waiting and get the game ID they have.

/game/gameID

shows you the board. You need something to set up the decision of who plays white, i'll leave that as an exercise. The GET operation gives you the board, so a POST sends a move; if you don't have the move you get an error. You find the result by the next GET.

Hell, in this model I'm not even using the gamer ID, although it might be good so no one can sneak into the game as a kibitzer.

So your thought is that instead of making the actions a first class object, each move would be treated as an update of the game itself? It's certainly a different way to do it, though I think I'd prefer to split the action object out into its own first class entity for a couple reasons. The biggest reason is that I believe it's more testable. Whether or not a move is valid could live in the action object, instead of needing to worry about the board being in a valid state at all times. Granted, I don't know what either approach would entail, but this feels better to me.

The other reason, which you can totally refute via YAGNI, is that a first class action object would provide a history of moves. Interesting perhaps, but unless a requirement, is a moot point.

One of the developers in planet.jabber is involved in Chesspark, an online chess community. They are using Jabber/XMPP extensively; if I'm not mistaken, these are his posts on the subject.

XMPP is an instant-messaging protocol, roughly based on small XML message exchange. There are libraries for most languages, including Javascript. I'm not sure it will fit your problem, though.

For a simple game like chess, it is really just about defining the mediatype.

Here is an example of what is probably an over simplified mediatype to model a chess game.

I'm going to skip the management of multiple games that may be running on the same server and just model an already running game.

The first step is usually to define an index to the application.

index

The entry point for the game. Fetch this to discover information about the game.

The payload might look something like this:

{
    "links": {
        "self": "http://my-chess-game.host/games/123",
        "player": "http://my-chess-game.host/players/1",
        "player": "http://my-chess-game.host/players/2",
        "me": "http://my-chess-game.host/players/1",
         ...
    }
    "board": [
        {
           "x": 0,
           "y": 1,
           "piece": null,
           "rel": "space",
           "href": "http://my-chess-game/.../boards/123/0/1/something-random-to-discourage-uri-construction"
        },
        {
           "x": 1,
           "y": 2,
           "rel": "space",
           "href": "...",
           "piece": {
               "player": "http://my-chess-game/.../players/1",
               "type": "http://my-chess-game/pieces/Bishop",
               "rel": "piece",
               "href": "http://my-chess-game/games/123/pieces/player1/Bishop/1",
               "links": [
                    { "rel": "move": "href": "http://my-chess-game/.../boards/123/..." },
                    ...
                ]
            }
        },

        ...
    ]
}

move

POST a JSON payload to links marked with a rel of move to move a piece. The following fields MUST be included:

  • location: the URI of the space to move to

Successful responses have a status code of 200 and will contain an entity that the same as the index payload with the updated state of the game.

400 if the user is not allowed to move his piece there, or if it isn't his turn.

player

GET a description of a player.

The following fields MUST be in the response:

  • username: User name of the player
  • href: URI identifying the player of this game.

piece

Pieces are embedded in the index payload, but MAY exist on their own. Each piece MUST have the following fields:

  • type: A URI identifying the type of the piece. E.G. Bishop, Rook, King. GETing this URI MAY provide information about how this piece works within the game of chess.
  • href: A URI identifying the actual piece on this board. GET requests to this URI MAY provide information about this particular piece.

Each piece MUST have a move link.


An alternative design decision I could have made here was to provide an individual link for every valid move. There may be good reasons to do that, but I wanted to demonstrate that it isn't required. There are probably a handful of other resources that you would want to include to handle things like helping the client determine whose turn it is and whatnot.

For more complicated games, like Civilization, RTSs, FPSs, or MMOGs and what not, this may not be so practical IMO.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top