Open In App

Twenty-one Game using MEAN Stack

Last Updated : 23 Jul, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Twenty-one Game is a popular card game played in casinos worldwide. In this article, we'll explore how to create a Twenty-one game also known as the BlackJack game using the MEAN stack (MongoDB, Express.js, Angular, and Node.js). By building a Twenty-one game, we'll see the various aspects of web development with the MEAN stack.

Project Preview:

Screenshot-2024-04-28-020213
Twenty-one Game using MEAN Stack

Prerequisites

Approach to Create the Game

  • Set up project structure with client and server folders.
  • Create Express.js routes for game actions.
  • Develop Angular components for the game UI.
  • Implement Blackjack game logic with JavaScript.
  • Apply CSS styling for a visually appealing interface.

Steps to Create the Project

Step 1: Create the project folder using the following command.

mkdir blackjack-mean
cd blackjack-mean

Step 2: Create a server folder

mkdir server
cd server

Step 3: Initialize the node application using the following command.

npm init -y

Folder Structure(Backend)

Screenshot-2024-04-28-020236
Backend Folder Structure
Step 4: Create the required files as shown in folder structure.

Step 5: Install required dependencies

npm install express mongoose cors body-parser path

Updated dependencies will look like

 "dependencies": {
        "body-parser": "^1.20.2",
        "cors": "^2.8.5",
        "express": "^4.19.2",
        "mongoose": "^8.3.2",
        "path": "^0.12.7"
      }

Example

JavaScript
//index.js

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const path = require('path');
const { setupGameRoutes } = require('./game');
const { connectToDatabase } = require('./db');

const app = express();
const PORT = process.env.PORT || 5000;

app.use(cors());
app.use(bodyParser.json());

// Connect to the database
connectToDatabase();

// Setup game routes
setupGameRoutes(app);

// Serve static files from the Angular app
app.use(express.static(path
    .join(__dirname, '..', 'client', 'dist')));
app.get('*', (req, res) => {
    res.sendFile(path
        .join(__dirname, '..', 'client', 'dist', 'index.html'));
});

// Start the server
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});
JavaScript
//game.js
const { Card, Player, Game } = require('./db');

// Helper function to create a new deck
function createDeck() {
    const suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades'];
    const ranks = ['2', '3', '4', '5', '6', '7',
                     '8', '9', '10', 'J', 'Q', 'K', 'A'];

    const deck = [];
    for (const suit of suits) {
        for (const rank of ranks) {
            const card = new Card({
                suit: suit,
                rank: rank,
                value: rank === 'A' ? 11 :
                     isNaN(rank) ? 10 : parseInt(rank),
            });
            deck.push(card);
        }
    }

    return deck;
}

// Helper function to calculate the score of a hand
function calculateScore(hand) {
    let score = hand.reduce((total, card) =>
         total + card.value, 0);

    // Handle Aces (reduce value from 11 to 1 if necessary)
    hand.filter(card => card.rank === 'A')
    .forEach(_ => {
        if (score > 21) {
            score -= 10;
        }
    });

    return score;
}

// Helper function to determine the winner
function determineWinner(playerScore, dealerScore) {
    if (playerScore > 21) {
        return 'Dealer'; // Player busts, dealer wins
    }

    if (dealerScore > 21) {
        return 'Player'; // Dealer busts, player wins
    }

    if (playerScore > dealerScore) {
        return 'Player'; // Player has a higher score
    } else if (playerScore < dealerScore) {
        return 'Dealer'; // Dealer has a higher score
    } else {
        return 'Draw'; // Scores are equal, it's a draw
    }
}

