Angular Project Blackjack: 13 – External Module, Keyboard

Keyboard support has been requested MULTIPLE TIMES from my friend @mhmazur during the development process. I figured it was finally time to fill his feature request.

The Search

Since I’ve never done any sort of keyboard support in angular, I set out to see how everyone else is doing it. The first thing I came across was the ngKeyup directive. This would do what I needed. It allows you to watch for keyboard presses and react to them.

I set about implementing it by adding the directive to game.directive.html. Unfortunately, while it worked the way we would want it, there was one major issue, it required a click. Yes, having the app loaded in the browser, when you start typing, ngKeyup does not register. The directive that we put keyup on must be selected. I imagine because ngKeyup was meant for more input type elements. On to the next plan of attack.

The Search Continues

One of the big external modules I came across was Angular Hotkeys. Diving into the source code I find that it utilizes Mousetrap, a popular js library for keyboard handling. At this point I was considering two options: write Mousetrap functionality into my app or just use Angular Hotkeys. I went with option two, knowing that if things didn’t work I could fall back to Mousetrap.

Bringing In the New

Up until now, all of our angular modules were of our own creation. We didn’t have any other real dependencies. Let’s change that now! First things first, install the package via bower:

$ bower install chieffancypants/angular-hotkeys --save

Next, we’ll require the lib as a dependency in our blackjack.module.js file:

angular.module('blackjack', [
    'blackjack.card',
    'blackjack.game',
    'blackjack.player',
    'blackjack.dealer',
    'cfp.hotkeys'
]);

Now that we have our dependencies defined, we can inject hotkeys into any controller we need it. That controller for us is game.controller.js. As per the documentation, we setup the hot keys to do the functions that our game buttons do:

game.setupHotKeys = function(){
    hotkeys.add({
        combo: 's',
        description: 'Press to Stay',
        callback: function() {
            if(game.buttonStayEnabled){
                game.end();
            }
        }
    });

    hotkeys.add({
        combo: 'h',
        description: 'Press to Hit',
        callback: function() {
            if(game.buttonHitEnabled){
                game.hit(true);
            }
        }
    });

    hotkeys.add({
        combo: 'b',
        description: 'Press to Bet',
        callback: function() {
            if(game.buttonBetEnabled){
                game.deal();
            }
        }
    });
};

One thing to notice is that we’re checking the button enabled status when the hot keys are pressed. We could probably do some double checking within the functions as well (but I haven’t yet).

Finishing Up

If you run the code now, you will see the hotkeys as being enabled! One thing that you will see is that our unit tests are now failing. We need to tell them to inject the hotkeys service in order to run. This is fairly trivial, but it will allow us to do complex things in the future like mocking hot keys.
game.controller.spec.js:

var gameController, CardService, PlayerService, DealerService, hotkeys;
beforeEach(function () {
    module('blackjack.game');
    module('blackjack.player');
    module('blackjack.card');
    module('blackjack.dealer');
    module('cfp.hotkeys');
    inject(function ($controller, _CardService_, _PlayerService_, _DealerService_, _hotkeys_) {
        CardService = _CardService_;
        PlayerService = _PlayerService_;
        DealerService = _DealerService_;
        hotkeys = _hotkeys_;
        gameController = $controller('GameController', {
            CardService: CardService,
            PlayerService: PlayerService,
            DealerService: DealerService,
            hotkeys: hotkeys
        });
    });
});

Hot keys should now be enabled on our live site if you’d like to try them out!

Code for this post is available here.

Feel free to follow me/ask questions on twitter, @AdamWeeks

Angular Project Blackjack: 12 – Github Pages Deploy

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Release-the-kraken-template-500js031710

I’m not going to lie, I thought this was going to be a GREAT post! So much information about how to deploy our site to github pages and make it available for the world. The good news is, we did all the hard work in our previous post. The bad news is, this will probably be the shortest post and commit in this series.

Github Pages

Github gives us the ability to host our projects on their servers via github pages. Basically, whatever we put in a gh-pages branch, will go into a publicly hosted page.

https://help.github.com/articles/creating-project-pages-manually/

Gulp

Ok, so how do we get our project into that gh-pages branch? Enter, our good friend, gulp. Sure, we could build our files, checkout the branch and manually commit our build folder to the branch, and push to github. OR, we can make use of someone else’s hard work! We can use this tool as a plugin to our gulp tasks.

npm install gulp-gh-pages --save-dev

Then, simply pipe the result of our build to the plugin and it will do the rest! We just need to add these lines to gulpfile.js.

var ghPages     = require('gulp-gh-pages');

gulp.task('deploy', ['build'], function(){
    return gulp.src('./build/**/*')
        .pipe(ghPages());
});

