Developing a Chess variant

From Jocly Wiki
Revision as of 20:01, 19 March 2014 by Jcfrog (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

For this tutorial, we will implement Shako, a 10x10 chess variant from Jean-Louis Cazaux, making use of a piece similar to the Xiangqi cannon and an elephant with special movement.


Get ready

Dev-chess-variant-10.png

Please setup your starting environment configuration as explained in our example My basic chess starter kit

You should now be able to run a regular chess version and have basic mode/restart/take-back/fullscreen controls and view options.

For your convenience, the result of this tutorial can be downloaded from this archive: File:Dev-chess-variant-tuto.zip.


Modifications

Edit the dev-stub.html file to add this section between the <head> and </head> tags:

<script type="text/jocly-model-view" data-jocly-game="basic-chess/shako-custom">
{
    "view": {
        "js": ["base-view.js", "grid-board-view.js", "staunton-set-view.js", "shako-custom-view.js"]
    }
}
</script>

<script type="text/jocly-resources" data-jocly-game="shako-custom">
{
    "shako-custom-view.js": "shako-custom-view.js"
}
</script>

What did we do here ? In the first script section, the type="text/jocly-model-view" attribute means that we want to define a new game. The attribute data-jocly-game="basic-chess/shako-custom" says that we want a game named shako-custom which is a copy of game basic-chess with the modifications included inside the <script> tag.

We could have simply used data-jocly-game="shako-custom" but in this case, we should have provided the full modified game specification. You can get this full specification from the Jocly game inspector

The structure in the script defines a view key, we will also have a model key later in this tutorial. All Jocly Game API enforces a clear separation between the model (basically the rules) and the view (the user interface to the game). The view key holds a js member which is the array (note it's surrounded by []) of all the Javascript files that are to be loaded to run the game view. In our case, those files are:

  • base-view.js: the base view file for all games from the chess module
  • grid-board-view.js: provides utilities to draw a board with a grid structure
  • staunton-set-view.js: defines Staunton pieces (we will later change this to use a set containing an elephant and a cannon)
  • shako-custom-view.js: a file we will provide to implement our variant
What is inside the <script> tag must be in valid JSON format, being a valid Javascript structure is not enough. In particular, this means that object keys must be surrounded by double quotes and that the last element of an array or an object must not be followed by a comma.

The second <script> tag with the type="text/jocly-resources" attribute defines the resources provided by your hosting site (by default, the files are loaded from Jocly servers). In our case, we say that the view-defined file shako-custom-view.js must be loaded from our server at the location (relative to the current page) shako-custom-view.js (note that the logic name defined in the view and the resource file do not need to have the same name but it's probably a good idea to keep them identical).

Go to the Jocly game inspector, search for Basic Chess and download the file basic-view.js, save it to your development directory under the name shako-custom-view.js.

Edit the file dev-stub.html, locate the line:

$("#applet").jocly("localPlay","basic-chess",{ });

and replace it with:

$("#applet").jocly("localPlay","shako-custom",{ });

Reload the page in your browser. You should see no difference with what we had before. What's new is that now we are running some code (in shako-custom-view.js) that we can modify to implement our game.

Edit the local file shako-custom-view.js. Change the boardLayout member from:

".#.#.#.#",
"#.#.#.#.",
".#.#.#.#",
"#.#.#.#.",
".#.#.#.#",
"#.#.#.#.",
".#.#.#.#",
"#.#.#.#.",

to:

".#.#.#.#.#",
"#.#.#.#.#.",
".#.#.#.#.#",
"#.#.#.#.#.",
".#.#.#.#.#",
"#.#.#.#.#.",
".#.#.#.#.#",
"#.#.#.#.#.",
".#.#.#.#.#",
"#.#.#.#.#.",

As you may have guessed, this describes the board and its squares. By default, '#' represents the black squares and '.' the white ones. It is possible to redefine the color and the painting method for each square (called cell in Jocly) but for this Shako examples, we will stick with black and white.

The board is now defined with 10 lines of 10 characters, which describes a 10x10 board.

If you refresh your browser at this point, it will display:

Dev-chess-variant-20.png

As you can notice, pieces are not at their correct position. This is because we need to modify their starting location. In the Jocly chess module, like in almost all Jocly games, positions are represented by contiguous integers, starting from 0, rather than by column/row coordinates (which may not fit some non-grid geometries). To see the actual position numbers on the board, we are going to make another modification to shako-custom-view.js.

First click the Notation checkbox in the view options. Note that the row numbers and column letters appear on the board.

Modify shako-custom-view.js to show this:

View.Game.cbDefineView = function() {

    var shakoBoardDelta = {
        notationMode: 'in',
        notationDebug: true,
    };      
    var shakoBoard3d = $.extend(true,{},this.cbGridBoardClassic3DMargin,shakoBoardDelta);
    var shakoBoard2d = $.extend(true,{},this.cbGridBoardClassic2DMargin,shakoBoardDelta);
    
    return {
        coords: {
            "2d": this.cbGridBoard.coordsFn.call(this,shakoBoard2d),
            "3d": this.cbGridBoard.coordsFn.call(this,shakoBoard3d),
        },
        boardLayout: [
            ".#.#.#.#.#",
            "#.#.#.#.#.",
            ".#.#.#.#.#",
            "#.#.#.#.#.",
            ".#.#.#.#.#",
            "#.#.#.#.#.",
            ".#.#.#.#.#",
            "#.#.#.#.#.",
            ".#.#.#.#.#",
            "#.#.#.#.#.",
        ],
        board: {
            "2d": {
                draw: this.cbDrawBoardFn(shakoBoard2d),                                     
            },
            "3d": {
                display: this.cbDisplayBoardFn(shakoBoard3d),                   
            },
        },
        clicker: {
            "3d": {
                scale: [.9,.9,.9],
            },
        },
        pieces: this.cbStauntonPieceStyle({
            "default": {
                "3d": {
                    scale: [.6,.6,.6],
                },
            },
        }),
    };
}

Now refresh the browser and check Notation again. The position numbers are displayed directly within the square, which will be useful soon. When we will be done with the setup we can go back to the traditional notation by commenting out those 2 lines:

notationMode: 'in',
notationDebug: true,
Dev-chess-variant-30.png

In the same way we overrode the View of the game, we are now going to alter the Model.

Back to dev-stub.html, modify the <script> sections to read:

<script type="text/jocly-model-view" data-jocly-game="basic-chess/shako-custom">
{
    "view": {
        "js": ["base-view.js", "grid-board-view.js", "staunton-set-view.js", "shako-custom-view.js"]
    },
    "model": {
        "js": ["base-model.js", "grid-geo-model.js", "shako-custom-model.js"]
    }
}
</script>

<script type="text/jocly-resources" data-jocly-game="shako-custom">
{
    "shako-custom-view.js": "shako-custom-view.js",
    "shako-custom-model.js": "shako-custom-model.js"
}
</script>

Go to the Jocly game inspector, download the file basic-model.js, save it to your development directory under the name shako-custom-model.js.

Make sure we didn't break anything by refreshing the browser.

Edit shako-custom-model.js and modify

var geometry = Model.Game.cbBoardGeometryGrid(8,8);

to

var geometry = Model.Game.cbBoardGeometryGrid(10,10);

Now our model knows about the new size of the grid.

In this Javascript code, the object returned by function Model.Game.cbDefine has a field pieceTypes. This object has keys for every type of piece that may be in play during the game. Each piece type defines information like its name, movement graph, how it must be displayed. The piece type can also have a field initial which describes the position of the piece type instances at the beginning of the game. This is an array of objects under the form {s:X,p:y} where s represents the side, 1 or -1, meaning White or Black (in Jocly terms, PlayerA and PlayerB), and p the position.

You may notice that there are more piece types than expected. There are 4 sorts of pawns corresponding to white/black and moved/not-moved combinations. This corresponds to different movement graphs. When a not-moved white pawn (type 1) is moved, it is "promoted" to a new piece type (type 0) which can no longer advance 2 squares. Since both piece types define the same aspect (pawn), the internal promotion won't appear to the player in the user interface.

We are now able to modify the starting positions of the pieces to match the new board geometry. We also add 2 new pawns according to Shako rules. The new piece definition is the following:

        pieceTypes: {

            0: {
                name: 'pawn-w',
                aspect: 'pawn',
                graph: this.cbPawnGraph(geometry,1),
                value: 1,
                abbrev: '',
                fenAbbrev: 'P',
                epCatch: true,
            },
            
            1: {
                name: 'ipawn-w',
                aspect: 'pawn',
                graph: this.cbInitialPawnGraph(geometry,1),
                value: 1,
                abbrev: '',
                fenAbbrev: 'P',
                initial: [{s:1,p:20},{s:1,p:21},{s:1,p:22},{s:1,p:23},{s:1,p:24},{s:1,p:25},{s:1,p:26},{s:1,p:27},{s:1,p:28},{s:1,p:29}],
                epTarget: true,
            },
            
            2: {
                name: 'pawn-b',
                aspect: 'pawn',
                graph: this.cbPawnGraph(geometry,-1),
                value: 1,
                abbrev: '',
                fenAbbrev: 'P',
                epCatch: true,
            },

            3: {
                name: 'ipawn-b',
                aspect: 'pawn',
                graph: this.cbInitialPawnGraph(geometry,-1),
                value: 1,
                abbrev: '',
                fenAbbrev: 'P',
                initial: [{s:-1,p:70},{s:-1,p:71},{s:-1,p:72},{s:-1,p:73},{s:-1,p:74},{s:-1,p:75},{s:-1,p:76},{s:-1,p:77},{s:-1,p:78},{s:-1,p:79}],
                epTarget: true,
            },
            
            4: {
                name: 'knight',
                graph: this.cbKnightGraph(geometry),
                value: 2.9,
                abbrev: 'N',
                initial: [{s:1,p:12},{s:1,p:17},{s:-1,p:82},{s:-1,p:87}],
            },
            
            5: {
                name: 'bishop',
                graph: this.cbBishopGraph(geometry),
                value: 3.1,
                abbrev: 'B',
                initial: [{s:1,p:13},{s:1,p:16},{s:-1,p:83},{s:-1,p:86}],
            },

            6: {
                name: 'rook',
                graph: this.cbRookGraph(geometry),
                value: 5,
                abbrev: 'R',
                initial: [{s:1,p:11},{s:1,p:18},{s:-1,p:81},{s:-1,p:88}],
                castle: true,
            },

            7: {
                name: 'queen',
                graph: this.cbQueenGraph(geometry),
                value: 9,
                abbrev: 'Q',
                initial: [{s:1,p:14},{s:-1,p:84}],
            },
            
            8: {
                name: 'king',
                isKing: true,
                graph: this.cbKingGraph(geometry),
                abbrev: 'K',
                initial: [{s:1,p:15},{s:-1,p:85}],
            },
            
        },

Refresh your browser. You can now see that the pieces have correct starting positions, however, we still don't have the additional Shako pieces: cannon and elephant.

If you look back at the javascript files we included for our view (reminder: ["base-view.js", "grid-board-view.js", "staunton-set-view.js", "shako-custom-view.js"]), you will notice the file staunton-set-view.js. This file defines the standard orthodox chess set called Staunton. The Jocly chess module also provide a fairy chess set which contains an elephant and a cannon in addition to the standard pieces. In the view javascript files, replace staunton-set-view.js with fairy-set-view.js. You must also edit shako-custom-view.js to replace cbStauntonPieceStyle with cbFairyPieceStyle:

pieces: this.cbFairyPieceStyle({ ...

Since all piece names in the fairy set are prefixed with fr-, we must make a pass on each piece type in shako-custom-model.js to either replace the aspect value with the fr- prefix (aspect: "pawn" becomes aspect: "fr-pawn") or to add the corresponding aspect: "fr-xxx" where it's not already defined. You should now have something like:

        pieceTypes: {

            0: {
                                    ...
                aspect: 'fr-pawn',
                                    ...
            },
            1: {
                ...
                aspect: 'fr-pawn',
                ...
            },
            2: {
                ...
                aspect: 'fr-pawn',
                ...
            },
            3: {
                ...
                aspect: 'fr-pawn',
                ...
            },
            4: {
                ...
                aspect: 'fr-knight',
                ...
            },
            5: {
                ...
                aspect: 'fr-bishop',
                ...
            },
            6: {
                ...
                aspect: 'fr-rook',
                ...
            },
            7: {
                ...
                aspect: 'fr-queen',
                ...
            },
            8: {
                ...
                aspect: 'fr-king',
                ...
            },
        },

Refresh the browser to verify pieces are still displayed.

Now we can add the cannon. In the pieceTypes definition of shako-custom-model.js, add:

            9: {
                name: 'cannon',
                aspect: 'fr-cannon',
                graph: this.cbXQCannonGraph(geometry),
                value: 4.9,
                abbrev: 'C',
                initial: [{s:1,p:0},{s:1,p:9},{s:-1,p:90},{s:-1,p:99}],
            },

We now defined a cannon piece type. Note the line graph: this.cbXQCannonGraph(geometry). Since the cannon movement is standard (in XiangQi, aka Chinese Chess), the chess module already provides a graph for this piece type.

This is not the case for the Shako elephant that has a special movement: it can move one square diagonally, or 2 squares leaping the first one. Add the elephant piece:

            10: {
                name: 'elephant',
                aspect: 'fr-elephant',
                graph: this.cbShortRangeGraph(geometry,[[-1,-1],[-1,1],[1,-1],[1,1],[-2,-2],[-2,2],[2,-2],[2,2]]),
                value: 2.6,
                abbrev: 'E',
                initial: [{s:1,p:10},{s:1,p:19},{s:-1,p:80},{s:-1,p:89}],
            },

The cbShortRangeGraph method provided by the chess module makes easy defining the movement of take-and-drop piece. There is also a cbLongRangeGraph method for sliding pieces (like bishop, rook and queen). If you need a more sophisticated piece movement, look at the file base-model.js from the Jocly game inspector implementation of cbShortRangeGraph and cbLongRangeGraph functions.

The chess module has limitations in the piece movements: - capture is made by replacement (the capturing piece replaces the captured one, with "en passant" capture handled specifically), - only one piece can be captured at a time, - when defining a movement/capture line, the walked-through squares must be empty, - there is special support to handle XiangQi cannon capture mode: the cannon must jump over one other piece to capture the next one on the line, - there is special support to handle XiangQi general: a piece may only be able to capture the opponent king (this move is never really played, but can be used to enforce checkmate).

Refresh your browser, you now have all the Shako pieces in place and they move according to the rules.

You may notice the pieces are a little too big for the board. Go back to shako-custom-view.js and change:

        clicker: {
            "3d": {
                scale: [.9,.9,.9],
            },
        },
        pieces: this.cbFairyPieceStyle({
            "default": {
                "3d": {
                    scale: [.6,.6,.6],
                },
            },
        }),

to:

        clicker: {
            "2d": {
                width: 1100,
                height: 1100,
            },
            "3d": {
                scale: [.75,.75,.75],
            },
        },
        pieces: this.cbFairyPieceStyle({
            "default": {
                "3d": {
                    scale: [.5,.5,.5],
                },
            },
        }),
Dev-chess-variant-40.png

Next, we need to adjust the promotion rule. Replace the promote section in shako-custom-model.js with:

        promote: function(aGame,piece,move) {
            if(piece.t==1)
                return [0];
            else if(piece.t==3)
                return [2];
            else if(piece.t==0 && geometry.R(move.t)==9)
                return [4,5,6,7,9,10];
            else if(piece.t==2 && geometry.R(move.t)==0)
                return [4,5,6,7,9,10];
            return [];
        },

We just defined that the white pawns promote on row 9 instead of 7 (rows are numbered from 0) and added 2 promotion options for piece type 9 and 10 (cannon and elephant).

We also need to adjust the castling. Replace the castle section with:

        castle: {
            "15/11": {k:[14,13],r:[12,13,14],n:"O-O-O"},
            "15/18": {k:[16,17],r:[17,16],n:"O-O"},
            "85/81": {k:[84,83],r:[82,83,84],n:"O-O-O"},
            "85/87": {k:[86,87],r:[87,86],n:"O-O"},
        },

In the castle object, the key is under the form king-position/rook-position and the value is an object with fields: - k: the array of the positions the king must go through - r: the array of the positions the rook must go through - n: how this move should appear in the game notation

You can now comment out the notation setup we made to see the position numbers on the board:

    var shakoBoardDelta = {
        // notationMode: 'in',
        // notationDebug: true,
    };      
Dev-chess-variant-50.png

You can switch to the 2D skin to verify everything is ok.

Dev-chess-variant-60.png

Congratulations, you now have your own chess variant running on your site.