Skip to main content

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

5 min read

Older Article

This article was published 13 years ago. Some information may be outdated or no longer applicable.

I love challenges and learning new technologies. My JavaScript knowledge pretty much stopped at jQuery. I thought that was it, there’s nothing more to know beyond .append(), .click(function ()) and so on. I was wrong. I’ve set myself a challenge: learn the latest and greatest of JavaScript by writing an online, multiplayer game using Node.js and Socket.io. I’m going to document the steps as I go through the learning process.

The code for part 1 won’t be on GitHub. It’s in a sketchy format that’d probably confuse people, and it’ll likely change as I keep developing. I’m not revealing the actual card game yet. That’ll go public once the game is live.

Let’s start with the basics: setting up and running Node.js, then installing Socket.io. I followed the official instructions for both (Socket.io setup). Done. Now let’s write some JavaScript.

The approach I’m following is elementary. Since this is a card game, we need to produce a deck, shuffle it, draw cards, and play cards. That’s it for now. Logic (when to play a card, which player can play what) comes later. Much later. To make this a bit “Node-ish”, I’ll create two JavaScript files: game.js (containing the game’s logic) and index.js (running a few tests).

The card game will use 52 cards from a French pack or Anglo-American pack. For those unfamiliar: there are 4 suits (Hearts, Diamonds, Spades, Clubs), numbers from 2 to 10, and figures (Jack, Queen, King, Ace). (I can’t resist mentioning the Ace of Spades.)

The simplest way to represent these cards is numbering from 1 to 13 for each card (1 is an Ace, 11-12-13 are Jack-Queen-King) and appending a letter: H for Hearts, D for Diamonds, and so on. The Ace of Spades becomes 1S. The Jack of Hearts becomes 11H.

Without further ado, here’s the code. First, we create a pack of 52 cards and add a method to shuffle it. Shuffling is a bit tricky because JavaScript doesn’t ship with a good array shuffle method, so we need 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[packCount++] = j + suits[i];
    }
  }
  finalPack = pack.concat(pack);
  return finalPack;

Two more functions needed: drawing a card and playing a card. These are simple array operations.

Update After testing, I noticed that concat() doesn’t work on arrays sent as references (in this case the “hand” array), so I switched to push.apply(). See the MDN reference for 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;
}

Finally, we need to export all these functions (making them available to other JavaScript files). The last lines of game.js should be:

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

Time to test. Create index.js and add the following content. Note the highlighted row where we pull in game.js:

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);

Run node index.js from the command line. The result should look something like:

# 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 it for now. Simple so far. Next time, we’ll add a websocket using Socket.io and keep building. Until then.