Now we can just run gulp deploy and our site is live! Thanks to our hard work in creating the build task, we are all ready to go.

Running the code gives us a deployed version of our blackjack game! If you haven’t been following along and running every blog post locally, you can now play the game here!

The code for everything up to this post is available here..

Angular Project Blackjack: 11 – User Experience

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Foreward

The coding portion of this post was BY FAR the most amount of time and effort I’ve put into any of them so far. My CSS skills were very rusty, so I had to do a lot of research and back and forth to get things right. More advanced front-end people may find some issues with my code, but it is, as always, a work in progress.

Experience

While our blackjack game is technically correct, it has all the glimmer of a game written for the TI-82. Don’t get me wrong, I love bootstrap, but for a game, it just isn’t enough.

New Visual Style

Here is what our game currently looks like:
Screenshot 2015-04-12 21.58.11

If you walked into a casino and had to play blackjack this way, you’d definitely find another casino quickly. Luckily for us, we’ve already setup most of our game objects as directives (game, hand, card) so we can go through and layout each item. Since most of this visual styling doesn’t have much to do with angular, I’ll just share some helpful links:

Putting all those together, we get a much nicer looking game area:
Screenshot 2015-04-12 21.59.31

Animations

The bad thing about angular is how fast it is. Well, let me clarify. It is tough to generate any suspense when the cards are dealt immediately to all players. The solution for suspense comes in the form of $timeout. Let’s use our ‘card back’ state in the card directive to hide the card’s value:

<div class="card back" ng-class="vm.cardIndexClass" ng-if="vm.card.hideValue"></div>

Now, when we deal the card to the player, we’ll pause for a second before revealing the card:

game.dealCardToPlayer = function(card, animate, callback){
    if(animate) {
        card.hideValue = true;
        game.playerCards.push(card);
        $timeout(function () {
            card.hideValue = false;
            callback();
        }, 250);
    }
    else{
        game.playerCards.push(card);
        callback();
    }
};

We also want to give the same suspense after we finish our hand and have the dealer complete, but since we are looping our commands through, it is a bit more complicated:

dealer.finish = function (callback) {

    var loop = {
        next: function(){
            dealer.getHandValue();
            if(dealer.handValue < dealer.minValue) {
                //Animate Card In
                dealer.hit(false, true, function () {
                    loop.next();
                });
            }
            else{
                loop.done();
            }
        },
        done: function(){
            if(dealer.handValue > dealer.maxValue){
                dealer.busted = true;
            }
            dealer.isDone = true;
            callback();
        }
    };

    //Reveal first card, then play:
    $timeout(function(){
        dealer.cards.forEach(function(card){
            card.hideValue = false;
        });
        loop.next();
    },500);

};

Now we’ve built in some timeouts to generate some player anxiety!

Our new user experience

Our new user experience

Testing $timeout

Now that we are using the $timeout service, when we run our tests, we don’t really want to have to wait like a regular user would. With the help of the angular team, we don’t have to! angular-mocks comes with a custom $timeout with a function called .flush(). This will clear anything in the $timeout queue and immediately kick it off. Here is how I’m testing our dealer function:

it('should set isDone value after finishing', inject(function ($timeout) {
    dealer.deal();
    expect(dealer.isDone).toBe(false);
    spyOn(dealer,'getHandValue').andCallFake(function(){
        dealer.handValue = 23;
    });
    dealer.finish(function(){

    });
    $timeout.flush();
    expect(dealer.isDone).toBe(true);
}));

By injecting $timeout, it makes it available to the test spec. You can see we’re doing dealer.finish() then immediately calling $timeout.flush() to not have to wait for the timeout to finish before checking our expectations.

Finally

Now we’ve got it looking pretty, the next step is to actually deploy!

You can checkout our code to this point here.

Angular Project Blackjack: 10 – The Dealer

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

In order to make the game fun and realistic, we need an opponent to play against. In any given casino around the world, this opponent is the dealer. This dealer operates on a very limited set of specific rules, which makes writing a virtual version of him easy!

Dealer Service

The first thing we are going to do is create a blackjack.dealer module that gives us a DealerService. This service will give us a dealer object that we can use in the game. During each hand, the dealer gets two cards and then waits for everyone to finish. Once they are finish, the dealer plays his hand, stopping once he gets above 16 or busts. This matches up to our service, we have two main functions dealer.deal() and dealer.finish(). Sadly, we’re not doing anything too mind breaking with this service, so I’ll just post it here:

