January 30, 2017

Jscrambler Themed Sliding Puzzle Tutorial Built with Phaser.js

by Jscrambler

Jscrambler Themed Sliding Puzzle Tutorial Built with Phaser.js

Introduction

Every day, we encounter puzzles in various forms. Most of the JavaScript code is ultimately a technical puzzle waiting to be solved.

Developers need to find creative solutions for these puzzles, from probing for weaknesses in real-world systems to creating scalable web apps.

Countless JavaScript frameworks are created with the express intent of solving a specific “puzzle”. One such framework is Phaser.js. If you’re a developer with a penchant for game development you may have heard of it.

Game Development

Phaser is a well designed and thoroughly documented JavaScript game development library created by the good folks at Photonstorm. Building games is totally different than typical application development. It can be an especially daunting task for those new to game development. Fortunately, Phaser was built with the sole purpose of making game development easier.

Those lucky enough to have used a vintage Macintosh may have played the memorable sliding puzzle game that came included as part of Mac OS 7-9.

sliding-puzzle-vintage-macintosh

Because we now live in the Golden Age, we’ll create our own Jscrambler themed sliding-puzzle game using Phaser.js. The basic game logic for our sliding puzzle game will be as follows:

  • Separating and shuffling the image into tiles
  • Event-handling of mouse-clicks
  • Moving tiles with animation
  • Re-calculating the tiles based on position of black tile
  • Define win conditions (puzzle solved criteria) based on position of indexed tiles

To begin, we encapsulate and load our entire game states using the Phaser.Game method. The game method falls within the scope of a single object, Phaser.Game. Put simply, the phaser game object represents the entire game state window:

Create a game.js

var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', {  
    preload: preload,
    create: create
});

var PIECE_WIDTH = 200,  
    PIECE_HEIGHT = 200,
    BOARD_COLS, BOARD_ROWS;

Our first few variables define the size of each piece of the puzzle so that the background image is sliced correctly:

var piecesGroup,  
    piecesAmount,
    shuffledIndexArray = [];

We then create variables for our group of pieces, amount of pieces and the Array for our shuffled puzzle, shuffledIndexArray.

function preload() {  
    game.load.spritesheet("background", "15puzzlebg.png", PIECE_WIDTH, PIECE_HEIGHT);
}

Next, we preload our background image (which you can grab here), while we define the parameters for the width and height of each sliding piece.

function create() {  
    prepareBoard();
}

Here is where all the fun begins, by creating a function that prepares our board with the individual pieces shuffled we can give each tile a variable to track index positions down the line.

function prepareBoard() {

        var piecesIndex = 0,
            i, j,
            piece;

The first portion of the function has to set the Index to 0 while defining our variables for the board rows and columns.

    BOARD_COLS = Math.floor(game.world.width / PIECE_WIDTH);
    BOARD_ROWS = Math.floor(game.world.height / PIECE_HEIGHT);

In this next portion of our code we pass in the width and height we selected earlier and sets the state of the board. We create a variable for the shuffled Index as shuffledIndexArray.

    piecesAmount = BOARD_COLS * BOARD_ROWS;
    shuffledIndexArray = createShuffledIndexArray();

The piecesGroup variable houses our algorithm. The purpose of our algorithm is to check for a black space within the group of shuffled pieces while placing the tile piece positions and allowing for user input:

    piecesGroup = game.add.group();


    for (i = 0; i < BOARD_ROWS; i++) {
        for (j = 0; j < BOARD_COLS; j++) {
            if (shuffledIndexArray[piecesIndex]) {
                piece = piecesGroup.create(j * PIECE_WIDTH, i * PIECE_HEIGHT, "background", shuffledIndexArray[piecesIndex]);
            } else { //initial position of black piece

                piece = piecesGroup.create(j * PIECE_WIDTH, i * PIECE_HEIGHT);
                piece.black = true;
            }
            piece.name = 'piece' + i.toString() + 'x' + j.toString();
            piece.currentIndex = piecesIndex;
            piece.destIndex = shuffledIndexArray[piecesIndex];
            piece.inputEnabled = true;
            piece.events.onInputDown.add(selectPiece, this);
            piece.posX = j;
            piece.posY = i;
            piecesIndex++;
        }
    }

    }

    function selectPiece(piece) {


        var blackPiece = canMove(piece);



        if (blackPiece) {
            movePiece(piece, blackPiece);
        }

    }

We use the above code to check for our black tile nearby. The canMove function is for ensuring that the tile piece that is interacted with is allowed to move to a new position.

function canMove(piece) {

    var foundBlackElem = false;

    piecesGroup.children.forEach(function(element) {
        if (element.posX === (piece.posX - 1) && element.posY === piece.posY && element.black ||
            element.posX === (piece.posX + 1) && element.posY === piece.posY && element.black ||
            element.posY === (piece.posY - 1) && element.posX === piece.posX && element.black ||
            element.posY === (piece.posY + 1) && element.posX === piece.posX && element.black) {
            foundBlackElem = element;
            return;
        }
    });

    return foundBlackElem;
}

Next, we add the movePiece function to create a temporary piece variable that is based on the current index of the piece.

