Gameworks

About

Gameworks is a framework for creating turn-based games hosted by a server, that can be interacted with via an API. At Instructure, we use it to create games for the final round of our coding competition: Mebipenny.

Installation

Add gem 'gameworks' to your Gemfile, or install manually with gem install gameworks

Usage

You will create classes to extend this engine, and then be able to interact with the game via an API. Let's go over how all that works.

Implementation

At minimum, you'll need a 3 files, that will look something like the following:

config.ru:

#!ruby

require_relative 'your_game/server'
use Rack::CommonLogger

# Override Lint middleware to handle async callback (-1 http response)
module Rack
  class Lint
    def call(env = nil)
      @app.call(env)
    end
  end
end

run YourGame::Server.new

your_game/server.rb:

require 'gameworks'
require_relative 'game'

module YourGame
  class Server < Gameworks::Server
    def game_class
      YourGame::Game
    end
  end
end

your_game/game.rb:

require 'gameworks'

module YourGame
  class Game < Gameworks::Game
    # you're reached player capacity
    def full?
    end

    # an array of players currently taking a turn
    def active_players
    end

    # given a json payload, create a move object
    def build_move(payload, player)
    end

    # given a move object, is it legal
    def move_legal?(move, player)
    end

    # given a lecal move, apply it
    def process_move(move, player)
    end
  end
end

API Data Types

The following are the default request/response data. You game implementation may extend any/all of these.

New Player (request)

  {name: <string>}

Player (response)

  {name: <string>,
   score: <integer>,
   disqualified: <boolean>}

New Game (request)

Nothing by default, determined by your game implementation.

Game (response)

  {players: [<Player>*],
   state: <Game State>}

players is the set of Players joined to the game.

Game Delta (response)

[*], state:

players is the set of Players whose scores have changed since the request began.

Game State (response)

'initiating', 'in play', or 'completed'

See descriptions of game states below.

New Move (request)

Nothing by default, determined by your game implementation.

Using the API

Starting a Game

To spawn a new game on the server, POST to / with a New Game object as payload. You will receive a 201 Created response with the game's root path (henceforth ) in the Location HTTP response header.

Game States

Initiating

The game begins in this state. You join a game by POSTing to /players with a Player object as payload.

If you successfully join the game, you will receive a 200 OK response at the beginning of your first turn, with a Game object as response payload. This response will include an X-Turn-Token HTTP response header.

If the game is no longer in this state (i.e. the game is full and has begun) when you POST, you will receive a 410 Gone response indicating the game can no longer be joined.

Once the game is full the game moves to the in play state.

In Play

In this state, players POST to /moves with Line objects as payload. These requests must include an X-Turn-Token HTTP request header with the value of the last turn token they received from the server.

After receiving a 200 OK response to a /players or /moves POST request (indicating it is your turn) you are required to POST your next move within limit}. If you fail to POST in that time, you are disqualified and the game moves to the completed state.

If the move POSTed is illegal, you will receive an immediate 403 Forbidden response describing the violation. You are disqualified and the game moves to the completed state.

Otherwise, you will receive a 200 OK response at the beginning of your next turn, with a Game Delta object. This response will include an X-Turn-Token response header to be used in your next move.

After a 200 OK response, the game may have moved to the completed state. This state change will be indicated in the Game Delta object. Once the game is completed, further moves are ignored with a 410 Gone response.

Completed

Play is complete. Game state can still be pulled, but no new POSTs are accepted.

Idempotent View

At any point, the full game state can be requested with a GET request to . You will receive an immediate 200 OK response with a Game object.

Observers (v1)

(Intended for use by game viewer, but may be used by others.)

At any point, an observer may be registered by POSTing to /observers. This will begin a long-running unbounded HTTP response of type application/x-multi-json , which is a series of one-line json documents separated by newlines.

You will receive an immediate 200 OK response with the first document, a Game object. After this, the response will remain open and new Game Delta objects will be returned as events occur.

The HTTP response will end when the game ends, after the final Game Delta object is sent.

The POST request requires no payload. However, if the payload includes a "wrapper" key, then each JSON document will be fprint'ed into that text, replacing the first %s. For instance, a HTML comet technique:

{ "wrapper": "<script type='text/javascript'>myUpdater(%s);</script>\n" }

Observers (v2)

TODO: notes about push

Matchmaking

When you have a bot you'd like to try against others, but you don't want to set up a game yourself and you don't care who your opponent is, you can connect your bot to our dirt-simple (and stupid) matchmaking system.

Simply GET /match and wait for a response. You will be added to a matchmaking queue, and once there are at least two people in the queue, the front two will be popped and each given a 302 Redirect with a game's url in the Location HTTP response header.

If no one else is joining, feel free to request a match against any of our bots! (Our bots are not eligible to win, so don't worry.)