(function() {
    'use strict';

    angular
        .module('blackjack.dealer')
        .factory('DealerService', DealerService);

    DealerService.$inject = ['GameService'];

    function DealerService(GameService) {
        var service = {
            newDealer: newDealer,
            Dealer: Dealer
        };

        function newDealer(deck) {
            var dealer = new Dealer(deck);
            return dealer;
        }

        function Dealer(deck) {
            var dealer = this;
            dealer.deck = deck;

            /**
             * Creates initial values for dealer object
             */
            dealer.init = function() {
                dealer.cards = [];
                dealer.handValue = 0;
                dealer.isDone = false;
                dealer.busted = false;
                dealer.maxValue = GameService.maxValue();
                dealer.minValue = 17;
            };

            /**
             * When a new game is started, dealer gets two cards
             */
            dealer.deal = function() {
                dealer.init();
                dealer.hit();
                dealer.hit();
            };

            /**
             * After player has completed game, tell dealer to finish
             * by dealing out until hand is busted or between 17-21
             */
            dealer.finish = function() {
                while (dealer.handValue & lt; dealer.minValue) {
                    dealer.hit();
                }
                if (dealer.handValue & gt; dealer.maxValue) {
                    dealer.busted = true;
                }
                dealer.isDone = true;

            };

            /**
             * Deals a card to the dealer's hand
             */
            dealer.hit = function() {
                dealer.cards.push(dealer.deck.deal());
                dealer.getHandValue();
            };

            /**
             * Uses game service to calculate hand value
             */
            dealer.getHandValue = function() {
                dealer.handValue = GameService.handValue(dealer.cards);
            };

            dealer.init();

        }

        return service;

        ////////////////

    }

})();

Implementation

In our GameController, we will tell the dealer when to do his thing. During game.deal() we’ll deal cards to our player as well as telling the dealer to game.dealer.deal(). Once a player ends their game (either by pressing ‘stay’ or busting), we’ll tell the dealer to finish via game.dealer.finish(). Then, we’ll do some hand comparisons to see how we shape up against the dealer and set our game results properly. Here is our game.controller.js file:

(function() {
    'use strict';

    angular
        .module('blackjack.game')
        .controller('GameController', GameController);

    GameController.$inject = ['PlayerService', 'CardService', 'GameService', 'DealerService'];

    function GameController(PlayerService, CardService, GameService, DealerService) {
        var game = this;

        /**
         * Our game can have multiple states:
         * 1) Game with no player (started = false, canDeal = false, showResults = false)
         * 2) Game with a player, nothing dealt (started = true, canDeal = true, showResults = false)
         * 3) Game with a player, game in progress (started = true, canDeal = false, showResults = false)
         * 4) Game with a player, game over (started = true, canDeal = true, showResults = true)
         */

        /**
         * Initialize our controller data
         */
        game.init = function() {
            game.maxValue = GameService.maxValue();
            game.canDeal = false;
            game.started = false;
            game.showResults = false;
            game.deck = CardService.newDeck();
            game.dealer = DealerService.newDealer(game.deck);
            game.betValue = 100;
            game.playerCards = [];
            game.handValue = 0;
        };

        /**
         * Starts a game by creating a new player
         */
        game.start = function() {
            game.player = PlayerService.newPlayer('Ringo', 100);
            game.started = true;
            game.canDeal = true;
            game.showResults = false;
        };

        /**
         * Deals a new hand by 'paying' from the score,
         * shuffles the deck, and deals two cards to the
         * player.
         */
        game.deal = function() {
            //Initialize values each game
            game.busted = false;
            game.started = true;
            game.canDeal = false;
            game.showResults = false;

            //Our bet defaults to 100
            game.player.changeScore(game.betValue * -1);

            //Shuffle before dealing
            game.deck.shuffle();

            //Empty our dealt card array
            game.playerCards = [];

            //Deal the cards
            game.hit();
            game.hit();

            //Deal to the dealer
            game.dealer.deal();
        };

        /**
         * Adds a card to our hand and calculates value.
         */
        game.hit = function() {
            game.playerCards.push(game.deck.deal());
            game.getHandValue();
        };

        /**
         * Ends the game for the current hand. Checks for wins
         * and 'pays' to player score
         */
        game.end = function() {
            //Tell the dealer to finish his hand
            game.dealer.finish();

            if (game.busted) {
                game.results = "BUSTED";
            } else {
                var wonGame = false;
                var tiedGame = false;
                //Check against dealer's hand
                if (game.dealer.busted) {
                    //Auto Win if dealer busts
                    wonGame = true;
                } else {
                    if (game.dealer.handValue === game.handValue) {
                        tiedGame = true;
                    } else {
                        wonGame = (game.handValue & gt; game.dealer.handValue);
                    }
                }

                if (wonGame) {
                    //Winning pays double the bet
                    game.player.changeScore(game.betValue * 2);
                    game.results = "YOU WON!";
                } else if (tiedGame) {
                    //A 'PUSH' gives the player back their bet
                    game.player.changeScore(game.betValue);
                } else {
                    game.results = "DEALER WON";
                }
            }

            game.canHit = false;
            game.canDeal = true;
            game.showResults = true;
        };

        /**
         * Resets our player's score and re-inits
         */
        game.reset = function() {
            game.player = null;
            game.init();
        };

        /**
         * Calculates value of player's hand via GameService
         * Determines if player can still hit or if busted.
         */
        game.getHandValue = function() {
            game.handValue = GameService.handValue(game.playerCards);
            game.canHit = game.handValue & lt;
            game.maxValue;
            game.busted = game.handValue & gt;
            game.maxValue;
            if (game.handValue & gt; = game.maxValue) {
                game.end();
            }
        };

        game.init();
    }
})();

