(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
.