Structure

Each card game is a single .js file inside the rules directory. The script declares a object with a name, and a few other required functions, and then calls on the Controller to register the game rule.

We wrap our script in a closure to prevent littering the namespace.

/**
 * A rules object that defines the layout of our game, how cards are dealt and win conditions.
 *
 */

;(function(){
  
  var template = { name: 'template' };
  
  template.requestLayout = function() {
    // Requested by the view when the game is started.
  };
  
  template.requestRulesWording = function() {
    // Requested when the player opens the game rules help page.
  };
  
  template.requestDeal = function(dealer, cards) {    
    // Called when a new game is dealt.
  };
  
  template.allowDragEvent = function(dragged) {
    // Determines if dragging one or more cards is allowed.
  };
    
  template.dropEvent = function(dragged, dropped) {
    // After one or more cards are dropped by the user.
  };

  template.setup = function() {
    // Called by the controller once the playfield has been set up.
  };
  
  // Register our game with the engine. It will show up in the games list.
  game.controller.registerGame(template);

})();

The requestLayout Function

The layout of the play field works on a grid system.

  template.requestLayout = function() {
    
    var layout = { };
    
    // Number of columns and rows our game will need.
    layout.columnsRequested = 6;
    layout.rowsRequested = 4;
    
    return layout;
    
  };

Grid System

The columnsRequested and rowsRequested values determine our play field. Cards are resize to fit so more columns mean smaller cards. Some example grids to give a visual understanding:

A 3x1 Grid
3x1 Grid

A 6x2 Grid
6x2 Grid

A 10x4 Grid
10x4 Grid

Zones

Zones can occupy a single grid space, or span multiple grid spaces. Zones provide an easy way to tell the game where to place cards. Zones are measure in grid column/row positions.

  template.requestLayout = function() {
    
    var layout = { };
    
    // Number of columns and rows our game will need.
    layout.columnsRequested = 6;
    layout.rowsRequested = 4;
    
    // Play zones
    layout.zones = {
      'tableau': { col:1, row:2, width:6, height:3 },
      'reserve': { col:1, row:1, width:1, height:1 },
      'discard': { col:2, row:1, width:1, height:1 },
      'hand': { col:6, row:1, width:1, height:1 }
    };

    return layout;
    
  };

The zones (white) highlighted here span across multiple grid spaces (blue).

Zones

Later you will see how easily we can deal cards into these zones, and move them around in the drop event.

The requestRulesWording Function

This return the wording for your game rules. You can return a plain string, or HTML formatted markup.

  template.requestRulesWording = function() {
    return `<p>This is a template game you can use to build your own card games.</p><p>The rules are simple:</p> <ul><li>Pick any top card to replace your current hand, which will be discarded</li><li>If you pick up the joker you win the game.</li></ul>`;
  };

The requestDeal Function

This function handles dealing a new game. It receives a dealer/deck (for creating new decks), and a cards object where you place your dealt cards.

When storing your dealt cards on the cards object, we refer to the same zones you set up earlier.

  template.requestDeal = function(dealer, cards) {
    
    // create a new deck, fill and shuffle it.
    var deck = dealer.new();
    deck.fill();
    deck.shuffle();
    
    // take the top cards as our hand
    cards.hand = deck.take();
    
    // face our hand up. Since a hand is a pile of cards, get the first item from this pile.
    cards.hand.get().up = true;
    
    // add the joker to the deck and reshuffle.
    // the joker value can be 101, 102 or 103, each corresponding
    // to a different image of the joker
    deck.add(deck.card(103, 'JOKER'));
    deck.shuffle();
    
    // face all the cards up
    deck.cards.forEach(function(card){
      card.up = true;
    });

    // tableau (an array of six piles)
    cards.tableau = [ ];
    for (var i = 0; i <= 5; i++) {
      cards.tableau[i] = deck.take(9);
    };
    
    // any zones we do not initialize (reserve, discard) will get set
    // to empty piles for us when our deal function ends.
  
  };

The allowDragEvent Function

This is called when the player starts a drag action. The dragged object contains information about the cards involved, the grid position and zone.

  template.allowDragEvent = function(dragged) {
    
    // Only allow single drags:
    return dragged.cards.length == 1;
    
  };

The dropEvent Function

This is called when a drop action completed. This only happens if the allowDragEvent returned true earlier.

The dropped object contains information about the cards involved, the grid position and zone.

  template.dropEvent = function(dragged, dropped) {
   
    if (dragged.zone == 'tableau' && dropped.zone == 'hand') {

        // discard our hand
        game.controller.place(game.controller.take('hand'), 'discard')
        
        // place new card in hand
        game.controller.place(dragged.cards[0], 'hand');

    };
    
  };

The setup Function

Called when your game is ready to start. Usually we request from the controller to begin the deal.

  template.setup = function() {
  
    game.controller.deal();
  
  };

Journal

Date Title
design pattern
rules