Display

We’ve also changed our display a bit to show our dealer’s hand (only one card before the player is done). We are also cleaning things up a bit with our hand value display and using ng-if to only display when the game has been started. Here is what we are doing with game.directive.html:

<div>

    <!-- Scoreboard -->
    <div class="panel panel-default">
        <div class="panel-heading">Player Info</div>
        <div class="panel-body">
            <div class="row">
                <div class="col-md-6">Player: {{game.player.name}}</div>
                <div class="col-md-6">Score: {{game.player.score}}</div>
            </div>
        </div>
    </div>
    <!-- Card Table -->
    <div class="panel panel-default">
        <div class="panel-heading">
            <div>Game In Progress</div>
            <div>Press "Start" to play</div>
        </div>
        <div class="panel-body">
            <div class="row">
                <div>

                    Dealer Hand: {{game.dealer.handValue}}

                </div>
                <div>

                    Dealer

                </div>
                <div class="well well-lg">
                    <div>
                        <div class="card">{{game.dealer.cards[0].name()}}</div>
                        <div class="card">******</div>
                    </div>
                    <div class="card">{{card.name()}}</div>
                </div>
            </div>
            <div class="row">
                <div>

                    Player Hand: {{game.handValue}}

                </div>
                <div>

                    Player

                </div>
                <div class="well well-lg">
                    <div class="card row">{{card.name()}}</div>
                </div>
            </div>
        </div>
    </div>
    <!-- Game Actions -->
    <div class="panel panel-default">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <div class="btn-group btn-group-justified">
                    <div class="btn-group">
                        <button class="btn btn-primary">Deal</button>
                    </div>
                    <div class="btn-group">
                        <button class="btn btn-warning">HIT</button>
                    </div>
                    <div class="btn-group">
                        <button class="btn btn-danger">STAY</button>
                    </div>
                </div>
            </div>
        </div>
        <div class="row">
            <button class="btn btn-primary">Start</button>
        </div>
    </div>
    <!-- Game Results -->
    <div class="panel panel-default">
        <div class="panel-heading">Results</div>
        <div class="panel-body">{{game.results}}</div>
    </div>
    <button class="btn btn-danger btn-sm">Reset Game</button>

</div>

Let’s Play

We’ve now got a playable blackjack game that would match any casino experience! Well, without all of the actually winning and losing of the money and the free booze and all. But, close enough, right?

For our next post, let’s ramp up the user experience some by making the cards look like actual cards. Let’s also put some drama in with some css animations and time delays using $timeout.

Angular Project Blackjack: 9 – Game Service

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Back to it

We’ve got a good setup now, with our gulp file building and running as necessary. Let’s move back to working on our code!

A Game Service

In our GameController we have the ability to deal a hand of cards but we can’t really do anything with it yet. To do so, we need to start putting the blackjack game rules into our application. Let’s create a GameService to start handling these rules for us. The main thing our GameService will do for now will be to take a hand of cards and return the numeric value of that hand. We can then use that value in our controller to enable/disable actions a user can take on the hand.

Here is our GameService:

(function(){
    'use strict';

    angular
        .module('blackjack.game')
        .factory('GameService', GameService);

    function GameService(){
        var service = {
            handValue: handValue
        };

        return service;

        ////////////////////


        function handValue(hand){
            var aces = 0;
            var totalValue = 0;
            var faceRanks = ['J','Q','K'];

            //Get the values of each card (counting 1 for each ace)
            hand.forEach(function(card){
                //Face Cards
                if(faceRanks.indexOf(card.rank) !== -1){
                    totalValue += 10;
                }
                //Aces
                else if(card.rank === 'A'){
                    totalValue += 1;
                    aces++;
                }
                //Number Cards
                else {
                    totalValue += Number(card.rank);
                }
            });

            //Loop through aces and try to add 10 to get highest value of hand
            // We are adding 10 here because we already added 1 for the ace above
            for(var i=0; i<aces; i++){
                if(totalValue <= 11){
                    totalValue += 10;
                }
            }

            return totalValue;
        }

    }
})();

