About the Jocly Game API

The Jocly Game API is a application program interface to create HTML5 (Javascript + CSS) two players turn-based brain games with minimum effort.

The general idea is that the developer writes a limited number of javascript functions, specific to the game implemented, and the platform will take care of calling those functions in order to provide automatically:

  1. an Artificial Intelligence to play against the computer
  2. the ability to play against people in remote
  3. the game application running on the Web, Android, iPhone/iPad, and other platforms
  4. game applicative support: moves history, game replay, save/load games, timing management, …

About the Jocly Game API

Prerequisite

Basics

Model descriptor

View descriptor

Game file tree

Model implementation

View implementation

Implementation methods

Model methods

Model.Game.InitGame()

Model.Game.DestroyGame()

Model.Board.Init()

Model.Board.InitialPosition(aGame)

Model.Board.CopyFrom(aBoard)

Model.Board.GenerateMove(aGame)

Model.Board.ApplyMove(aGame,aMove)

Model.Board.Evaluate(aGame,aFinishOnly,aTopLevel)

Model.Board.QuickEvaluation(aGame)

Model.Board.IsValidMove(aGame,aMove)

Model.Move.Init(aMove)

Model.Move.CopyFrom(aMove)

Model.Move.Equals(aMove)

Model.Move.ToString()

View methods

View.Game.InitView()

View.Game.DestroyView()

View.Board.Display(aGame)

View.Board.HumanTurn(aGame)

View.Board.HumanTurnEnd(aGame)

View.Board.PlayedMove(aGame,aMove)

View.Board.ShowEnd(aGame)

Using sounds

Debugging a game

Detecting game loops

Prerequisite

Using the Jocly Game API is simple but it’s probably not suitable for a beginner to learn Javascript from.

Assuming a general knowledge of Javascript (at least you must know how to use closures) and jQuery (if you want to create or modify the game user interface), you should not have too much trouble using the API.

Basics

Jocly is roughly implemented under a MVC (Model-View-Controler) scheme. The Jocly platform implements the controler so you don’t have to care much about this. The model is the code that implements the rules independantly of how the game will be displayed. The view part takes care of the game user interface, really the game itself, not the user interface that runs the game (like buttons, menus, …) that are part of the application controler.

Each of model and view comes with at least a JSON descriptor describing the entity and one or several Javascript files for the implementation.

In addition to the model/view split, a Jocly game implementation deals with 3 objects: Move, Board and Game. The Game object represents the game being played from the first move. The Board object holds the game state at a given time. The Move object represents a player move, like in chess moving the king pawn e2-e4.

As a game developer, you must write/adapt a number of methods to those objects for the game to play.

Since Jocly implements two players games, it is assumed that the player making the first move is player A, the other one player B. All Jocly methods assume that player A has value 1 (JocGame.PLAYER_A), player is represented by value -1 (JocGame.PLAYER_B).

Model descriptor

This is a Javascript object defined in JSON describing a number of options for the game.

At the very minimum, the model must define:

{

    "name" : "mygame",

    "title" : "My Game",

    "summary" : "A 40 chars max description of the game",

    "js" : "mygame-model.js"

    "rules" : "rules.html",

    "description": "description.html",

    "credits": "credits.html",

    "thumbnail" : "thumbnail.png",

    "maxLevel" : 9,

    "defaultLevel" : 2,

}

