Creating an AI for Risk board game

Introduction

I am currently working on a Javascript-based implementation of the classic boardgame Risk. It is written using ES2015 JS-syntax and I use Babel for transpiling. Currently I have a fully functional game engine so that two players can play on the same computer (hotseat) against each other. In the future I hope to implement online mutliplayer so that several human player can play against each other, each on their own computer, but this is not something that I am realistically going to finish anytime soon. In the meantime though I wanted to enhance the singleplayer aspect by adding an AI to play against. The goal was to make the AI at least fairly competent at the game so that it would provide a decent challenge to play against, playing against a bad AI that just do nonsensical moves at random would not be very fun.

You can find the project in question here.

The rules of Risk

Before I go about explaining my approach towards creating the AI I need to go over some of the rules of Risk (don’t worry, I won’t recite the entire rulebook, just the relevant parts). In classic Risk you play against each other on a game board of the world map. The goal is to conquer land, expand your territory, defeat your opponents and become as powerful as possible. In classic Risk you win by simply taking over every single territory on the map, but for my implementation of the game I have added the ability to choose the desired percentage of map control to achieve victory. This is because a game of Risk where you have to conquer 100% of the map can sometimes take an excruciatingly long time to finish, and it’s often quite clear anyway after a certain point who will most likely win.

The world map on the Risk game board is divided into regions (continents), each of which are then in turn broken down into territories. For instance in the game of Risk the region of Africa consists of 6 different territories. If a player holds every territory in a region then the player will recieve additional reinforcements each turn as a region bonus. Because of this, it is highly prioritized for players to try and secure entire regions and then try to hold them as they are very important for reinforcements.

1462528836773051681

There are three different phases to a players turn; deployment, attack and movement phase. During deployment you get additional troops to deploy. In the attack phase a player attacks and tries to take over other territories and in the movement phase the player may make one strategic movement of troops from one territory to another.

If you have won at least one battle during a turn you may draw a card from a pile. These cards can be collected and traded in as a combination of 3 cards during the deployment phase to gain additional reinforcements.

Theory

Contemplating important factors

For all three phases of a turn there are important factors that a player needs to consider. For instance in the reinforcement phase you have to think one step ahead and try to deploy troops in good positions from where you can then attack in the attack phase, but also you need to identify territories that are especially important to protect and therefore reinforce them if they are threatened (for instance if an opponent has lots of troops in an adjacent territory). Let’s say a player controls the entire region of South america. There are only two territories in South america that borders to other regions, Venezuela (that borders to central america) and Brazil (that borders with North africa). Because of this these two territories are especially important to reinforce if they are adjacent to enemy territory, since they are the entry points into South america. Placing lots of troops in Argentina though would not make sense because they can’t protect the region from there. These are all things that a player, and therefore also my AI, needs to take into account when planning deployment.

Capture

As for attacking this goes pretty much hand in hand with the deployment phase, because when a player deploys troops to a specific territory he usually have an attack target in mind to attack from there. There are also many things to take into account when attacking. For instance you always ideally want a numerical advantage of troops, especially since in Risk the defender has an advantage. Also a player needs to take into account the overall strength of the player to attack. Let’s say you’re playing a 3 player game, both player 1 and 2 are very strong with large territories but player 3 is very small and weak. In this scenario it might be more important for player 1 to try to prepare against and attack player 2, because he/she is the largest threat, player 3 however does not constitute as large of a threat and therefore does not need to be prioritized as much as a target. However let’s say player 3 is so weak that he/she is on the verge of being eliminated, let’s say player 3 has just one territory left that is badly defended, then it’s probably worth it for both player 1 and 2 to try and eliminate player 3 because that would mean one less enemy and also when you eliminate an opponent in Risk you may gain all of his or her cards. All of these are factors that also need to be accounted for when deciding where to attack and where to deploy.

Third and finally is the movement phase. Here it also very important to make a wise decision. You only get to make one movement, you can’t do everything that you might want to do, so you have to try and make the most optimal movement. Just like with deployment, it’s important to think about which territories are especially important to protect and that are under threat of being attacked, for instance a territory that acts as a choke point frontline.

Basically I tried to analyze whatever factors I try to take into account when playing Risk and I wrote them all down, for these are also the factors I’d like for my AI to contemplate.

Initial approach

Importance points

As I started working on the AI I had the idea, inspired by “minxmax” decision making, that for each turn phase I should determine every possible move and then try to find the best one(s). For instance for the deployment phase I would for each possible option (territory to deploy to) determine the value/importance of that option. I would then take into account all the relevant factors for each option by basically validating a series of different conditions.

  • Is the territory adjacent to a territory belonging to a player that can be easily eliminted?
  • Is the territory adjacent to a player that can be considered a big threat?
  • Is the territory a part of a region that I currently control that is therefore important to me?
  • Is the territory a part of a region that I am close to capturing completely?
  • If yes to previous statement, then also, how good is this region (considering they all have different region bonuses dependenat on how hard or easy they are to capturing)?
  • Is the territory adjacent to a viable target that can be taken in order to break up another players controlled region, thus denying him/her a reinforcement bonus?

After answering all of these conditions for every possible move I am then able to use this to calculate a value used to symbolize the importance of the move. By making this into a number I can easily then sort the list and then pick the move or moves with highest importance ratings. Codewise it would look a bit like this (simplified):