// Setup game routes
function setupGameRoutes(app) {
    // Endpoint to start a new game
    app.post('/game/start', async (req, res) => {
        try {
            const newDeck = createDeck();

            // Shuffle the deck (Fisher-Yates algorithm)
            for (let i = newDeck.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [newDeck[i], newDeck[j]] = [newDeck[j], newDeck[i]];
            }

            const newGame = new Game({
                deck: newDeck,
                player: { name: 'Player', hand: [], score: 0 },
                dealer: { name: 'Dealer', hand: [], score: 0 },
            });

            // Deal the initial two cards to the player and the dealer
            newGame.player.hand.push(newGame.deck.pop());
            newGame.dealer.hand.push(newGame.deck.pop());
            newGame.player.hand.push(newGame.deck.pop());
            newGame.dealer.hand.push(newGame.deck.pop());

            // Update scores
            newGame.player.score = calculateScore(newGame.player.hand);
            newGame.dealer.score = calculateScore(newGame.dealer.hand);

            // Save the new game to the database
            await newGame.save();

            res.status(201).json(newGame);
        } catch (error) {
            console.error(error);
            res.status(500).send('Internal Server Error');
        }
    });

    // Endpoint to handle player hits
    app.post('/game/hit', async (req, res) => {
        try {
            const gameId = req.body.gameId;

            // Fetch the game from the database
            const game = await Game.findById(gameId);

            // Draw a card from the deck and add it to the player's hand
            const drawnCard = game.deck.pop();
            game.player.hand.push(drawnCard);

            // Update the player's score
            game.player.score = calculateScore(game.player.hand);

            // Set the winner field
            game.winner = determineWinner(game.player.score, game.dealer.score);

            // Save the updated game to the database
            await game.save();

            res.json({ ...game.toObject(), winner: game.winner });
        } catch (error) {
            console.error(error);
            res.status(500).send('Internal Server Error');
        }
    });

    // Endpoint to handle player standing
    app.post('/game/stand', async (req, res) => {
        try {
            const gameId = req.body.gameId;

            // Fetch the game from the database
            const game = await Game.findById(gameId);

            // Dealer draws cards until their score is 17 or higher
            while (game.dealer.score < 17) {
                const drawnCard = game.deck.pop();
                game.dealer.hand.push(drawnCard);
                game.dealer.score = calculateScore(game.dealer.hand);
            }

            // Set the winner field
            game.winner = determineWinner(game.player.score, game.dealer.score);

            // Save the updated game to the database
            await game.save();

            res.json({ ...game.toObject(), winner: game.winner });
        } catch (error) {
            console.error(error);
            res.status(500).send('Internal Server Error');
        }
    });
}

module.exports = { createDeck, calculateScore, determineWinner, setupGameRoutes };
JavaScript
//db.js

const mongoose = require('mongoose');

// Database connection configuration
function connectToDatabase() {
    mongoose.connect('mongodb://localhost:27017/blackjack', {
        useNewUrlParser: true,
        useUnifiedTopology: true,
    });
}

const cardSchema = new mongoose.Schema({
    suit: String,
    rank: String,
    value: Number,
});

const playerSchema = new mongoose.Schema({
    name: String,
    hand: [cardSchema],
    score: Number,
});

const gameSchema = new mongoose.Schema({
    deck: [cardSchema],
    player: playerSchema,
    dealer: playerSchema,
    winner: String,
});

const Card = mongoose.model('Card', cardSchema);
const Player = mongoose.model('Player', playerSchema);
const Game = mongoose.model('Game', gameSchema);

module.exports = { connectToDatabase, Card, Player, Game };

To start the server run the following command.

node server.js

Step 6: Install Angular CLI if you haven't already in the root folder.

npm install -g @angular/cli

Step 7: Initialize a new Angular project.

ng new client
cd client

Folder Structure(Frontend)

Screenshot-2024-04-23-222746
Frontend Folder Structure
Step 8: Install the required dependencies

npm install axios
npm install @angular/common @angular/compiler @angular/core @angular/forms 
@angular/platform-browser @angular/platform-browser-dynamic 
@angular/router rxjs tslib zone.js

Updated dependencies will look like:

"dependencies": {
        "@angular/animations": "^17.3.0",
        "@angular/common": "^17.3.5",
        "@angular/compiler": "^17.3.5",
        "@angular/core": "^17.3.5",
        "@angular/forms": "^17.3.5",
        "@angular/platform-browser": "^17.3.5",
        "@angular/platform-browser-dynamic": "^17.3.5",
        "@angular/router": "^17.3.5",
        "axios": "^1.6.8",
        "rxjs": "~7.8.0",
        "tslib": "^2.6.2",
        "zone.js": "^0.14.4"
      },

Example: Create the required files as shown in the folder structure and add the following codes.

HTML
<!-- src/app/game/game.component.html -->

<div class="navbar">
    <h1>GFG Twenty-one Game</h1>
</div>

