Online card game with node.js and socket.io - Episode 1
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.