possibleTerritoriesToAttack.forEach(territory => {
    territory.importancePoints = 0;
    territory.importancePoints += territory.opportunityToEliminatePlayer ? 5 : 0;
    territory.importancePoints += territory.belongsToBigThreat ? 3 : 0;
    territory.importancePoints += territory.mostTroopsInThisRegion ? 3 : 0;
    territory.importancePoints += territory.closeToCaptureRegion ? 6 : 0;
    territory.importancePoints += territory.canBeAttackedToBreakUpRegion ? 5 : 0;
    territory.importancePoints += territory.lastTerritoryLeftInRegion ? 3 : 0
});

(There are actually more conditions in the actual game but for this article I’m choosing to cover these ones)

By the snippet you can see that for different conditions I add different “importancePoints” to the total for each move. I didn’t include the code where I determine all of these different factors for each condition as I figured there would be too much, but you can analye the entire code by reading this file.

After having finished calculating the importancePoints for each possible move I can simply sort my list by importancePoints and then the first move in the list should be the best possible choice, assuming my current model for determining that is working.

Threat

As mentioned above, some of the conditions for determining importancePoints also take into account the “threat” of the player owning the viable attack target. To determine such a thing I also needed some sort of system of calculating overall threat of another player, and I ended up using a very similar approach to how how I calculate importancePoints. Here is the function:

calculateStandingsWithThreatPoints() {
    const currentStandings = getCurrentOwnershipStandings(this.gameEngine.map, this.gameEngine.players);
    currentStandings.forEach(standing => {
        standing.threatPoints = 0;
        standing.threatPoints += standing.totalTroops;
        standing.threatPoints += (Math.floor(standing.totalTerritories / 2));
        standing.threatPoints += (standing.cardsOwned * 2);

        standing.regionsOwned.forEach(region => {
            standing.threatPoints += (Math.floor(this.gameEngine.map.regions.get(region).bonusTroops * 1.5));
        });
    });
    currentStandings.sort((a, b) => a.threatPoints - b.threatPoints);
    return currentStandings;
}

Basically the threat of a player is determined by:

  • How many total troops the player has across all territories
  • The amount of territores controlled by the player
  • How many cards the player currently has on hand
  • How many regions the player controls and how big reinforcements bonuses they
    each give

This way we can, just like with importancePoints, calculating a value called “threatPoints” for each player which can then be used when determining importancePoints. As you can see in the code snippet there are also modifiers to some of these threatPoints, for instance:

standing.threatPoints += (Math.floor(standing.totalTerritories / 2));
standing.threatPoints += (standing.cardsOwned * 2);
standing.regionsOwned.forEach(region => {
    standing.threatPoints += (Math.floor(this.gameEngine.map.regions.get(region).bonusTroops * 1.5));
});

This way the amount of threatPoints for a given condition can be modified and adjusted. In this example for instance each card owned equals 2 threat points, whereas each territory owned is just worth 0.5 threat points. That is because holding many territories is not really in and of itself a very significant thing, what’s important in Risk is to control entire regions (which explains the 1.5 modifier for each region reinforcement bonus). Similarly having many cards on hand is also very powerful as these can be turned in for a lot of extra troops, which is why they’re considered valuable. By having these modifiers I can then later readjust and tweak the system in order to optimize the calulation. More on this later.

Movement

For movement I wen’t with pretty much the exact same approach as with reinforcement/attack phase. Basically just devise a strategy to calculate a value for each possible option, determine which option is the best by sorting by this value and then choosing the top ranked option as the move to use. For movement I had some different conditions though to consider when calculating my importance value:

  • Is the territory (to move to) a frontline that borders with enemies?
  • If so, do my enemies have many troops adjacent to the territory which would constitute a big threat of being invaded?
  • If the territory borders with an enemey, how strong is that enemy compared to me? (Based on threat points once again)
  • Is the territory an important defensive strategic position for holding a currently controlled region?

AI testing

Problem

One problem so far is that my systems in place used for determining best moves are based upon adding values together based on different conditions, but it’s pretty hard knowing how many points a certain condition should be valued as. For instance in the example snippet displayed above regarding valuePoints possible attack targets I add 3 points if the condition “belongsToBigThreat” is met and 6 points if the condition “closeToCaptureRegion” is met, but how can I know for sure that these numbers are optimal for each given condition? The answer is that I don’t, so far I have only declared these values myself by pretty much shooting from the hip, guesstimating what I think sounds reasonable, but I need to do something about this.

Solution

I figured I could optimize my declared values used for determining value of possible choices by conducting AI tests. Basically what I do is that I let an AI player play against another AI player. Player 1 uses the default values I already have declared myself whereas player 2 gets a randomized confiugration of values. They play each other until one of them wins. The winner gets to keep it’s configuration of values and the loser gets a new randomized configuration. Then they simply keep this up and play game after game against each other, all the while changing the configuration of used values so that the ones that actually seem to work and produce good results are the ones that are passed on to the next iteration. It becomes sort of an evolution. I created a game mode specifically for this task where the speed of the AI is really fast and where the game automatically resets when one of them wins and keep tracks of the games played. I decided to set something like 80% map control as win condition and then I just let my two AI foes duke it out against each other. After just about an hour or so they had already played over 3000 games against each other, so I took the last successful winning configuration and started using that as the default for my AI player.

Here’s a video of how the automated speed testing looked.

Result

I’m quite happy with the result. Even though the AI is far from perfect, considering this is the first version of the AI I think it works very well. The moves that the AI does seem logical and makes sense, at least almost every time.

Below is a video of 6 AI opponents playing against each other in a free for all. I have sped up the video quite since it was originally 8 minutes long.

Future improvements