Playing the Game

As I started writing the game playing portion of the GameController, a conversation I had about state based programming with @JoelMartinez kept popping into my head. When I broke it down into basics, our controller could be in one of four basic states:

  • Game with no player (started = false, canDeal = false, showResults = false)
  • Game with a player, nothing dealt (started = true, canDeal = true, showResults = false)
  • Game with a player, game in progress (started = true, canDeal = false, showResults = false)
  • Game with a player, game over (started = true, canDeal = true, showResults = true)

Breaking it down this way, you can see what your controller variables are and what they should be during each state. You can also easily see what you need to do to change states. Let’s update our GameController to handle these states:

(function(){
    'use strict';

    angular
        .module('blackjack.game')
        .controller('GameController',GameController);

    GameController.$inject = ['PlayerService', 'CardService', 'GameService'];

    function GameController(PlayerService, CardService, GameService){
        var game = this;

        /**
         * Our game can have multiple states:
         * 1) Game with no player (started = false, canDeal = false, showResults = false)
         * 2) Game with a player, nothing dealt (started = true, canDeal = true, showResults = false)
         * 3) Game with a player, game in progress (started = true, canDeal = false, showResults = false)
         * 4) Game with a player, game over (started = true, canDeal = true, showResults = true)
         */

        /**
         * Initialize our controller data
         */
        game.init = function () {
            game.maxValue = 21;
            game.canDeal = false;
            game.started = false;
            game.showResults = false;
            game.deck = CardService.newDeck();
        };

        /**
         * Starts a game by creating a new player
         */
        game.start = function () {
            game.player = PlayerService.newPlayer('Ringo', 100);
            game.started = true;
            game.canDeal = true;
            game.showResults = false;
        };

        /**
         * Deals a new hand by 'paying' from the score,
         * shuffles the deck, and deals two cards to the
         * player.
         */
        game.deal = function () {
            //Initialize values each game
            game.busted = false;
            game.started = true;
            game.canDeal = false;
            game.showResults = false;

            //Our bet defaults to 100
            game.player.changeScore(-100);

            //Shuffle before dealing
            game.deck.shuffle();

            //Empty our dealt card array
            game.playerCards = [];

            //Deal the cards
            game.hit();
            game.hit();

            //Calculate value of hand
            game.getHandValue();
        };

        /**
         * Adds a card to our hand and calculates value.
         */
        game.hit = function () {
            game.playerCards.push(game.deck.deal());
            game.getHandValue();
        };

        /**
         * Ends the game for the current hand. Checks for wins
         * and 'pays' to player score
         */
        game.end = function () {
            //Since we have no dealer, we win if we don't bust
            if(!game.busted) {
                game.player.changeScore(200);
                game.results = "YOU WON!";
            }
            else{
                game.results = "BUSTED";
            }
            game.canHit = false;
            game.canDeal = true;
            game.showResults = true;
        };

        /**
         * Resets our player's score and re-inits
         */
        game.reset = function () {
            game.player = null;
            game.init();
        };

        /**
         * Calculates value of player's hand via GameService
         * Determines if player can still hit or if busted.
         */
        game.getHandValue = function () {
            game.handValue = GameService.handValue(game.playerCards);
            game.canHit = game.handValue < game.maxValue;
            game.busted = game.handValue > game.maxValue;
            if(game.handValue >= game.maxValue){
                game.end();
            }
        };

        game.init();
    }
})();

As you can see, our first state is when the controller is called via init(). We move to the next state with the start() function. Once we deal() a hand, we are in the third state and we continue that way until end() is called. Our controller is now using our CardService to deal cards and our GameService to play the game. This really keeps our controller neat and makes our services reusable and testable. (Side note: I had a lot of internal debate on keeping the canHit and busted calculations insdie the GameController. In the end, I’ve left it there, but don’t be surprised if in another post or commit you see them moved into the GameService.)

Testing Injections

If you run our tests now, you will see a failure for a provider not found. This is due to the fact that our controller needs the GameService and CardService to function. Let’s go into our game.controller.spec.js file and inject those services (as well as write a few more tests).
Here is the top of our test:

describe('GameController Unit Tests', function () {
    var gameController, CardService, PlayerService;
    beforeEach(function () {
        module('blackjack.game');
        module('blackjack.player');
        module('blackjack.card');
        inject(function ($controller, _CardService_, _PlayerService_) {
            CardService = _CardService_;
            PlayerService = _PlayerService_;
            gameController = $controller('GameController', {
                CardService: CardService,
                PlayerService: PlayerService
            });
        });
    });

The biggest change you’ll see here is the inject(function ($controller, _CardService_, _PlayerService_) { line. We didn’t make a typo by putting underscores before and after our service names, this is a testing feature in AngularJS that allows us to get a localized copy of each service it is injecting into the test.

The line up top: var gameController, CardService, PlayerService;is what sets up our local variables for the instances. Now we can use those variables to allow us to do fun things like watch and mock functions of those services. For now, we’ll wait on writing tests with those advanced features and save it for another blog post.

Updating the Display

The last thing we need to do is to update our game directive view with all of our new GameController logic. We’re still using Bootstrap for our layout and we’ll divide our game display into four separate Bootstrap Panels:

  • Scoreboard
  • Card Table
  • Game Actions
  • Results

With the use of our state programming, we can hide or show each panel as needed with an ng-if statement on each panel. We are also using ng-disabled to control our action buttons based on state. Here is our game.directive.html:

<div>
    <!-- Scoreboard -->
    <div class="panel panel-default" ng-if="game.started">
        <div class="panel-heading">Player Info</div>
        <div class="panel-body">
            <div class="row">
                <div class="col-md-6">
                    Player: {{game.player.name}}
                </div>
                <div class="col-md-6">
                    Score: {{game.player.score}}
                </div>
            </div>
        </div>
    </div>

    <!-- Card Table -->
    <div class="panel panel-default">
        <div class="panel-heading">
            <div ng-if="game.started">
                Game In Progress
            </div>
            <div ng-if="!game.started">
                Press "Start" to play
            </div>
        </div>
        <div class="panel-body">
            <div class="row">
                <div class="col-md-12">
                    <div class="card" ng-repeat="card in game.playerCards">{{card.name()}}</div>
                </div>
            </div>
        </div>
        <div class="panel-footer" ng-if="game.started">
            <p>Hand Value: {{game.handValue}}</p>
        </div>
    </div>

    <!-- Game Actions -->
    <div class="panel panel-default">
        <div class="row" ng-if="game.started">
            <div class="col-md-6 col-md-offset-3">
                <div class="btn-group btn-group-justified">
                    <div class="btn-group" role="group">
                        <button class="btn btn-primary" ng-click="game.deal()" ng-disabled="!game.canDeal">Deal</button>
                    </div>
                    <div class="btn-group" role="group">
                        <button class="btn btn-warning" ng-click="game.hit()" ng-disabled="!game.canHit">HIT</button>
                    </div>
                    <div class="btn-group" role="group">
                        <button class="btn btn-danger" ng-click="game.end()" ng-disabled="!game.canHit">STAY</button>
                    </div>
                </div>
            </div>
        </div>
        <div class="row" ng-if="!game.started">
            <button class="btn btn-primary" ng-click="game.start()">Start</button>
        </div>
    </div>

    <!-- Game Results -->
    <div class="panel panel-default" ng-if="game.showResults">
        <div class="panel-heading">Results</div>
        <div class="panel-body">
            {{game.results}}
        </div>
    </div>

    <button class="btn btn-danger btn-sm" ng-click="game.reset()">Reset Game</button>

</div>

Wrapping Up

We’ve now got our GameService and GameController working properly and displaying correctly. You should now be able to run gulp buildDev and then http-server and play a game or two of blackjack! We’ve also added the concept of ‘losing’ a game (by busting), so bust a few hands and watch your points dwindle. You can ‘game’ the system though, but simply ‘STAY’-ing after dealing your initial hand. We need an opponent to keep us honest, and in casinos around the world, that opponent is the Dealer!

Up Next: The Dealer

Angular Project Blackjack: 8 – Gulp Tasks

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Now that we have gulp setup and running and merging our files via gulp-concat, we don’t need to stop there. Let’s go ahead and make some more tasks to run and make our lives easier.

Testing

Since our unit tests are ready to be run from the command line via karma, we can add them as a gulp task. Karma is actually built into gulp, so there are no extra packages to install! In our gulpfile.js, let’s create a karma variable and a task to run our tests:

var karma = require('karma').server;

gulp.task('test', function (done) {
    karma.start({
        configFile: __dirname + '/karma.config.js',
        singleRun: true
    }, done);
});

We’re telling gulp to run our tests once and where to locate our karma configuration. Since gulp is powered by node, we can use the global variable __dirname to get the path that gulp is running from. You should now be able to run gulp test and watch our tests run!

Building

If you’ve completed the previous post and done any sort of debugging on it,
one thing you may have noticed is that debugging a concatenated app.js is very
confusing. We’ve optimized our project for our end users but we’ve made life difficult
for ourselves. This is where the whole “source” vs “release” builds comes in. What
we can do is have a separate folder that our production version will go into.
There are a lot of names used for these production folders: prod, release, deploy,
but we will use build in our project.

The first thing we’ll do to prepare our build is to change the js files that our index.html
loads depending on environment. We will do this by using gulp injection (not to be confused
with Angular’s dependency injection). Install the package with npm install gulp-inject --save-dev.
Next, we’ll need to prepare our index.html file for injection. This is done by inserting html comment markers that gulp will use:

<!DOCTYPE html>
<html data-ng-app="blackjack">
<head lang="en">
    <meta charset="UTF-8">
    <title>Blackjack</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <style>
        /* This helps the ng-show/ng-hide animations start at the right place. */
        /* Since Angular has this but needs to load, this gives us the class early. */
        .ng-hide {
            display: none!important;
        }
    </style>

    <!-- bower:css -->
    <!-- endinject -->

    <!-- inject:css -->
    <!-- endinject -->


</head>
<body>
    <div data-ng-include="'app/layout/shell.html'"></div>

    <!-- bower:js -->
    <!-- endinject -->

    <!-- Begin: Source JS -->
    <!-- inject:js -->
    <!-- endinject -->
    <!-- End: Source JS -->

</body>
</html>

You’ll notice that all of our files are gone! Don’t fret, they will be back when our task is completed. We’ve got a few different injection types in our index: bower:css, inject:css, bower:js, and inject:js.

Our injections will be separate based on if they are bower components or our code. The bower components that are injected will be the same on both environments (although, we could use the minified versions of each in the production environment, but it is a bit more configuration than I’d care to throw at this post!). What will change is our code that is getting injected. We want the concatenated version of our js files to go in the production release, while we want our files to be individually included for the development environment.

One more thing we want to do is to make sure our build folder could easily be deployed to a server. This means we want all the files necessary to serve our application within the build folder. There will be a few tasks that copy the necessary files to the build folder. Let’s look at our gulpfile.js:

var gulp = require('gulp');
var concat = require('gulp-concat');
var ngAnnotate = require('gulp-ng-annotate');
var karma = require('karma').server;
var del = require('del');
var inject = require('gulp-inject');
var bowerFiles = require('main-bower-files');

//Configuration
var buildFolder = __dirname + '/build/';

//Tasks
gulp.task('test', function (done) {
    karma.start({
        configFile: __dirname + '/karma.config.js',
        singleRun: true
    }, done);
});

gulp.task('index',['clean'],function(){
    return gulp.src('./src/index.html')
        .pipe(gulp.dest('./build'));
});

// Production Build Tasks
/**
 * Injects release files into index.html
 */
gulp.task('inject', ['concat', 'css', 'html', 'vendor'], function(){
    return gulp.src('./src/index.html')
        .pipe(gulp.dest('./build'))
        .pipe(inject(gulp.src('./build/vendor/*.js', {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./build/vendor/*.css', {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./build/app.js', {read: false}), {relative: true}))
        .pipe(inject(gulp.src('./build/content/*.css', {read: false}), {relative: true}))
        .pipe(gulp.dest('./build'));
});

/**
 * Copies vendor files from bower to the build/vendor folder
 */
gulp.task('vendor', function () {
    return gulp.src(bowerFiles())
        .pipe(gulp.dest('./build/vendor'));
});

/**
 * Cleans the build folder
 */
gulp.task('clean', function() {
    del(buildFolder + '**');
});

/**
 * Concats application js files into app.js
 */
gulp.task('concat', function () {
    return gulp.src(['src/app/**/*.module.js', 'src/app/**/*.js', '!src/app/**/*.spec.js'])
        .pipe(concat('app.js'))
        .pipe(ngAnnotate())
        .pipe(gulp.dest(buildFolder));
});

/**
 * Copies application css to build folder
 */
gulp.task('css', function () {
    return gulp.src('./src/content/*.css')
        .pipe(gulp.dest('./build/content'));
});

/**
 * Copies html files to build folder
 */
gulp.task('html', function () {
    return gulp.src('./src/app/**/*.html')
        .pipe(gulp.dest('./build/app'));
});

// Development Build Tasks
/**
 * Updates the index.html file
 * with all the injections
 */
gulp.task('injectDev', function(){
    return gulp.src('./src/index.html')
        .pipe(inject(gulp.src(['./src/app/**/*.module.js','./src/app/**/*.js','!./src/app/**/*.spec.js'], {read: false}), {relative: true, name: ''}))
        .pipe(inject(gulp.src(bowerFiles(), {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./src/content/*.css',{read:false}), {relative: true}))
        .pipe(gulp.dest('./src'));
});




gulp.task('build', ['inject']);

gulp.task('buildDev', ['injectDev']);

We now have two main tasks: build and buildDev. As you can see, these execute a series of sub tasks in our gulp file. One nice thing I found was the npm package main-bower-files. This uses your bower.json dependencies and builds a list of files for use in gulp. It makes it much easier to include bower files into your injections.

With these build tasks complete, we should be able to run http-server from either the src folder or the build folder and have our application work as expected.

Here is the result of our work.

Up Next: Playing the game

Angular Project Blackjack: 7 – File Concatenation with Gulp

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

We’ve created quite a lot of files in just our application so far. Unfortunately, for every one of those files, it means we need to add an include to our index.html file and increase the request count for visitors just to load the app. Wouldn’t it be nice if we could just package all of our code into one file and just load that file in the application? That’s exactly what we’ll set out to do here.

There are a TON of applications that will get us the end result we are looking for: browserify, require, webpack, etc. Although, in my opinion, the simplest and easiest to understand is gulp-concat. At its very basic form, gulp-concat literally merges the files you give it into one output file. To get it going, we’ll need to have gulp installed, and get to gulp installed, we really should know what it is doing first!

Gulp (or gulp) is a javascript powered task runner. You define your tasks in a gulpfile.js file and run them from the command line. It is easiest to run gulp when installed globally, so we’ll install gulp via npm globally and also add it to our developer dependencies. We’ll also install two different gulp plugins: “gulp-concat” and “gulp-ng-annotate”. “ng-annotate” provides us with a safeguard for dependency injection. If it comes across code with a missing injection, while it is processing it will put those in.

npm install --global gulp
npm install --save-dev gulp gulp-concat gulp-ng-annotate

Let’s configure our file concatenation task in our gulpfile.js:

var gulp = require('gulp');
var concat = require('gulp-concat');
var ngAnnotate = require('gulp-ng-annotate');

gulp.task('concat', function () {
    return gulp.src(['src/app/**/*.module.js', 'src/app/**/*.js', '!src/app/**/*.spec.js'])
        .pipe(concat('app.js'))
        .pipe(ngAnnotate())
        .pipe(gulp.dest('src/app/'));
});

We’ve created a task called ‘concat’ that can be run from the command line with ‘gulp concat’. Inside this task, we are loading source files with the ‘src’ command and then piping those files into a flow of commands. Since all of our angular configuration modules need to be loaded before the ‘run’ code is loaded, we will put them first. We also don’t want our tests in there, so we’ll tell it to ignore all the ‘spec’ files in those paths. The first task is to merge them together with the ‘concat’ command. We are merging these into a file called app.js. Next, we’ll fix any issues with our injections via ‘ngAnnotate()’. Finally, we’ll tell the task to output the file into the ‘src/app’ directory.

If you run this task now, you’ll see our new ‘app.js’ file get created. We can now load our single app.js file in the index.html file instead of the multiple files we were loading before. This will help reduce the amount of requests each page load will make and keep our html source cleaner. You should now be able to run http-server and have it load only the app.js file.

<!DOCTYPE html>
<html data-ng-app="blackjack">
<head lang="en">
    <meta charset="UTF-8">
    <title>Blackjack</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <style>
        /* This helps the ng-show/ng-hide animations start at the right place. */
        /* Since Angular has this but needs to load, this gives us the class early. */
        .ng-hide {
            display: none!important;
        }
    </style>

    <!-- Begin: Vendor CSS -->
    <link href="bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- End: Vendor CSS -->

    <!-- Begin: Source CSS -->
    <link href="content/blackjack.css" rel="stylesheet">
    <!-- End: Source CSS -->


</head>
<body>
    <div data-ng-include="'app/layout/shell.html'"></div>

    <!-- Begin: Vendor JS -->
    <script src="bower_components/angular/angular.min.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
    <script src="bower_components/jquery/dist/jquery.min.js"></script>
    <!-- End: Vendor JS -->

    <!-- Begin: Source JS -->
    <script src="app/app.js"></script>
    <!-- End: Source JS -->
</body>
</html>

Now that we have gulp ready for tasks, our next step (and blog post) will be to automate some of our development activities.

Here is our project up to this point:

https://github.com/adamweeks/angular-blackjack/tree/blog-post-7

Up Next: More Gulp Tasks!