Where:

  1. name: a machine readable name for the game. Don’t use spaces or punctuation characters, only lowercase letters, digits and dashes
  2. title: the game name to be displayed for humans
  3. summary: a short description of the game. It must not exceed 40 characters
  4. js: the javascript implementation for the model. If you need to load several files, use a Javascript array like [“mygame-model.js”,”mygame-model-extension.js”]. If there are several files, they will be loaded in the order of the array.
  5. rules: the name of a HTML file containing the rules of the game. Within the HTML file, you may need to include images. In this case, you must use the {GAME} placeholder in the src attribute. For instance: <img src=”{GAME}/res/myimage.png”/>. Jocly will replace this with the appropriate value when needed.
    In case you want to internationalize the game rules, it is possible to define several HTML files like this: “rules”: { “en”:”rules.html”, “fr”:”rules-fr.html” }. In this case, Jocly will use the appropriate file depending on the context language. Having a “en” version in english is mandatory.
  6. description: a description of the game context. The same placeholder and internationalization remarks as for rules apply.
  7. credits: game credits regarding the game inventors, code developers and graphics designers. The same placeholder and internationalization remarks as for rules apply.
  8. maxLevel: used for the IA, it describes the levels offered to the player and refers to the depth of the search. In a fully fledged implementation, you should rather use a finer description of the levels using the parameter “levels” (see below).
  9. defaultLevel: the IA level by default. If you use finer described levels, the default level should be specified within the level description (see below).
  10. thumbnail: the name of a 85x85 image representing a board snapshot.

There are a number of other parameters that can be used within the model descriptor:

  1. levels: an array of descriptors describing the levels available to the player when playing against an AI. For instance:
    "levels": [

            {

                    "label": "Baby",

                    "potential": 1000,

                    "maxDepth": 0

            },

            {

                    "label": "Beginner",

                    "potential": 1000,

                    "maxDepth": 5,

                    "isDefault": true

            }]
        where:

  1. label: the label to be displayed for this level.
  2. maxDepth: the maximum depth for the computer to search moves.
  3. potential: an approximative number of positions for the computer to search for. Tuning this parameter generally takes a set and try approach.
  4. isDefault: set to true if the level is the default one.

  1. gameOptions: an implementation specific object that will be passed to the javascript. It may contain anything related to the game if it makes sense not to have this hardcoded in javascript, like the board size, the initial position, some rules options, … This object will be available from the javascript through the aGame.mOptions member.
  2. startMessage: a text message displayed before the first move.

It is possible for a descriptor to contain several models: instead of having { model descriptor }, use [ { model1 descriptor}, { model2 descriptor} ]. Make sure the model descriptors names are different.

View descriptor

This is a Javascript object defined in JSON describing a number of view options for the game.

At the very minimum, the view must define:

{

    "title": "My Game View",

    "model": "mygame",

    "js": "mygame-view.js",

    "css": "mygame.css"

}

Where:

  1. title: a human readable name for the view (unused but required)
  2. model: the model name(s) the view works with. If there are several compatible models, use a javascript array like “model”=[“mygame”,”myothergame”]
  3. js: the javascript implementation for the view. If you need to load several files, use a Javascript array like [“mygame-view.js”,”mygame-view-extension.js”]. If there are several files, they will be loaded in the order of the array.
  4. css: the CSS stylesheet for the view. If you need to load several files, use a Javascript array like [“mygame.css”,”mygame-extension.css”]. If there are several files, they will be loaded in the order of the array.

A number of other parameters can also be used in the view descriptor:

  1. preferredRatio: the width/height ratio to be used by Jocly to setup the application layout. A game played on a squared board like chess should be 1, a vertical board like scrum should be less than 1, an horizontally shaped board should be greater than 1.
  2. switchable: a boolean that defines whether the board can be switched, like in Chess where you may want to switch between the whites and blacks perspective (default is false).
  3. animateSelf: when a human inputs a move, the user interface may not need to show an animation for the move being played. In that case, set value to false (default is true).
  4. useNotation: whether you want support for optional notation display (default is false). If set to true, the implementation must check aGame.mNotation value and do the display accordingly.
  5. useShowMoves: whether you want support for optionally showing possible moves (default is false). If set to true, the implementation must check for aGame.mShowMoves and do the display accordingly.
  6. skins: a game may implement several skins that the user can switch between. For instance: “skins”: [{“name”:”wood”,”title”:”Wood”},{“name”:”iron”,”title”:”Iron”}] where name is a machine readable name to be retrieved by the implemention from aGame.mSkin and title the name to be displayed to the user.
  7. defaultOptions: if useNotation, useShowMoves or skins are used, it might be a good idea to setup what the default values are. For instance:
    "defaultOptions": {

                    "sounds": true,

            "notation": false,

"moves": true,

"skin": "skin-name"

 }

  1. sounds: Jocly games come with default sounds you can hear when the game ends. It is possible to redefine those sounds and create new ones, for instance to be played when a piece is caught or moves. Using sounds is detailed later in this document.