Like I said, I consider this a first iteration of more to come regarding my Risk AI, but for now this will do just fine. I have thought about improvements I could implement to improve the AI and here are some of the things I’ve gathered:

  • MORE AI TESTING I should do even more AI testing, letting AI play against AI and let it “learn” along the way what works best. I could also make the process smarter, instead of simply generating a new configuration at random at every iteration it could somehow try to remember what has worked in the past and build on that incrementally. I haven’t come far with analyzing how this is supposed to be done though, but that’s a future problem.
  • AI FOR TEAM GAMES There are different things you need to consider when playing against many opponents as opposed to playing just 1vs1. Therefore I should also conduct AI testing where several AI players face off in a free for all. I could also add specific conditions that makes sense in these scenarios.
  • THINK AHEAD One big difference between my AI and a human player is that human player usually tries to think several steps ahead whereas the AI don’t do that. The AI assesses the best possible options move by move, it doesn’t analyze and come up with long term goals like a human would. Despite this it kind of works out quite well anyway because of the current logic I have in place. For instance the AI is configured to try to prioritize capturing regions, because of how valuable these are. In the sped up gameplay video you can also see that each AI player is actively working towards that and are trying to solidify control of regions, instead of just attacking randomly all over the map.
  • MORE AI VARIETY In the final version I would like to have more than one type of AI. There could for instance different difficulty levels for each AI. One other thing is to have AI:s that behave a bit differently and adopt different strategies. For instance one could be more defensive and play a turtle style, whereas another plays very aggressively.
  • MORE SCRIPTED DECISIONS I need to add some “scripted decisions” which basically mean that for some certain conditions my ordinary systems of value points are overruled and the AI instead follows a predetermined behaviour. An example of this would be if the AI is just one territory away from winning the game. In such a case most human players would try their best to conquer that last territory in order to win the game, even if it might mean taking a fight where the odds are not entirely in your favour, however my AI as it is right is programmed to not ever try to make risky decisions. This is why I need to identify possible scenarios where such scripted decisions are necessary.

 

Advertisement

Promise chains with delays and pauses

Introduction

As a part of my recent hobby development of a Javascript-based version of the classic boardgame “Risk” I have played around quite a bit with promises and I wanted to share some stuff that I found useful.

First of all, I am writing code in the recent ES2015 syntax, so the Promises am I using are the newly added native Promises, no framework needed. I am then using Babel to transpile my code thus making it backwards compatible with old browser.

Full demo can be found at the bottom of this article.

Delays

I wished to create an AI opponent to play against for my Risk-game. When it’s the AI players turn it should execute a sequence of actions such as turn in cards, reinforce territories, attack territories and lastly ordering a troop movement (those of you who have played Risk are surely familiar with these turn phases). I could just let the AI execute all of these commands one after another but it would go by so fast that a human player would not be able to see what’s happening. Because of this I want some pauses between each action.

Javascript is of course single-threaded and do not have any equivalents of Thread.sleep (like in Java), but it does have some nifty timing- and interval-functions like setTimeout that executes a function after a delay.

To perform a chain of commands using setTimeout it would look something like this:

const turnInCards = () => { console.log('1') };
const deployment = () => { console.log('2') };
const attack = () => { console.log('3') };
const movement = () => { console.log('4') };

setTimeout(() => {
    turnInCards();
    setTimeout(() => {
        deployment();
        setTimeout(() => {
            attack();
            setTimeout(() => {
                movement();
            }, 1000);
        }, 1000);
    }, 1000);
}, 1000);

If you run this code in your browsers console it would output 1, 2, 3 and 4 one after another with 1 second delay between each.

Of course this looks rather horrendous, and the longer the chain would become the crappier it would look, and also becomes harder to navigate and manage the code. In Javascript there already exists a nice solution to getting out of many of these callback hells and it is of course use of Promises. By making all of our commands into functions that returns promises we can chain them together and force to execute one after another. To also have a delay between each command we also need to use some kind of delay function. I wrote one that looks
like this:

const delay = ms => new Promise((resolve, reject) => {
    setTimeout(resolve, ms);
});

Delay returns a Promise that uses the setTimeout-function by resolving itself after a given amount of time. Now we can use this delay function in a chain of promises, like this:

const turnInCards = () => { console.log('1'); Promise.resolve(); };
const deployment = () => { console.log('2'); Promise.resolve(); };
const attack = () => { console.log('3'); Promise.resolve(); };
const movement = () => { console.log('4'); Promise.resolve(); };

delay(1000)
.then(turnInCards)
.then(() => delay(1000))
.then(deployment)
.then(() => delay(1000))
.then(attack)
.then(() => delay(1000))
.then(movement);

Try to run this piece of code in your terminal (don’t forget the declared delay function) and it will produce the exact same outcome as the previous snippet, but this one looks so much cleaner because don’t get a nested tree of callbacks. Everything is on the same line. We could also refactor the code to look like this:

const turnInCards = () => { console.log('1'); return delay(1000); };
const deployment = () => { console.log('2'); return delay(1000); };
const attack = () => { console.log('3'); return delay(1000); };
const movement = () => { console.log('4'); return delay(1000); };

delay(1000)
.then(turnInCards)
.then(deployment)
.then(attack)
.then(movement);

We could just make the command functions return the delay function instead, which in turn returns a Promise, so this way the Proimse-chain becomes even cleaner and easier to follow.

Pausing

Next I wanted to be able to pause the game. If the AI opponent is in the middle of his turn I want to be able to press a pause button at which point the chain of commands pauses for as long as I like, until I unpause. For this I created a recursive promise-function called pauser that looks like this:

pauser() {
    if (!isPaused) {
        return;
    }
    return delay(500).then(() => pauser());
}

This function utilizes the delay function I already previously written. When called it checks if the game is not paused, in which case it just returns. If the game is paused however it will delay for 500 ms and then call itself, and it will continue to call itself (since it’s recursive) until the game unpauses. Now I need to add the pauser function between each command in the chain.

