Online card game with node.js and socket.io - Episode 1

I love challenges as well as learning about new technologies. My knowledge and exploration of JavaScript pretty much stopped with jQuery. I thought, that's it, there's nothing more to know about JS then .append(), .click(function ()) and so on, and how wrong I was. I have set a challenge to myself, I want to learn the 'latest and greatest' of JavaScript and I decided to write an online, multiplayer game using node.js and socket.io and I'm going to document the steps while going through this learning process.

The code behind part 1 of the series is not going to be available on GitHub as it's in a pretty sketchy format, so it'd probably confuse people and it's likely that the code will change as I keep on developing the application further. At the moment I won't reveal what the actual card game is, that will be made public once the game is available online.

Let's start from the basics, setting up and running node.js and then installing socket.io. I haven't done anything but followed the official instructions. Simples. Now let's write some JavaScript. The approach I was trying to follow is elementary - this being a card game, we need to produce a deck, shuffle a deck, draw cards and play cards. That's pretty much it for the time being. Logic, such as when to play a card, and which player can play what card will be added later (much later, actually). To make this a bit 'nodejs-y', I will create two JavaScript files, game.js and index.js, where the former will include the JS containing the game's logic and the latter will run a few tests. The card game at the moment will also be limited to have 52 cards - French pack or Anglo-American pack) - for those of you who are not familiar with these cards, there are 4 suits: Hearts, Diamonds, Spades and Clubs; numbers: from 2-10 and finally figures: J(ack), Q(ueen), K(ing) and A(ce). (In light of the above, I'm sorry but I can't resists not mentioning the Ace of Spades.)

The most simple way to represent these cards is to use numbering from 1-13 for each card where 1 is an Ace and 11-12-13 is Jack-Queen-King, and append these with a letter, H for Hearts, D for diamonds, etc, therefore the above mentioned Ace of Spades will become 1S and the Jack of Hearts will become 11H.

Without further ado, behold the code - first we need to create a pack of 52 cards, and also add a method to shuffle it. Shuffling a deck is a bit complicated as JavaScript doesn't come with a good array shuffle method, so we need to use the Fisher-Yates algorithm.

function createPack() {
  var suits = new Array("H", "C", "S", "D");
  var pack = new Array();
  var n = 52;
  var index = n / suits.length;

  var count = 0;
  for(i = 0; i <= 3; i++)
      for(j = 1; j <= index; j++)
          pack[count++] = j + suits[i];

  return pack;
}

function shufflePack(pack) {
  var i = pack.length, j, tempi, tempj;
  if (i === 0) return false;
  while (--i) {
     j = Math.floor(Math.random() * (i + 1));
     tempi = pack[i];
     tempj = pack[j];
     pack[i] = tempj;
     pack[j] = tempi;
   }
  return pack;
}

There are two more functions needed, to draw a card and to play a card - these will be simple operations on arrays:

Update After testing I noticed that the concat() method does not work on arrays that are sent as references (in this case the 'hand' array) and hence I have to use the push.apply() method. Please see the MDN reference for further details.

function draw(pack, amount, hand, initial) {
  var cards = new Array();
  cards = pack.slice(0, amount);

  pack.splice(0, amount);

  if (!initial) {
    hand.push.apply(hand, cards);
    //hand.concat(hand);
  }

  return cards;
}

function playCard(amount, hand, index) {
  hand.splice(index, amount)
  return hand;
}

And finally we need to export all these functions (that is, making the functions available for other JavaScript files) so the last lines of game.js should be:

exports.createPack = createPack;
exports.shufflePack = shufflePack;
exports.draw = draw;
exports.playCard = playCard;

Time to test this all, so create index.js and add the following content - make note of the highlighted row, this is where we 'insert' the game.js file:

var game = require("./game");

var pack = game.createPack();
var myPack = game.shufflePack(pack);
console.log("Size of pack before draw: " + myPack.length);
console.log("Drawing 5 cards.");
var hand = game.draw(myPack, 5, '', true);
console.log("Size of pack after draw: " + myPack.length);
console.log("Cards in hand:");
console.log(hand);
console.log();
console.log("Now I'll draw a card");
var draw = game.draw(myPack, 1, hand, false);
console.log(draw);
console.log("Size of pack after drawing one card: " + myPack.length);
console.log("So all cards in my hand are: ");
console.log(hand);
//////
console.log();
console.log("Now I'll draw 3 cards");
var draw = game.draw(myPack, 3, hand, false);
console.log(draw);
console.log("Size of pack after drawing 3 cards: " + myPack.length);
console.log("So all cards in my hand are: ");
console.log(hand);
console.log();
console.log("I'll play one card now, dropping the last card.");
console.log()
var lastCard = hand.length-1;
console.log("Last card's index: " + lastCard);
var newHand = game.playCard(1, hand, lastCard);
console.log("Cards in my new hand are:");
console.log(newHand);
console.log();
console.log("I'll play the third card:");
var thirdCard = 2; //index of 3rd card is 2
console.log("Index of the third card: " + thirdCard);
var evenNewerHand = game.playCard(1, newHand, thirdCard);
console.log("Cards now in my hand hand are:");
console.log(evenNewerHand);
console.log();
console.log("Size of pack should not change: " + myPack.length);

Now simple execute node index.js from your command line and the result should be something similar to:

# node index.js
Size of pack before draw: 52
Drawing 5 cards.
Size of pack after draw: 47
Cards in hand:
[ '6H', '3H', '5S', '13H', '8D' ]

Now I'll draw a card
[ '8H' ]
Size of pack after drawing one card: 46
So all cards in my hand are: 
[ '6H', '3H', '5S', '13H', '8D', '8H' ]

Now I'll draw 3 cards
[ '2H', '7C', '7S' ]
Size of pack after drawing 3 cards: 43
So all cards in my hand are: 
[ '6H', '3H', '5S', '13H', '8D', '8H', '2H', '7C', '7S' ]

I'll play one card now, dropping the last card.

Last card's index: 8
Cards in my new hand are:
[ '6H', '3H', '5S', '13H', '8D', '8H', '2H', '7C' ]

I'll play the third card:
Index of the third card: 2
Cards now in my hand hand are:
[ '6H', '3H', '13H', '8D', '8H', '2H', '7C' ]

Size of pack should not change: 43

That's if for now, so far simple. Next time we will try to add a websocket using socket.io and continue developing our application. Until next time!

Show Comments