<div class="kl">
    <h1>Twenty-one Game</h1>
    <p class="winner-message">{{ winnerMessage }}</p>
    <div class="ma">
        <div class="playerside">
            <h2>Player Hand:</h2>
            <ul>
                <li *ngFor="let card of gameState?.player.hand">
                    {{ card.rank }} of {{ card.suit }}
                </li>
            </ul>
            <p>Score: {{ gameState?.player.score }}</p>
        </div>
        <div class="dealerside">
            <h2>Dealer Hand:</h2>
            <ul>
                <li *ngFor="let card of gameState?.dealer.hand">
                    {{ card.rank }} of {{ card.suit }}
                </li>
            </ul>
            <p>Score: {{ gameState?.dealer.score }}</p>
        </div>
    </div>
    <div class="buttons">
        <button (click)="handleHit()">Hit</button>
        <button (click)="handleStand()">Stand</button>
        <button (click)="startNewGame()">Start New Game</button>
    </div>
</div>
HTML
<!-- app.component.html -->

<app-game></app-game>
HTML
<!-- index.html -->

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>BlackjackClient</title>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>

<body>
    <app-root></app-root>
</body>

</html>
CSS
/* src/app/game/game.component.css */

body {
    background-color: #00203F;
    color: #ADEFD1;
    font-family: sans-serif;
}

.navbar {
    background-color: #00203F;
    color: green;
    padding: 15px 0;
    text-align: center;
}

.navbar h1 {
    margin: 0;
    font-size: 24px;
    font-weight: bold;
}

.kl {
    text-align: center;
    padding: 20px;
}

ul {
    list-style-type: none;
    padding: 0;
}

button {
    background-color: #efd8ad;
    color: #00203F;
    border: none;
    padding: 10px 20px;
    margin: 5px;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

button:hover {
    background-color: #00203F;
    color: #ade2ef;
}

.winner-message {
    color: #FF5555;
    font-size: 30px;
    font-weight: bolder;
    margin-top: 20px;
    animation: fadeIn 1s ease;
}

@keyframes fadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

.ma {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
}

h2,
p {
    color: #00203F;
}

.playerside {
    padding: 2%;
    background-color: #adcfef;
    color: #00203F;
    width: fit-content;
    margin: 2%;
    border-radius: 35px;
}

.dealerside {
    width: fit-content;
    margin: 2%;
    border-radius: 35px;
    padding: 2%;
    background-color: #adceef;
    color: #00203F;
}
JavaScript
//src/app/game/game.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
    selector: 'app-game',
    templateUrl: './game.component.html',
    styleUrls: ['./game.component.css']
})
export class GameComponent implements OnInit {
    gameState: any;
    winnerMessage: string = '';

    constructor(private http: HttpClient) { }

    ngOnInit(): void {
        this.startNewGame();
    }

    startNewGame(): void {
        this.http.post<any>('http://localhost:5000/game/start', {})
            .subscribe(response => {
                this.gameState = response;
                this.winnerMessage = '';
            }, error => console
                .error('Error starting a new game:', error));
    }

    handleHit(): void {
        this.http.post<any>('http://localhost:5000/game/hit', 
        { gameId: this.gameState._id })
            .subscribe(response => {
                this.gameState = response;
                this.checkWinner(response.winner);
            }, error => console
                .error('Error hitting:', error));
    }

    handleStand(): void {
        this.http.post<any>('http://localhost:5000/game/stand',
         { gameId: this.gameState._id })
            .subscribe(response => {
                this.gameState = response;
                this.checkWinner(response.winner);
            }, error => console.error('Error standing:', error));
    }

    checkWinner(winner: string): void {
        this.winnerMessage = `Winner: ${winner}`;
        setTimeout(() => {
            this.startNewGame();
        }, 3000);
    }
}
JavaScript
//app.component.ts

import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}
JavaScript
//app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { GameComponent } from './game/game.component';

@NgModule({
    declarations: [
        AppComponent,
        GameComponent
    ],
    imports: [
        BrowserModule,
        HttpClientModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }
JavaScript
//app.routes.ts

import { Routes } from '@angular/router';

export const routes: Routes = [];
JavaScript
//main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));


In another terminal, navigate to the client folder and start the Angular development server.

ng serve

Open your web browser and go to http://localhost:4200 to access the MEAN Blackjack game.

Output:

game


Similar Reads