const turnInCards = () => { console.log('1'); return delay(1000); };
const deployment = () => { console.log('2'); return delay(1000); };
const attack = () => { console.log('3'); return delay(1000); };
const movement = () => { console.log('4'); return delay(1000); };

delay(1000)
.then(() => pauser())
.then(turnInCards)
.then(() => pauser())
.then(deployment)
.then(() => pauser())
.then(attack)
.then(() => pauser())
.then(movement)
.then(() => pauser());

In this example if you follow the chain delay is always called before pauser, so to clean up the chain a bit I could refactor so that these two are executed in the same function that utilizes both pauser and delay. It could look something like this.

const delayAndPause = () => delay(1000).then(() => pauser());

const turnInCards = () => { console.log('1'); return delayAndPause(); };
const deployment = () => { console.log('2'); return delayAndPause(); };
const attack = () => { console.log('3'); return delayAndPause(); };
const movement = () => { console.log('4'); return delayAndPause(); };

delayAndPause()
.then(turnInCards)
.then(deployment)
.then(attack)
.then(movement);

Now if the window variable isPaused is somewhere along this chain changed to true, the chain will pause and wait for it to become false again. I created a JS-Fiddle to demonstrate here:
https://jsfiddle.net/ToWelie89/dwnb3yh1/

I hope you found this post useful and informative 🙂 Feedback is always appreciated.

Peace out.

Speed up your website

Introduction

This is small tutorial that covers some tips how you can improve the performance of your website. Many of these steps include tasks that can be handled by a build tool (I use Grunt) so I recommend you using a build tool like that. You can test your sites current performance by using a tool like this one or this one.

Don’t use images when it’s not needed

In the earlier days of web development images were used for all sorts of things; buttons, icons, gradients, backgrounds and even texts for displaying headings and logotypes that required specific fonts. With modern day web technlogies though you can easily recreate all of these things without the use of image files.

  • Buttons: CSS3 introduced lots of interesting attributes that can create effects that could previously only could be achieved by rendering a image in for instance Photoshop and then using the img-tag. Attributes like border-radius for rounded corners, box-shadow for drop shadow effects, text-shadow for text shadow effects, text-stroke for text outline as well as a bunch of neat CSS animations for click/hover/focus events. You can use a library like Bootstrap to get some decent looking default buttons. Here is a nice codepen as an example of what can be achieved with HTML and CSS only.
  • Icons: Instead of using images for small icons consider using a glyph icon library like Bootstrap icons or Fotn awesome. It’s better for performance and also scales better.
  • Gradients: If for whatever reason you wan’t to use gradients you can use a gradient css generator like this one to generate css gradients.
  • Backgrounds: It is possible to create a lot of quite impressive background patterns using CSS only, here are a few examples of these.
  • Texts: With CSS3 you can use custom fonts for texts, so there is no need to create images for headings and logos. Here is a nice tutorial about this. You may also consider using a convenient service like Google fonts for this. Here are a few examples of impressive typographies created using CSS only; Example 1, Example 2, Example 3, Example 4, Example 5, Example 6, Example 7, Example 8

Optimize images

There are obviously still some instances where you must use images, for instance when using photos for hero components and whatnot. When using image files you should remember to optmizie them. You can do this by using a simple service like Optimizilla. You’ll be surprised how much space you can save by optmizing images while not seemingly losing any photo quality at all.

Minify JS

I use the build tool Grunt and the plugin uglify to minify, compress and uglify all my Javascript code. This will reduce the file size making it load faster. Here’s how to set it up using Grunt. First install the plugin (this assumes you already have npm and Grunt setup):

npm install grunt-contrib-uglify --save-dev

Now configure your Gruntfile.js:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.registerTask('default', [
        'uglify'
    ]);
};

Now you can compress and uglify your Javascript by just running the default Grunt build task. This will take all js-files (recursively) in your js-folder and compile them into one large js-file. The output will be located in build/main.min.js.

Minify CSS

Minifying CSS is also recommended. I recommend the Grunt plugin grunt-contrib-cssmin. Install it by running:

npm install grunt-contrib-cssmin --save-dev

Personally I don’t use this plugin. Instead I am using the plugin “grunt-contrib-less” (since I am using less as a css postprocessor). This plugin will not only transpile less to css but also minify. However if you are not using less you can use grunt-contrib-cssmin. I add the following configuration to my Gruntfile.js:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        },
        cssmin: {
            minify: {
                src: 'css/default.css',
                dest: 'build/default.min.css'
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.registerTask('default', [
        'uglify',
        'cssmin'
    ]);
};

This will take the file css/default.css and minify it, the output will be located in build/default.min.css.

Lazy load CSS

If your website uses very large CSS-files you might wan’t to consider lazy loading your CSS. Or perhaps you could break up your css into a default stylesheet file that can be included normally and one that can be lazyloaded. Simply lazy load css by using the following script:

<script>
var cb = function() {
    var l = document.createElement('link');
    l.rel = 'stylesheet';
    l.href = 'yourCSSfile.css';
    var h = document.getElementsByTagName('head')[0];
    h.parentNode.insertBefore(l, h);
};
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
    webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);
</script>

Inline CSS

Another way to speed up your site is to simply inline you CSS. This means that instead of referencing a separate css-file you paste the full css in a style-tag within the head-tag. By doing this your browser won’t have to download the css-file and it reduces the amount of HTTP requests your browser have to make. Obviously it’s not very convenient having to constantly copypaste your css to your markup so I suggest using the grunt plugin grunt-replace for inlining CSS on every build. Simply run:

npm grunt-replace --save-dev