Game file tree

The game implementation must contain a number of files at the root directory:

  1. model: the JSON representation of the model descriptor
  2. javascript model implementation: typically mygame-model.js, may include several  javascript files for libraries or model extension
  3. view:  the JSON representation of the view descriptor
  4. javascript view implementation: typically mygame-view.js, may include several  javascript files for libraries or view extension
  5. javascript view CSS: typically mygame.css, not mandatory but is generally very useful
  6. thumbnail image: typically thumbnail.png, a 85x85 screenshot of the board. There may be several thumbnails if the model implements more than one game
  7. documentation files: typically rules.html, description.html, credits.html. Those files may be localized, locale versions should be named like rules-fr.html, rules-de.html, …
  8. the res directory: files and sub-directories below the resource directory are copied as is to be part of the game package. It is important to keep the total size to a reasonable value like 2MB.

Model implementation

Functions must be defined like Model.<Object>.<Method>.

The methods to be implemented in the model are:

  1. Object Game:
  1. InitGame()
  2. DestroyGame() (optional)
  1. Object Board:
  1. Init()
  2. InitialPosition(aGame) (optional)
  3. CopyFrom(aBoard) (optional)
  4. GenerateMoves(aGame)
  5. ApplyMove(aGame,aMove)
  6. Evaluate(aGame, aFinishOnly, aTopLevel)
  7. QuickEvaluate(aGame)
  8. IsValidMove(aGame,aMove)
  1. Object Move:
  1. Init(aMove)
  2. CopyFrom(aMove) (optional)
  3. Equals(aMove)
  4. ToString()

View implementation

Functions must be defined like View.<Object>.<Method>.

The methods to be implemented in the model are:

  1. Object Game
  1. InitView()
  2. DestroyView()
  1. Object Board
  1. Display()
  2. HumanTurn(aGame)
  3. HumanTurnEnd(aGame,aMove)
  4. PlayedMove(aGame,aMove) 
  5. ShowEnd(aGame)

Implementation methods

Model methods

Model methods must not make any assumption regarding the view being used. In particular, model methods must never call a method defined in the view (though calls from view to model are acceptable).

Model methods must never make use of global variables. In general, variables with large scope like constants are defined at Game object level.

Move methods are generally easy to implement, but it is important to understand that, since the game history uses the list of moves to reach the current state, changing the move structure will result in saved games not working anymore. So it is important to make sure the Move object definition won’t be modified after the game is released.

Model.Game.InitGame()

This method is called when a game is first created. The keyword ‘this’ refers to the Game object.

InitGame is the place where to initialize pre-calculated values like a graph for walking through the board, constants, …

In order to prevent name collision with existing Game object members, new members should attach to this.g instead of directly to this. For instance, developers should prefer this.g.MY_CONSTANT to this.MY_CONSTANT.

A model is user interface agnostic, you must never reference any DOM element. That should be done within View.Game.InitView instead.

Model.Game.DestroyGame()

This method matches Model.Game.InitGame to clear what has been setup, however in practice there is generally no need to do any such thing so it’s safe not to implement the method.

Model.Board.Init()

This method refers to the Board object, accessed through the this keyword. It is called whenever a new Board object is to be created. The Board object represents a game state at a given time. It holds the game pieces positions and every information needed to resume a game.