function movePiece(piece, blackPiece) {


        var tmpPiece = {
            posX: piece.posX,
            posY: piece.posY,
            currentIndex: piece.currentIndex
        };

Our code should switch the places of the chosen piece with our black tile aka blackPiece. Then after every move we initiate a check to see if the puzzle is completed with checkIfFinished();

    game.add.tween(piece).to({
        x: blackPiece.posX * PIECE_WIDTH,
        y: blackPiece.posY * PIECE_HEIGHT
    }, 300, Phaser.Easing.Linear.None, true);

    piece.posX = blackPiece.posX;
    piece.posY = blackPiece.posY;
    piece.currentIndex = blackPiece.currentIndex;
    piece.name = 'piece' + piece.posX.toString() + 'x' + piece.posY.toString();

    // previously clicked piece is the new black tile
    blackPiece.posX = tmpPiece.posX;
    blackPiece.posY = tmpPiece.posY;
    blackPiece.currentIndex = tmpPiece.currentIndex;
    blackPiece.name = 'piece' + blackPiece.posX.toString() + 'x' + blackPiece.posY.toString();
    checkIfFinished();
    }

    function checkIfFinished() {

        var isFinished = true;

        piecesGroup.children.forEach(function(element) {
            if (element.currentIndex !== element.destIndex) {
                isFinished = false;
                return;
            }
        });

        if (isFinished) {
            showFinishedText();
        }
    }

Lastly, we have to announce when the user completes the puzzle correctly by creating a showFinished Text function:

function showFinishedText() {  
    var style = {
        font: "40px Arial",
        fill: "#000",
        align: "center"
    };
    var text = game.add.text(game.world.centerX, game.world.centerY, "Congratulations! \nYou made it!", style);
    text.anchor.set(0.5);
}

The createShuffledIndexArray creates a brand new array based off of the shuffled puzzle piece index.

function createShuffledIndexArray() {

    var indexArray = [];

    for (var i = 0; i < piecesAmount; i++) {
        indexArray.push(i);
    }

    return shuffle(indexArray);

}

The following is likely the second most important function within our game. Within this function, we use Math.floor to create a randomized index which we iterate over every time we check to see if our puzzle is shuffled.

function shuffle(array) {

    var counter = array.length,
        temp,
        index;

    while (counter > 0) {
        index = Math.floor(Math.random() * counter);

        counter--;

        temp = array[counter];
        array[counter] = array[index];
        array[index] = temp;
    }

    return array;
}

Our completed Jscrambler sliding puzzle code is somewhat lengthy but we’ve created an entire game using only one library.

Adding Jscrambler Security

The final piece of the puzzle is protecting our app. For brevity, we’ll skip the full rundown on setting up the web pack loader because the complete how-to is already available here in npm. The next part is as easy as copying the code directly as new files within the dashboard:

jscrambler-dashboard

As a priority, we’ll need to download a JSON file with the corresponding API Credentials. These can also be found on the Profile Page once you’ve logged into the Jscrambler Account Panel.

Here we're applying transformations that include Self Defending and Date Locks. Self Defending will protect the App from being tampered and debugged, while the Date Lock will only allow the App to run in a certain time interval, useful for delivering expirable demos.

In addition, Date Locks, and all other Locks, allow you to have a warning function that will be triggered if someone tries to run the App outside the parameters you set. With this function, you can be warned whenever someone tries to use your App incorrectly.

jscrambler.json  
{
  "keys": {
    "accessKey": “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”,
    "secretKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  },
  "applicationId": “YYYYYYYYYYYYYYYYYY”,
  "params": [{
        "name": "whitespaceRemoval"
    }, {
        "options": {
            "mode": "SAFEST"
        },
        "name": "identifiersRenaming"
    }, {
        "name": "duplicateLiteralsRemoval"
    }, {
        "name": "functionReordering"
    }, {
        "name": "dotToBracketNotation"
    }, {
        "name": "functionOutlining"
    }, {
        "name": "selfDefending"
    }, {
        "name": "booleanToAnything"
    }, {
        "name": "propertyKeysReordering"
    }, {
        "name": "propertyKeysObfuscation"
    }, {
      "name": "dateLock",
      "options": {
        "endDate": "2017/02/01",
        "startDate": "2017/01/18"
      }
    }],
  "areSubscribersOrdered": false,
  "applicationTypes": {
    "webBrowserApp": true,
    "desktopApp": false,
    "serverApp": false,
    "hybridMobileApp": false,
    "javascriptNativeApp": false,
    "html5GameApp": true
  },
  "languageSpecifications": {
    "es5": true,
    "es6": false,
    "es7": false
  }
}

That’s it! Simply running Webpack after configuration of our webpack.config.js will bundle our entire Puzzle.

Conclusion

Anyone willing to dip their feet in the waters of game development should know Phaser.js is shallow enough to keep you from drowning in complexity.

There are a few other options out there for game development with JavaScript. Pixi.js (which Phaser wisely incorporates) is great for lightning fast 2D rendering and Bablyon.js is used for 3D/WebGL games. Where Phaser stands out is that it provides support for both Canvas and WebGL rendering. It garners wide support has a very active community, making it one of the best performing JS game engines available today.