Then modify your Gruntfile.js file so that it looks a bit like this:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        },
        cssmin: {
            minify: {
                src: 'css/default.css',
                dest: 'build/default.min.css'
            }
        },
        replace: {
            inline: {
                options: {
                    patterns: [{
                        match: 'defaultCss',
                        replacement: '<%= grunt.file.read("build/default.min.css") %>'
                    }]
                },
                files: [{
                    src: ['head.html'],
                    dest: 'build/head.html'
                }]
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-replace');
    grunt.registerTask('default', [
        'uglify',
        'cssmin',
        'replace'
    ]);
};

In your markup instead of referencing the CSS-file normally instead write:

<style>
    @@defaultCss
</style>

Now everytime you run the default Grunt build command (by simply executing ‘grunt’ in your terminal) the placeholder variable ‘@@defaultCss’ in head.html will be replaced with the contents of your default.min.css file (in this case located in build/css/). The output file will be located in build/head.html. Note how I am here using the plugins cssmin and replace together. It’s therefore important to run cssmin BEFORE replace as it is cssmin that will generate the minified CSS output.

Server cache of static files

The Apache configuration

Make it so that static files (javascript, css and images) are cached for a longer duration so that they don’t have be fetched from the server on every new page request. This one is a bit trickier as it requires some server configuration (I am using Apache) and you may not have access to the server depending on what webhost you are using, if any. Regardless, this is my fix.

First you must enable the Apache mod “expires” by running:

a2enmod expires

Now restart your server for the change to take effect.

/etc/init.d/apache2 restart

Now that the expires mod is enabled I can configure my settings for the mod by adding it to my .htaccess file in my site root folder.

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresDefault "access plus 1 month"
    ExpiresByType image/jpg "access 1 year"
    ExpiresByType image/jpeg "access 1 year"
    ExpiresByType image/gif "access 1 year"
    ExpiresByType image/png "access 1 year"
    ExpiresByType text/css "access 1 month"
    ExpiresByType text/x-javascript "access 1 month"
</IfModule>

You can use whatever expiration time you like, learn more about this mod here.

Naturally there’s an obvious problem with this, what if we make changes to static files which we most certainly will if we regularly update the site? There’s a solution to this which once again involves Grunt.

Grunt job for version-tagging static files

Now I will install the Grunt plugins grunt-filerev, grunt-filerev-replace, grunt-contrib-clean and grunt-contrib-copy. Run:

npm install grunt-filerev grunt-filerev-replace grunt-contrib-copy grunt-contrib-clean --save-dev

Now modify your Gruntfile a bit like this:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        },
        cssmin: {
            minify: {
                src: 'css/default.css',
                dest: 'build/default.min.css'
            }
        },
        replace: {
            inline: {
                options: {
                    patterns: [{
                        match: 'defaultCss',
                        replacement: '<%= grunt.file.read("build/default.min.css") %>'
                    }]
                },
                files: [{
                    src: ['build/head.html'],
                    dest: 'build/head.html'
                }]
            }
        },
        filerev: {
            options: {
                algorithm: 'md5',
                length: 8
            },
            images: {
                src: 'build/**/*.{jpg,jpeg,gif,png}'
            },
            js: {
                src: ['build/main.min.js']
            }
        },
        filerev_replace: {
            options: {
                assets_root: './'
            },
            compiled_assets: {
                src: [
                    'build/main.min.js',
                    'build/default.min.css',
                    'build/head.html'
                ]
            }
        },
        copy: {
            main: {
                files: [{
                    src: 'img/*',
                    dest: 'build/',
                    expand: true,
                    flatten: true
                }, {
                    src: 'head.html',
                    dest: 'build/head.html'
                }]
            },
        },
        clean: {
            files: ['build/*']
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-replace');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-filerev');
    grunt.loadNpmTasks('grunt-filerev-replace');
    grunt.loadNpmTasks('grunt-contrib-clean');

    grunt.registerTask('default', [
        'clean',
        'copy',
        'uglify',
        'filerev',
        'cssmin',
        'filerev_replace',
        'replace'
    ]);
};

The grunt plugin “filerev” will generate new files from the files specified, in this scenario image files and our js-file (main.min.js), however an MD5 hash will be generated based on the content of the file and appended to the file name. For instance a file named background.png would become background.86182fa8.png. If the files are changed the generated hash that is appended to the filename will also change. This means that everytime a file, for instance the compiled js-file is changed, it will no longer be cached on the server since the filename is changed and thus interpreted as a new file. By using this we can safely cache static files on the server.

The plugin “filerev_replace” will go through the specified files (main.min.js and default.min.css) and replace all references to the original files (like background.png) and change it to the newly generated hashed files (like background.86182fa8.png).

I also added the plugins clean and copy for cleaning previously generated build files and also copying files to be hashed to the build-folder. Here is a full explanation of the flow of tasks:

  • clean: Remove all previously built files from the build-folder before doing anything else.
  • copy: Copy all images from the img-folder to the build-folder. The reason is that we will later use filerev for the images in the build folder to generate hashed version but we want to let the original images remain. It is the hashed files in the build-folder that will be used, so references to images in css- and js-files should reference for instance build/background.png. The filerev_replace will then modify these references to build/background.86182fa8.png. We also copy the head.html file (this is a file that contains all tags in the head-tag, this file can then be included in the index.html file).
  • uglify: Minify, uglify and compress all js files from the js-folder and compile it into one js-file, build/main.min.js.
  • filerev: Generate hashed versions of all the images in the build folder and our js-file, build/main.min.js.
  • cssmin: Compress and minify css, output will be located in build/default.min.css.
  • filerev_replace:: Replace all references to images in main.min.js and default.min.css. As you can see build/head.html is also included, this is because it includes the reference to main.min.js which will be hashed.
  • replace: Replace the @@defaultCss placeholder variable in build/head.html and replace it with the content of the default.min.css file. This will inline the css. This is the reason we didn’t include css in the filerev task, because the css is inlined in the markup in this step anyway, it will not be a separate file that the server has to request.