Many Board objects are created in background, particularly when the AI is running, so performance is critical. It is not required that the Init method setups the initial position of the pieces, but in this case, method Model.Board.InitialPosition must be implemented.

A Board object must never hold a reference a DOM element or anything that is not JSON serailizable, neither in the model nor in the view.

Model.Board.InitialPosition(aGame)

When the platform needs to create a starting board, it first calls Init then InitialPosition. If InitialPosition is not implemented, the Init method must setup the initial position, which, depending on the game, may be a performance issue.

The aGame parameter refers to the Game object being run.

Model.Board.CopyFrom(aBoard)

This method is used to copy the Board object in parameter to the current one. If not implemented, a copy based on JSON stringify/parse is used.

For performance reasons, a developer may want to override the default behavior.

Model.Board.GenerateMove(aGame)

This method is called from several places within the Jocly framework, in particular there might be thousands of calls when the AI is thinking. Developers should pay attention to performances to make sure no useless code is being run.

GenerateMoves applies to the board object and creates Move objects representing possible moves available from the current Board. Move objects must be saved to this.mMoves array.

There is no need to init this.mMoves since GenerateMoves is always called with an this.mMoves array.

Moves can be stored to the this.mMoves array using this.PushMove(aGame,<move data>) (for instance, this.PushMove(aGame,{row:1,col:2}) or directly using this.mMoves.push(<move data>).

GenerateMoves must never return with an empty mMoves array, unless the end of game is detected. In that case member this.mFinished must be set to true and this.mWinner set to JocGame.PLAYER_A (1), JocGame.PLAYER_B (-1) or JocGame.DRAW (2).

If the game rules allows a player not to play while the game is not finished, a Move object representing the pass action must be added to mMoves.

Most of the times, GenerateMoves needs to know which player is the current one. This can be read from this.mWho, with value JocGame.PLAYER_A or JocGame.PLAYER_B.

There are 2 strategies when implementing the GenerateMoves method: either making an array of all possible moves, or picking only the moves that are the most likely to be the best moves. In case there is a large number of possible moves (like Scrum or Yohoho), the second option should be preferred.

Although the AI will work whatever the move order in mMoves is set to, performance will improve a lot if the best moves appear first in the array. For instance, it is generally best to enter first moves resulting to catching a piece from the opponent.

Model.Board.ApplyMove(aGame,aMove)

The ApplyMove method is in charge of apply the move as parameter to the current Board object, so it will reflect the new game state. For instance, it could be:

Model.Board.ApplyMove=function(aGame,aMove) {

        this.board[aMove.row][aMove.col]=this.mWho;

}

Model.Board.Evaluate(aGame,aFinishOnly,aTopLevel)

The method is in charge of providing a static evaluation of the current Board object. The result of the evaluation must be assigned to the mEvaluation member of the Board object, in such a way that a high value of this.mEvaluation reflects a position in favor of PLAYER_A, and a low value in favor of PLAYER_B. It is generally convenient to consider that value 0 represents a situation where no player has the advange, and use a positive or negative numbers to indicate PLAYER_A or PLAYER_B advange respectively.

The Evaluate method can also indicate the end of the game. If this situation is detected, the member mFinished must be set to true and mWinner must take value within JocGame.PLAYER_A (1), JocGame.PLAYER_B (-1), JocGame.DRAW (2). If the end of the game did not occur, there is no need to set mFinished to false. Alternatively, it can be more convenient to set mFinished and mWinner from within the GenerateMoves method.

Parameters aFinishOnly and aTopLevel are to be used for optimisation and can safely be ignored. aFinishOnly with value true means that only mFinished and mWinner are to be eventually assigned, mEvaluation will be ignored anyway. aTopLevel with value true means that the Evaluate function is called for the current board. In this case, the implementation may want to store specific information to indicate the reason of the game end (like the winning alignments in Connect4).

Model.Board.QuickEvaluation(aGame)

This method is optional. If implemented, it must return a value indicating the player advantage in the same way Evaluate assign mEvaluation. This method is used for optimisation and can in some cases results in much better overall AI performances. However, because of the additional calls it generates, the resulting performance might be worse. A good strategy consists of not implementing the method in a first version, and check afterwards if the method improves performances.

mFinished and mWinner must be left unchanged in the QuickEvaluation method.

Model.Board.IsValidMove(aGame,aMove)

The default implementation of the IsValidMove method consists of verifying that aMove has an equivalent value in the mMoves array created from GenerateMoves. However, if GenerateMoves does not create an exhaustive list of possible moves, the method must verify that the move in parameter is acceptable.

In general, if the view for a game does not allow the input of an invalid move, the method can simply return true.

Model.Move.Init(aMove)

This method initializes a Move object. As aMove may not be provided at method call, the implementation must check “typeof aMove”.

A typical implementation can be:

Model.Move.Init(aMove) {

        if(typeof aMove != “undefined”)

                this.CopyFrom(aMove);

}

Model.Move.CopyFrom(aMove)

The method copies the members of the parameter to the current Move object. If not implemented, a JSON stringify/parse based copy is performed.

Model.Move.Equals(aMove)

The method returns whether the Move object passed as parameter holds the value than the current object. If not implemented, a comparison of the JSON string values is used.

Model.Move.ToString()

The method must return a string to be displayed to humans in the game history. If not implemented, the JSON representation is used.

View methods

The view implements methods for the Game and Board objects to interface with the platform the game is being run on.

Jocly views use jQuery 1.5.2 to manipulate DOM elements.

Since a view cannot make any assumption about the environment, the methods must not try to access any DOM element outside of the game viewport (reached via aGame.mWidget). Accessing a particular element within the game viewport must be done with aGame.mWidget.find(relative jQuery selector). For instance, aGame.mWidget.find(“.mygame[jocly-pos=12]”).hide(). Check jQuery documentation to see how to manipulate the DOM.

The view implementation must not change the size of aGame.mWidget nor display anything outside of this element. It is the view responsability to ensure the available space (aGame.mWidget.width() x aGame.mWidget.height()) is optimally occupied.

View methods must never make use of global variables. In general, variables with large scope like constants are defined at Game object level.

View.Game.InitView()

The InitView method is called whenever the Game object is first displayed, resized or switched. The method should create the DOM elements needed to display the game, according to the geometry that can be read from this.mGeometry.

To prevent name space collisions, constants should be defined under the this.g object.

A typical InitView implementation could be:

View.Game.InitView=function() {

    var smallDimension=Math.min(this.mGeometry.width, this.mGeometry.height);

    this.g.cellSide=smallDimension/4;

    this.g.cellMargin=this.g.cellSide/16;

    this.g.tokenSide=this.g.cellSide-2*this.g.cellMargin;

    this.g.top=(this.mGeometry.height-3*this.g.cellSide)/2;

    this.g.left=(this.mGeometry.width-3*this.g.cellSide)/2;

    for(var row=0;row<3;row++)

            for(var col=0;col<3;col++) {

                    $("<div/>").attr("id","ttt-cell"+row+col).addClass("ttt-cell").

                            attr("abcell",JSON.stringify({row: row, col: col})).css({

                                    width: this.g.cellSide,

                                    height: this.g.cellSide,

                                    top: this.g.top+row*this.g.cellSide,

                                    left: this.g.left+col*this.g.cellSide

                            }).appendTo(this.mWidget);

            }

}

View.Game.DestroyView()

This method is called prior to InitView whenever the game is resized or switched. Since the default implementation perform a call to this.mWidget.empty() to clear all DOM elements created from previous calls to InitView, there is generally no need to implement the DestroyView method.

View.Board.Display(aGame)

The Board Display method is called to show the current state of the Board object.

View.Board.HumanTurn(aGame)

The HumanTurn method is called when a human player must input his move.

Typically, the method installs UI handlers to the DOM elements that can be clicked. Click handlers should be installed using aGame.mWidget.find(my selector).bind(JoclyGame.CLICK,function() { handle click }) in order to work properly on mobile platforms.

Once the move input is complete, the method must call aGame.MakeMove({move definition}).

View.Board.HumanTurnEnd(aGame)

This method is always called to indicate that no input is requested from the human player. The method must uninstall all handlers installed from the previous call to HumanTurn.

View.Board.PlayedMove(aGame,aMove)

This method is used to animate the move that was just played. If there is no animation to perform, the method must return true. Otherwise, the method returns false and when the animation is complete, a call to aGame.MoveShown() must be performed.

View.Board.ShowEnd(aGame)

This method is called when the game is over and can be used to display a particular animation to indicate the game end specifics. For instance, in an alignement game, it may be a good idea to flash the winning alignement. If there is no animation to display, the method must return true, otherwise true is returned and a call to aGame.EndShown() must be performed when the animation is complete.

Using sounds

It is possible to refine Jocly existing default sounds or to generate new ones for a given game.

First of all, it is important to understand that sounds are handled within the view, not in the model. With the view descriptor, the member ‘sounds ‘ defines a mapping between tags and sound files. For instance:

“sounds”: {

        “usermove”: “my-notification-sound”

}

redefines the default sound played whenever a human is requested to play. The system expects to find two files: /res/sounds/my-notification-sound.mp3 and /res/sounds/my-notification-sound.ogg as two versions of the same sounds, respectively in MP3 and OGG formats.

There are four pre-defined tags:

  1. usermove to atract player attention
  2. win when a human beats the computer
  3. loss when the computer wins against the human
  4. end in case of draw, or if win/loss does not make sense, like in the lab page

It is possible to define custom audio tags in the same way, as long as the appropriate audio files are provided in the /res/sounds folder.

Custom sounds playing is initiated from within the view implementation, by calling aGame.PlaySound(tag) . Good places for placing this call is from View.Board.PlayedMove and View.Board.HumanTurn. Calling aGame.PlaySound from View.Board.Display is generally not a good idea because the sound will be played whenever the board resizes.

Debugging a game

When creating a game with Jocly, the developer is likely to be in a situation requiring some insights of the code running.

A few debug tools are available for Jocly developers. They must be enabled from the Jocly profile on the web site, and are available from the lab page the game is running in.

Traces can be logged using the JocLog global function. JocLog(args,...) takes any number of parameters and will display them separated with a blank, as a string or a JSON representation.

The debug menu contains a few options:

  1. Clear log
  2. State Machine: this loads a tab containing information more related to the Jocly controler than to the game implementation itself, but it might be convenient to see why a game is blocking. For instance, the system may be waiting for aGame.MoveShown().
  3. Log game: displays the JSON representation of the game in the log
  4. Log board: displays the JSON representation of the current board in the log
  5. Log moves: log available moves from the current board
  6. Log children eval: log evaluation value of all next possible board state
  7. Random seed: when developing a game, it is common to play against the computer (or computer vs computer) to verify it works ok. When a problem occurs, it may be difficult to reproduce the issue if computer moves have a random factor (if several moves have the same evaluation, the AI will pick randomly). If a random seed value is set (whatever integer will do)
  8. Last explo: the number of Board objects considered in the last computer turn
  9. Last comp timing: the time spent in the last computer turn

Detecting game loops

Some games like Chess specifies that the game must end whenever a same board state occured several times (generally 3). Jocly has support for managing this.

The model descriptor must specify :

"gameOptions": {

            "preventRepeat": true,

 }

then, the model implementation can check the number of times the current board state happened, using aGame.GetRepeatOccurence(aBoard). A typical usage of this feature would be in Model.Board.Evaluate:

if(aGame.GetRepeatOccurence(this)>2) {

            this.mFinished=true;

            this.mWinner=JocGame.DRAW;

 }