One Approach To Making A Card Game With Firebase Realtime Database

Myles Tyrer
5 min readOct 6, 2020

The Firebase Realtime Database is one of my favorite things on the internet. You can watch in real time as people interact with your code, and the screen lights up as the data changes. After learning about Firebase, I had an idea: could I use it to make a card game to play remotely with someone else? I decided to challenge myself and create a platform that would allow me to play cribbage over the internet. My parents first taught me cribbage when I was very young to help me learn basic math. It’s fun, but has a lot of rules. Luckily this will be no problem for the Realtime Database!

This article will cover making a virtual deck of cards that can be dealt, shuffled, and shared between at least two people on different machines. In a later article I’ll code out the rules to cribbage, but you can use these principles to create just about any card game. Let’s go!

See the final product here: https://mtv1243.github.io/deck_of_cards/
See the code here: https://github.com/mtv1243/deck_of_cards

Initialize Firebase

Initializing Firebase is fairly straightforward.

firebase.initializeApp(firebaseConfig);

Here, firebaseConfig is an object holding my firebase credentials, the project ID, etc. Then, inside the database, I will create a reference to a deck of cards, and I’ll call the variable deckRef, which will reference ‘deckoCards’.

let DeckReference = firebase.database();
let deckRef = DeckReference.ref('deckoCards');

Creating A Virtual Card Deck

To make the card deck, I create a class - Card - and have the constructor take two arguments: suit and value.

class Card {
constructor(suit, value){
this.suit = suit;
this.value = value;
}

Then I create a class - Deck - that will assemble these cards into a deck and shuffle them.

class Deck {
constructor() {
this.deck = [];
}

createDeck(suits, values) {
for(let suit of suits) {
for(let value of values){
this.deck.push(new Card(suit, value));
}
}
return this.deck;
}

shuffle() {
let counter = this.deck.length, temp, i;
while(counter) {
i = Math.floor(Math.random() * counter--);
temp = this.deck[counter];
this.deck[counter] = this.deck[i];
this.deck[i] = temp;
}
return this.deck;
}

Now when I create a deck by calling the createDeck method, the number of suits and values dictate the number of cards.

let suits = ['Hearts', 'Diamonds', 'Spades', 'Clubs'];
let values = ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'];
let cardDeck = new Deck();
cardDeck.createDeck(suits, values);
cardDeck.shuffle();

I have a virtual deck of cards! Next I need to get it into Firebase using the set() method. But to make sure both players are working from the same deck, I will update the Deck’s shuffle method with the database reference, so that any time someone shuffles the deck, the deck in the database is updated automatically.

shuffle() {
let counter = this.deck.length, temp, i;
while(counter) {
i = Math.floor(Math.random() * counter--);
temp = this.deck[counter];
this.deck[counter] = this.deck[i];
this.deck[i] = temp;
}
// update the database with this new shuffled deck
deckRef.set(this.deck);
return this.deck;

Working From The Database, Not The DOM

The ‘value’ event fires any time there is a change to the firebase database, and it takes a callback function which gives you a snapshot of the part of the database that changed. I can use the ‘value’ event to change the DOM any time a card moves from one part of the database to another, like when dealing.

For my purposes, I’ve created three references in the database.

let deckRef = DeckReference.ref('deckoCards');
let player1Ref = DeckReference.ref('player1Hand');
let player2Ref = DeckReference.ref('player2Hand');
// set the hands to empty arrays to start
player1Ref.set({player1Cards:[]});
player2Ref.set({player2Cards:[]});

Here is what dealing a card to player 1 from the deck looks like:

deckRef.once('value', (snap)=>{
// get one time snapshot of the deck array
let fbDeck = snap.val();

// take the top card off the deck
let fbCard = fbDeck.shift();

// set the new deck to the database
deckRef.set(fbDeck);

// push the new card into player 1's database hand
player1Ref.push().set(fbCard);
});

But we will need to alternate dealing between the two players, so there should be a boolean variable called ‘turn’ that alternates between true and false. If true, cards are dealt to player 1; if false, cards are dealt to player 2.

deckRef.once('value', (snap)=>{
// get one time snapshot of the deck array
let fbDeck = snap.val();

// take the top card off the deck
let fbCard = fbDeck.shift();

// set the new deck to the database
deckRef.set(fbDeck);

// deal to alternate players
let turn = true;
if(turn){
player1Ref.push().set(fbCard);
turn = false;
} else {
player2Ref.push().set(fbCard);
turn = true;
});

Now if I am listening for the ‘value’ event on a player’s hand, I can use the change in value to trigger a change in the DOM.

player1Ref.on('value', (snap)=>{
// get all cards in the hand
let hand = snap.val();
// reset the player's hand element
player1El.innerHTML='';
for (key in hand) {
let suit = hand[key].suit;
let value = hand[key].value;
let cardEl = document.createElement('div');
cardEl.classList.add('card');
cardEl.classList.add(suit);
cardEl.innerHTML =
`<span class="${value} value">${value}</span>`;
}
});

So any time the number of cards in a player’s hand changes, it will rewrite the DOM to match the database. And since this code will run on each machine that loads the page, everyone using the app will see the same thing.

See the DOM update as cards are dealt from the database

For now, players can hide or show their opponent’s cards with a button. This will have to be on the honor system! In a subsequent article I will make sure players can control when the opponent sees their cards.

Next time I’ll go over how clicking a card in the DOM plays it into either the crib or the pot, while still aligning with the practice of user actions only directly changing the database, and letting the database change the DOM.

--

--

Myles Tyrer

I’m a web developer focusing on interactive projects using React and Nodejs.