This is about the same Grunt configuration I use on my site and I’m pretty happy with it. I managed to improve the performance of the site a lot by setting this up.

Testing server caching

To test that the files specified in the Apache configuration are now cached properly you can download and install the plugin Live HTTP Headers for Chrome or the Firefox version. Start the plugin and enter your site, watch the server file requests in the Live HTTP Headers plugin and you should now be able to see that image-files and js-files now have an Expires-header and a Cache-Control-header.

Asynchronus js-files

The async-attribute is a new addition to HTML5 and it’s an easy solution to load external javascript-files asynchronously. Note that this is not supported by older browsers like (IE8). Here’s how it looks:

<script async src="build/main.min.js"></script>

The end!

I hope you enjoyed this tutorial. There are more things I could mention regarding website performance but I think I covered the essentials, perhaps one day I will write a part 2 to this tutorial. 🙂

Create a customized Javascript code documentation

Introduction

In this tutorial I will cover the basics of how you can generate a Javascript code documentation for your codebase using the Grunt-plugin grunt-jsdoc. It works pretty much like Java-doc. Comments are added to your code that covers information about classes and functions, jsdoc will then generate a full API documentation using the information from these comments that can be navigated through in your browser. I will also cover how you can customize the default template of this JsDoc-webpage to match your desired look. In this tutorial I will use Grunt as my build tool. Also the site I am generating JsDoc for is built using Angular, but obviously you could still use this tutorial for any Javascript codebase. To see an example how this may look please see the JsDoc I generated for my own portfolio website.

Setup

First of all let’s add jsdoc to our dependency list in package.json.

{
    "name": "myPage",
    "version": "1.0.0",
    "repository": {
        "type": "git",
        "url": "https://github.com/me/myRepo"
    },
    "license": "MIT",
    "devDependencies": {
        "angular": "1.3.15",
        "grunt": "^0.4.5",
        "load-grunt-config": "^0.17.2",
        "load-grunt-tasks": "^3.3.0",
        "grunt-jsdoc": "1.1.0"
    }
}

Next add jsdoc as a Grunt build job to your Gruntfile.js.

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js'
                ],
                options: {
                    destination: 'docs',
                    recurse: true
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

Install npm modules:

npm install

Now try running the newly created Gruntjob ‘build-docs’ just to see that you don’t get any errors. The output should be generated into the folder ./docs (as configured in options). The jsdoc should cover all Javascript code in the angular-folder, however since we haven’t added any jsdoc-comments yet the generated jsdoc site should be empty.

grunt build-docs

If you browse to localhost/docs in your browser you now see an empty jsdoc template. Now you are ready to start adding the jsdoc comments to your code.

Jsdoc global definitions

First of all we will ad a file for jsdoc global definitions, references we will use in the comments we add to our js-files. Create a js-file called jsdoc-global-definitions.js to your js-folder. Then add this file to your sources in the jsdoc Grunt task.

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js',
                    'js/jsdoc-global-definitions.js'
                ],
                options: {
                    destination: 'docs',
                    recurse: true
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

In the file jsdoc-global-definitions.js I have the following configuration:

/**
 * AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template language and lets you extend HTML's syntax to express your application's components clearly and succinctly. Angular's data binding and dependency injection eliminate much of the code you would otherwise have to write. And it all happens within the browser, making it an ideal partner with any server technology.
 * @external "Angular"
 * @see {@link https://docs.angularjs.org/guide|Angular documentation}
 */

/**
 * @namespace controllers
 * @description Controllers linked to a specific view.
 */

/**
 * @namespace directives
 * @description Reusable directives that can be used by multiple views.
 */

/**
 * @namespace filters
 * @description Reusable filters that can be used by {@link controllers}, {@link directives} and {@link services}.
 */

/**
 * @namespace services
 * @description Reusable services that can be used by {@link controllers}, {@link directives} and {@link filters}.
 */

As you can see the file contains only comments written with the jsdoc syntax. Here we define global namespaces that we will later use when commenting the javascript codebase. Obviously you will have to modify your global definition file to fit the design pattern and frontend framework you have chosen (if any). Like I said, in this tutorial I am assuming that that the website is Angular-based, which is why I have defined the following namespaces: controllers, directives, filters and services. Each namespace also have a fitting description. We also have one external library dependecy, namely Angular, which is why I also added the first comment that has a link to Angulars developer documentation. This will list Angular as an external library. Also note that in the description you can link to other namespaces by writing {@link namespace}. To read more about jsdoc links please read this.

Adding jsdoc comments to your code

Here is an example of jsdoc comments written in jsdoc syntax added to an Angular controller:

(function() {
    var app = angular.module('myPage');
    /**
     * @constructor MyController
     * @memberof controllers
     * @description Controller for my webpage
     * @param {$scope} $scope - See {@link https://code.angularjs.org/1.2.26/docs/api/ng/type/$rootScope.Scope}
     * @param {services.myService} myService - Services used for cool stuff
     */
    var MyController = ['$scope', 'myService', function($scope, myService) {
        /**
         * @function controllers.MyController#getFibonacciNumberByIndex
         * @description Gets a number in the Fibonacci sequence by index
         * @param {number} The index of the Fibonacci sequence
         * @returns {number} The calculated Fibonacci number
         */
        function getFibonacciNumberByIndex(index) {
            var list = [1, 1];
            for (var i = 0; i <= index; i++) {
                var num1 = list[list.length - 1];
                var num2 = list[list.length - 2];
                var next = num1 + num2;
                list.push(next);
            }
            return list[index];
        }
    }];
    app.controller('MyController', MyController);
}());

As you can see in the controller MyController the function MyController is treated as the constructor, therefore the @constructor comment. I have also added the comment @memberof controllers which is referring to the namespace “controllers” that we defined earlier. MyController has two dependencies, $scope and myService, therefore these are documented as @param. Since $scope is a built-in Angular module I add a link to the documentation of $scope using the {@link} syntax. The other dependency is to to an Angular service in OUR codebase, therefore it is referenced to as services.myService (services is the namespace), this will generate a link to the documenation of myService.

The controller contains one function that returns a Fibonacci number by give index. For this function we define it using @function controllers.MyController#getFibonacciNumberByIndex. The syntax goes: namespace.fileName#functionName. The function takes in one parameter of the expected type {number} and returns a response also of the type {number}. To learn more about other parameter/return types read this.

Now generate your jsdoc by yet again running

grunt build-docs

and then check the generated jsdoc in localhost/docs. You should now see a menu to the right with external sources (Angular), Internal sources (MyService) and defined namespaces (controllers, directives, filters and services). If you click on MyController you should be able to see detailed description of the constructor and the function getFibonacciNumberByIndex.

Custom startpage

Right now the startpage of your jsdoc documentation should be blank, but you can add a custom template by creating a file named jsdoc.md and add a reference to it in your Grunt task:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js',
                    'js/jsdoc-global-definitions.js',
                    'js/jsdoc.md'
                ],
                options: {
                    destination: 'docs',
                    recurse: true
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

The jsdoc.md file should be written using Markdown syntax, same syntax used for Github readme-files. To learn more about it read here.

For an example, see how my file looks.

Custom stylings

If you see my jsdoc for my portfolio website you’ll see that it is a bit more colorful than the regular grey template. If you also wan’t your jsdoc page to have a more fabolous look follow these steps.

After you installed the grunt-jsdoc module using npm you should then be able to find the following folder: /node_modules/jsdoc/templates/default.

Copy this folder to your repository root folder:

sudo cp -rf /myRepo/node_modules/jsdoc/templates/default /myRepo/myTemplate

Now add the following setting to your Gruntfile.js:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js',
                    'js/jsdoc-global-definitions.js',
                    'js/jsdoc.md'
                ],
                options: {
                    destination: 'docs',
                    recurse: true,
                    template: './myTemplate'
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

Jsdoc will now instead of the default template in node_modules use template found in the myTemplate directory. Now you can modify this template as much as you want, this template folder includes css, scripts and markup that can be modified as you please.

That’s it! I hope you enjoyed this tutorial 🙂 🙂

Create an image/video gallery using Angular and Photoswipe

Introduction

In this tutorial I will cover how you can create an art gallery for your website using Angular and Photoswipe. Angular is used for the presentation logic, we will handle a list of items as a scope-variable and an Angular controller for fetching the list, perhaps cleaning the data and so on. Photoswipe is the library used in this tutorial the “gallery functionality”, which includes displaying an image in fullscreen, swiping left and right and so on. The reason why I chose Photoswipe is because it is easy to use, very customizable and also works very well on mobile devices. You can read more about Photoswipe here.

To see a demo of how a gallery like this can look, please view my art gallery that I build using these technologies.

Displaying videos

When I first started working on this I had an idea that I also wanted to be able to show videos in the gallery, not just images. If an item in the gallery was a video a play-icon should be displayed over the thumbnail to indicate it is a video, and if opened the video should automatically start playing and be stopped when closing or swiping to next item. Unfortunately I found out that Photoswipe has no video support, it is just for images. However I managed to find a solution to this that I am happy with. In this tutorial I will cover what I did.

Setup Photoswipe

First of all, a default Photoswipe template must be added to your site. This template contains the markup used for the gallery interface. It should just be copypasgted, do not change classes and id’s in this template as it will mess up Photoswipe.

Here is how your index-file could look (in this case I am using php for the include function):

<!DOCTYPE html>
<html xmlns="http:/www.w3.org/1999/xhtml" lang="en">
    <head>
        <link rel="stylesheet" href="css/myCss.css">
        <title>My cool gallery</title>
    </head>
    <body ng-app="myApp" ng-controller="galleryController">
        <?php include("photoswipe.php"); ?>
    </body>
</html>

This is the contents of the file photoswipe.php:

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
    <!-- Background of PhotoSwipe.          It's a separate element as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
        <!-- Container that holds slides.             PhotoSwipe keeps only 3 of them in the DOM to save memory.             Don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
                <!--  Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
                <button class="pswp__button pswp__button--share" title="Share"></button>
                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>
            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>

 

Like I said, this is Photoswipes default template markup, I have not created this.

Now, as you can read in the Photoswipe getting started tutorial you should add references to the files photoswipe.css, default-skin.css, photoswipe.js and photoswipe-ui-default.min.js.

<!DOCTYPE html>
<html xmlns="http:/www.w3.org/1999/xhtml" lang="en">
    <head>
        <link rel="stylesheet" href="css/myCss.css">
        <link rel="stylesheet" href="css/photoswipe.css">
        <link rel="stylesheet" href="css/default-skin.css">
        <script type="text/javascript" src="js/photoswipe.js"></script>
        <script type="text/javascript" src="js/photoswipe-ui-default.min.js"></script>
        <title>My cool gallery</title>
    </head>
    <body ng-app="myApp" ng-controller="galleryController">
        <?php include("photoswipe.php"); ?>
    </body>
</html>

Photoswipe should now be setup and ready to use!

The controller

Time to get going! I will start by working on the Angular controller and the corresponding view. Here’s how the controller that we will use will look like:

(function() {
    var app = angular.module('myApp');

    var galleryController = ['$scope', function($scope) {

        // Public variables
        $scope.loading = true; // Set loading to false when initialization is done
        // The list of items to use
        $scope.list = [{
            thumbnail: 'thumbnail_for_item_1.png',
            bigImage: 'image_1.png',
            width: 640,
            height: 640,
            index: 0
        }, {
            thumbnail: 'thumbnail_for_item_2.png',
            bigImage: 'image_2.png',
            width: 640,
            height: 640,
            index: 1
        }, {
            thumbnail: 'thumbnail_for_image_1.png',
            video: 'video.mp4',
            width: 640,
            height: 640,
            index: 2
        }];
        // Private variables
        // The list that Photoswipe will use
        var itemsForPhotoswipe = [];

        // Public methods
        $scope.openPhotoSwipe = openPhotoSwipe;

        function openPhotoSwipe(artItem) {
            var pswpElement = document.querySelectorAll('.pswp')[0];

            // define options (if needed)
            var options = {
                // history & focus options are disabled on CodePen
                history: false,
                focus: false,
                index: artItem.index,

                showAnimationDuration: 0,
                hideAnimationDuration: 0
            };

            var gallery = new PhotoSwipe(pswpElement, 
                                         PhotoSwipeUI_Default,
                                         itemsForPhotoswipe,
                                         options);
            gallery.init();
        }

        function init() {
            for (var j = 0; j < $scope.list.length; j++) {
                if ($scope.list[j].bigImage) {
                    itemsForPhotoswipe.push({
                        src: $scope.list[j].bigImage,
                        w: $scope.list[j].width,
                        h: $scope.list[j].height,
                        index: $scope.list[j].index
                    });
                } else if ($scope.list[j].video) {
                    itemsForPhotoswipe.push({
                        html: '<div class="videoSlide">' +
                              '<div class="videoSlideInner">' +
                              '<video width="100%" height="100%" id="videoPlayer' +
                              $scope.list[j].index + '" controls><source src="' +
                              $scope.list[j].video.url + '" type="video/mp4" />' +
                              '</video>' +
                              '</div>' +
                              '</div>',
                        index: $scope.list[j].index
                    });
                }
            }

            $scope.loading = false;
        }

        init();
    }];

    app.controller('galleryController', galleryController);
}());

As you can see in this example I have a hardcoded list called “list” that contains all the items to be displayed. You could also choose to load a list from a JSON-file or something if you like. As you can see items can either have a “bigImage” property if it’s an image or a “video” property if it’s a video.

In the init-function I loop through this list and create the list, itemsForPhotoswipe, that Photoswipe will use. This is because Photoswipe want’s the input list to have a specific format for each item. Properties like src, w, h, index and html are what Photoswipe expects.

The html-property allows the user to have a custom html template for displaying the current item. As you can see in the controller above I am using this to have a customt template for video items. With this a HTML5 video element will be shown for videos. Here is the CSS I used for the videoSlide container:

.videoSlide {
    width: 100%;
    height: 100%;
    text-align: center;
    display: table;
}
.videoSlide .videoSlideInner {
    height: 100%;
    display: table-cell;
    vertical-align: middle;
}
.videoSlide .videoSlideInner video {
    max-width: 100%;
}

This will center the video vertically and horizontally and make sure it never stretches outside the screen. Add this to your css-file.

The function openPhotoSwipe is the function to be called when clicking on a thumbnail in the gallery. The function takes the entire item object as inparamter and then initializes Photoswipe.

The view

Now it’s time for the view. It should look a little bit like this:

<div ng-show="loading">
    <h1>CONTENT IS LOADING, PLEASE WAIT</h1>
</div>
<div data-ng-show="!loading">
    <img class="galleryImg"
    src="{{ item.video ? 'img/play_icon.png' : '' }}"
    width="150"
    height="150"
    style="background-image: url({{item.thumbnail}});"
    data-ng-click="openPhotoSwipe(item)"
    data-ng-repeat="item in list"/>
</div>

This will generate a wall of thumbnail tiles for items in our list scope-variable. I use an img-element for this because I wan’t to be able to set a src-attribute for a play icon overlaying the thumbnail for video items. The video items will still have the thumbnail beneath the play icon. To see an example of this just view my gallery.

Almost done, minor tweaks next

Like I mentioned before, Photoswipe doesn’t really support videos right off the bat. We have made sure that video items now will we displayed with our custom template but we need to add some presentation logic for playing/pausing the video.

To do this I modified the file photoswipe.js as it was the simplest way to solve this. You can download my modified file from my Github repository here: https://github.com/ToWelie89/martinsweb/blob/master/js/libs/photoswipe/photoswipe.js

To see exactly what I did to the file you can analyze this commit: https://github.com/ToWelie89/martinsweb/commit/ff5f68448da553fd4288bb12985c9ea0c46a5c75#diff-b82b7d7b1784a22c00d03fa5d457ed51

The functions stopVideo and startVideo handles playback and pausing. As you can see identifiers such as videoPlayer + index is used to find the specific video element by matching with the current items index value. That is why I set id of the video element to videoPlayer + $scope.list[j].index in the controller when looping through the list.

With these additional changes Photoswipe will now automatically start a video when clicking on a video item, stopping when closing the item or swiping to the next.

Thank you for reading this tutorial, I hope you liked it 🙂