Blackjack Game using MEAN Stack
Last Updated :
15 Mar, 2024
This is a project to get a thorough understanding of MEAN Stack technologies (MongoDB, Express, Node JS, Angular). This will give you a step-by-step process to create a blackjack game from scratch. This article will discuss Starting a new game, the logic to play it and store it in the database. It will also help you to understand the CRUD operations.
Output Preview: Let us have a look at how the final output will look like.
OUTPUT PREVIEW OF START GAME PAGE
Prerequisites:
Approach to create Blackjack Game using MEAN Stack:
Backend:
- Set up a new node.js project
- Create server.js file to setup the server using express.
- Create an instance of app and use cors package as a middleware.
- Create routes folder and setup routes for servicing API requests.
- Set up mongo DB locally and create a method to connect to the database in server.js file.
- Create a database and a collection to store and retrieve the game related data.
- Implement the core logic of game for example – dealing cards, hit action, stand action, calculate scores and determine the winner
- Test your API using tools like postman.
Frontend:
- Create a new Angular project.
- Create various components for game for different functionalities and define HTML and CSS files for all of them.
- Create a service to set up communication between backend API endpoints and Angular HttpClient.
- Update the user interface based on the state of game given by backend.
- Test your frontend application in browser.
Steps to create the project:
Step 1: Create the main folder for the project using the following command.
mkdir black-jack
Step 2: Initialize the node.js project.
npm init -y
Step 3: Install the required dependencies
npm install express cors mongoose
The updated Dependencies in package.json file of backend will look like:
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.3",
"mongoose": "^8.2.1"
}
Project Structure(Backend):
OUTPUT IMAGE OF PROJECT STRUCTURE FOR BACKEND
Example: Create the required files as seen on the folder structure and add the following codes.
Node
// server.js
const express = require('express');
const mongoose = require('mongoose');
const gameRoutes = require('./routes/gameRoutes');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
app.use('/api/game', gameRoutes);
mongoose.connect('mongodb://localhost:27017/blackjack', {
useNewUrlParser: true,
useUnifiedTopology: true,
family: 4,
})
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
const PORT = 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Node
// gameController.js
const {
dealCard,
calculateHandValue,
isBlackjack,
} = require("../utils/gameUtils");
const Game = require("../models/gameModels");
exports.getAllGames = async (req, res) => {
try {
const games = await Game.find();
res.json(games);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
exports.createGame = async (req, res) => {
const existingGame = await Game.findOne({
playerHand: [],
status: "ongoing",
});
if (existingGame) {
return res.status(200).json(existingGame);
}
const game = new Game({
playerHand: [],
dealerHand: [],
playerTotal: 0,
dealerTotal: 0,
winner: "",
status: "ongoing",
});
try {
const newGame = await game.save();
res.status(201).json(newGame);
} catch (err) {
res.status(400).json({ message: err.message });
}
};
exports.getGameById = async (req, res) => {
try {
const game = await Game.findById(req.params.id);
if (game == null) {
return res.status(404)
.json({ message: "Game not found" });
}
res.json(game);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
exports.dealCards = async (req, res) => {
try {
const game = await Game.findById(req.params.id);
game.playerHand.push(dealCard());
game.dealerHand.push(dealCard());
game.playerHand.push(dealCard());
game.dealerHand.push(dealCard());
game.playerTotal = calculateHandValue(game.playerHand);
game.dealerTotal = calculateHandValue(game.dealerHand);
if (isBlackjack(game.playerHand) && !isBlackjack(game.dealerHand)) {
game.status = "player_wins";
game.winner = "Player";
} else if (isBlackjack(game.dealerHand) && !isBlackjack(game.playerHand)) {
game.status = "dealer_wins";
game.winner = "Dealer";
} else if (isBlackjack(game.dealerHand) && isBlackjack(game.playerHand)) {
game.status = "tie";
game.winner = "Tie";
}
await game.save();
res.json(game);
} catch (err) {
res.status(400).json({ message: err.message });
}
};
exports.updateGame = async (req, res) => {
try {
const { id } = req.params;
const updates = req.body;
const game = await Game.findById(id);
if (!game) {
return res.status(404)
.json({ message: "Game not found" });
}
if (game.status === "ended") {
return res
.status(400)
.json({ message: "Cannot update a game that has already ended" });
}
if (game.playerHand.length > 0 && "playerHand" in updates) {
return res
.status(400)
.json({ message: "Cannot change player hand after dealing" });
}
const updatedGame = await Game.findByIdAndUpdate(id, updates);
res.json(updatedGame);
} catch (err) {
res.status(400).json({ message: err.message });
}
};
exports.hitAction = async (req, res) => {
try {
const { id } = req.params;
const game = await Game.findById(id);
if (!game) {
return res.status(404)
.json({ message: "Game not found" });
}
if (game.status !== "ongoing") {
return res.status(400)
.json({ message: "Game is not in progress" });
}
const newCard = dealCard();
while (
game.playerHand.includes(newCard) ||
game.dealerHand.includes(newCard)
) {
newCard = dealCard();
}
game.playerHand.push(newCard);
game.playerTotal = calculateHandValue(game.playerHand);
if (game.playerTotal > 21) {
game.status = "player_busted";
game.winner = "Dealer";
} else if (game.playerTotal == 21) {
game.status = "player_wins";
game.winner = "Player";
}
await game.save();
res.json(game);
} catch (err) {
console.error(err);
res.status(500).json({ message: "Internal Server Error" });
}
};
exports.standAction = async (req, res) => {
try {
const { id } = req.params;
console.log("Stand action", id);
const game = await Game.findById(id);
if (!game) {
return res.status(404)
.json({ message: "Game not found" });
}
if (game.status !== "ongoing") {
return res.status(400).json({ message: "Game is not in progress" });
}
game.status = "player_stands";
while (calculateHandValue(game.dealerHand) < 17) {
let newCard = dealCard();
while (
game.playerHand.includes(newCard) ||
game.dealerHand.includes(newCard)
) { }
game.dealerHand.push(newCard);
}
game.dealerTotal = calculateHandValue(game.dealerHand);
const playerTotal = calculateHandValue(game.playerHand);
const dealerTotal = calculateHandValue(game.dealerHand);
if (dealerTotal > 21 || playerTotal > dealerTotal) {
game.status = "player_wins";
game.winner = "Player";
} else if (playerTotal < dealerTotal) {
game.status = "dealer_wins";
game.winner = "Dealer";
} else {
game.status = "tie";
game.winner = "Tie";
}
await game.save();
res.json(game);
} catch (err) {
console.error(err);
res.status(500).json({ message: "Internal Server Error" });
}
};
exports.endGame = async (req, res) => {
try {
const { id } = req.params;
const game = await Game.findById(id);
if (!game) {
return res.status(404).json({ message: "Game not found" });
}
const playerTotal = calculateHandValue(game.playerHand);
const dealerTotal = calculateHandValue(game.dealerHand);
if (playerTotal <= 21 && playerTotal > dealerTotal) {
game.winner = "Player";
} else if (dealerTotal <= 21 && playerTotal < dealerTotal) {
game.winner = "Dealer";
} else if (playerTotal > 21 && dealerTotal <= 21) {
game.winner = "Dealer";
} else if (dealerTotal > 21 && playerTotal <= 21) {
game.winner = "Player";
} else {
game.winner = "Tie";
}
game.status = "ended";
const updatedGame = await game.save();
res.json(updatedGame);
} catch (err) {
res.status(400).json({ message: err.message });
}
};
exports.deleteGame = async (req, res) => {
try {
const { id } = req.params;
await Game.findByIdAndDelete(id);
res.json({ msg: "Game deleted" });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
};
Node
// gameRoutes.js
const express = require('express');
const router = express.Router();
const gameController = require('../controller/gameController');
router.get('/', gameController.getAllGames);
router.post('/create', gameController.createGame);
router.get('/:id', gameController.getGameById);
router.post('/deal/:id', gameController.dealCards);
router.put('/update/:id', gameController.updateGame);
router.put('/end/:id', gameController.endGame);
router.post('/hit/:id', gameController.hitAction);
router.post('/stand/:id', gameController.standAction);
router.delete('/delete/:id', gameController.deleteGame);
module.exports = router;
Node
// gameUtils.js
const cardValues = {
'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10,
'J': 10, 'Q': 10, 'K': 10, 'A': 11
};
function dealCard() {
const suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades'];
const ranks = Object.keys(cardValues);
const suit = suits[Math.floor(Math.random() * suits.length)];
const rank = ranks[Math.floor(Math.random() * ranks.length)];
return { rank, suit, value: cardValues[rank] };
}
function calculateHandValue(hand) {
let total = 0;
let numAces = 0;
for (const card of hand) {
total += card.value;
if (card.rank === 'A') numAces++;
}
while (total > 21 && numAces > 0) {
total -= 10;
numAces--;
}
return total;
}
function isBlackjack(hand) {
return hand.length === 2 && calculateHandValue(hand) === 21;
}
module.exports = { dealCard, calculateHandValue, isBlackjack };
Node
// gameModels.js
const mongoose = require('mongoose');
const gameSchema = new mongoose.Schema({
playerHand: [{ rank: String, suit: String, value: Number }],
dealerHand: [{ rank: String, suit: String, value: Number }],
playerTotal: { type: Number, default: 0 },
dealerTotal: { type: Number, default: 0 },
winner: String,
status: { type: String, default: 'ongoing' }
});
module.exports = mongoose.model('Game', gameSchema);
To start the backend run the following command.
nodemon server.js
Step 4: Install the angular CLI
npm install -g @angular/cli
Step 5: Create a new angular project using the following command.
ng new frontend
Step 6: Create components in angular for different functionality
ng generate component <component-name>
Step 7: Create service for communication between backend and frontend
ng generate service <service-name>
Step 8: Create model for game matching to the game schema we have created in mongo DB
ng generate interface <model-name>
The updated Dependencies in package.json file of frontend will look like:
"dependencies": {
"@angular/animations": "^17.0.0",
"@angular/cdk": "^17.2.2",
"@angular/common": "^17.0.0",
"@angular/compiler": "^17.0.0",
"@angular/core": "^17.0.0",
"@angular/forms": "^17.0.0",
"@angular/material": "^17.2.2",
"@angular/platform-browser": "^17.0.0",
"@angular/platform-browser-dynamic": "^17.0.0",
"@angular/platform-server": "^17.0.0",
"@angular/router": "^17.0.0",
"@angular/ssr": "^17.0.10",
"express": "^4.18.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.2"
}
Project Structure(Frontend):
OUTPUT GIF OF PROJECT STRUCTURE FOR FRONTEND
Example: Create the required files s seen in folder structure and add the following codes.
HTML
<!-- game-list.component.html -->
<a href="/create" class="link-button">Start new game</a>
<div *ngIf="games.length > 0; else noGames" class="game-list">
<h1 class="game-list-title">List of Games</h1>
<table class="game-table">
<thead>
<tr>
<th>Player Hand</th>
<th>Dealer Hand</th>
<th>Player Total</th>
<th>Dealer Total</th>
<th>Winner</th>
<th>Status</th>
<th style="text-align: center">Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let game of games" class="game-row">
<td *ngIf="game.playerHand.length > 0 ||
game.status !== 'ongoing'" class="player-hand">
<div *ngFor="let player of game.playerHand">
{{ player.rank }} of {{ player.suit }}
</div>
</td>
<td *ngIf="game.playerHand.length > 0 || game.status !== 'ongoing'"
class="dealer-hand">
<div *ngFor="let dealer of game.dealerHand">
{{ dealer.rank }} of {{ dealer.suit }}
</div>
</td>
<td *ngIf="game.playerHand.length > 0 || game.status !== 'ongoing'"
class="player-total">
{{ game.playerTotal }}
</td>
<td *ngIf="game.playerHand.length > 0 || game.status !== 'ongoing'"
class="dealer-total">
{{ game.dealerTotal }}
</td>
<td *ngIf="game.playerHand.length > 0 || game.status !== 'ongoing'"
class="winner">
{{ game.winner }}
</td>
<td *ngIf="game.playerHand.length > 0 || game.status !== 'ongoing'"
class="status">
{{ game.status }}
</td>
<td *ngIf="game.playerHand.length > 0 || game.status !== 'ongoing'"
class="actions"
style="text-align: center">
<button (click)="getGameById(game._id)" class="get-button">
Get
</button>
<button (click)="updateGame(game._id)" [disabled]="game.status === 'ended'"
class="update-button"
style="background-color: green">
Update
</button>
<button (click)="confirmDelete(game._id)" style="background-color: rgb(181, 57, 57)">
Delete
</button>
</td>
</tr>
</tbody>
</table>
</div>
<ng-template #noGames>
<div class="noGames">
<p>No games available</p>
</div>
</ng-template>
HTML
<!-- game-dialog.component.html -->
<div *ngIf="!isUpdateDialog" class="game-details-dialog">
<h2>Game Details</h2>
<table class="game-details-table">
<tr>
<th>Player Hand</th>
<td>
<div *ngFor="let player of data.playerHand" class="card">
{{ player.rank }} of {{ player.suit }}
</div>
</td>
</tr>
<tr>
<th>Dealer Hand</th>
<td>
<div *ngFor="let dealer of data.dealerHand" class="card">
{{ dealer.rank }} of {{ dealer.suit }}
</div>
</td>
</tr>
<tr>
<th>Player Total</th>
<td>{{ data.playerTotal }}</td>
</tr>
<tr>
<th>Dealer Total</th>
<td>{{ data.dealerTotal }}</td>
</tr>
<tr>
<th>Winner</th>
<td>{{ data.winner }}</td>
</tr>
<tr>
<th>Status</th>
<td>{{ data.status }}</td>
</tr>
</table>
<button mat-button class="close-button" (click)="closeDialog()">Close</button>
</div>
<div *ngIf="isUpdateDialog" class="update-game-dialog">
<h2>Update Game</h2>
<table class="update-game-table">
<tr>
<th>Player Hand</th>
<td>
<div *ngFor="let player of data.game.playerHand" class="card">
{{ player.rank }} of {{ player.suit }}
</div>
</td>
</tr>
<tr>
<th>Dealer Hand</th>
<td>
<div *ngFor="let dealer of data.game.dealerHand" class="card">
{{ dealer.rank }} of {{ dealer.suit }}
</div>
</td>
</tr>
<tr>
<th>Player Total</th>
<td>{{ data.game.playerTotal }}</td>
</tr>
<tr>
<th>Dealer Total</th>
<td>{{ data.game.dealerTotal }}</td>
</tr>
<tr>
<th>Winner</th>
<td>{{ data.game.winner }}</td>
</tr>
<tr>
<th>Status</th>
<td>{{ data.game.status }}</td>
</tr>
</table>
<div class="dialog-buttons">
<button mat-button class="play-button" (click)="playGame(data.game._id)">
Continue Game
</button>
<button mat-button class="close-button" (click)="closeDialog()">
Close
</button>
</div>
</div>
HTML
<!-- start-game.component.html -->
<div class="start-game">
<h2>Player Hand</h2>
<app-hand
[isPlayer]="true"
[playerHand]="game.playerHand"
class="hand"
></app-hand>
<h2>Dealer Hand</h2>
<app-hand
[isPlayer]="false"
[dealerHand]="game.dealerHand"
class="hand"
></app-hand>
</div>
<div class="button-container">
<button
(click)="dealCards(game._id)"
[disabled]="game.playerHand.length > 0"
class="action-button"
style="margin-left: 2vmax"
>
Deal Cards
</button>
<button
(click)="hitAction(game._id)"
[disabled]="game.status !== 'ongoing'"
class="action-button"
>
Hit
</button>
<button
(click)="standAction(game._id)"
[disabled]="game.status !== 'ongoing'"
class="action-button"
>
Stand
</button>
<button (click)="checkStart()"
class="action-button">Start new Game</button>
<button (click)="endGame(game._id)"
class="action-button">End Game</button>
<button (click)="homePage()"
class="action-button">Back to Home page</button>
</div>
HTML
<!-- hand.component.html -->
<div class="hand-container">
<h2>Deck of Cards</h2>
<div class="card-container" *ngIf="playerHand">
<ng-container *ngFor="let card of playerHand">
<div class="card">
Card {{ card.rank }} of {{ card.suit }}
</div>
</ng-container>
<div *ngIf="isPlayer" class="total-score">
<span>Total Score:
{{ calculateScore(playerHand) }}</span>
</div>
</div>
<div class="card-container" *ngIf="dealerHand">
<ng-container *ngFor="let card of dealerHand">
<div class="card">
Card {{ card.rank }} of
{{ card.suit }}
</div>
</ng-container>
<div *ngIf="!isPlayer" class="total-score">
<span>Total Score:
{{ calculateScore(dealerHand) }}</span>
</div>
</div>
</div>
HTML
<!-- winner-dialog.component.html -->
<div class="winner-dialog">
<h1>{{ data.message }}</h1>
<button mat-button class="close-button"
(click)="closeDialog()">Close</button>
</div>
HTML
<!-- app.component.html -->
<div class="container">
<h1>GFG Blackjack Game</h1>
</div>
<router-outlet></router-outlet>
CSS
/* game-list.component.css */
.game-list {
margin-top: 20px;
margin-left: 8%;
}
.game-list-title {
margin-bottom: 2vmax;
text-align: center;
font-weight: bold;
box-sizing: border-box;
box-shadow: #777;
}
.game-table {
width: 90%;
border-collapse: collapse;
border-radius: 5px;
}
.game-table th,
.game-table td {
padding: 8px;
text-align: left;
border: 1px solid black;
}
.game-table th {
background-color: #f2f2f2;
}
.game-table tbody tr:hover {
background-color: #f5f5f5;
}
.game-table button {
padding: 5px 10px;
border: none;
background-color: #0056b3;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
.game-table button[disabled] {
background-color: #cccccc;
cursor: not-allowed;
}
.actions button {
margin-right: 5px;
margin-bottom: 1vmax;
}
.link-button {
display: inline-block;
padding: 8px 16px;
background-color: #1b599a;
color: #fff;
text-decoration: none;
border-radius: 4px;
cursor: pointer;
margin-left: 8%;
margin-top: 3%;
}
button:disabled {
background-color: rgb(124, 170, 124);
color: black;
cursor: not-allowed;
opacity: 0.5;
}
.noGames {
width: 82%;
margin-top: 20px;
padding: 10px;
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-radius: 4px;
margin-left: 8%;
margin-right: 8%;
}
.noGames p {
text-align: center;
margin: 0;
}
CSS
/* game-dialog.component.css */
.game-details-dialog,
.update-game-dialog {
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.game-details-table,
.update-game-table {
width: 100%;
margin-bottom: 20px;
}
.game-details-table th,
.game-details-table td,
.update-game-table th,
.update-game-table td {
padding: 10px;
border-bottom: 1px solid #ddd;
}
.card {
margin-bottom: 5px;
}
.close-button,
.play-button {
padding: 10px 20px;
margin-right: 10px;
border: none;
border-radius: 5px;
background-color: #0056b3;
color: #fff;
cursor: pointer;
float: right;
margin-bottom: 1vmax;
}
.dialog-buttons {
text-align: right;
}
h2 {
text-align: center;
}
CSS
/* start-game.component.css */
h2 {
margin-left: 12%;
margin-top: 1.5vmax;
}
.hand {
margin-bottom: 20px;
}
.action-button {
padding: 10px 20px;
margin-right: 10px;
border: none;
border-radius: 5px;
background-color: #0056b3;
color: #fff;
cursor: pointer;
}
.button-container {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin-top: -16vmax;
margin-left: 30%;
}
button {
margin-bottom: 1.5vmax;
}
button:disabled {
background-color: #95b1ce;
color: black;
cursor: not-allowed;
opacity: 0.5;
}
CSS
/* hand.component.css */
.hand-container {
display: flex;
flex-direction: column;
justify-content: center;
border: 1px solid lightgrey;
border-radius: 10px;
margin: 2vmax;
margin-left: 12%;
width: 20%;
text-align: center;
}
.card-container {
flex-wrap: 1;
}
.card {
font-size: 1.1rem;
padding-left: 1.5vmax;
padding-right: 1.5vmax;
padding-bottom: 1vmax;
}
.total-score {
padding: 1.5vmax;
border-top: 1px solid lightgrey;
border-radius: 10px;
font-weight: bold;
text-transform: uppercase;
font-size: 1.2rem;
}
h2 {
padding-top: 1.5vmax;
padding-left: 1.5vmax;
padding-right: 1.5vmax;
padding-bottom: 0;
margin-bottom: 1.5vmax;
text-decoration: underline;
}
CSS
/* winner-dialog.component.css */
h1 {
color: green;
font-weight: bold;
font-family: cursive;
font-size: 3rem;
text-align: center;
padding-top: 2vmax;
}
.winner-dialog {
background-color: rgb(174, 213, 174);
}
.close-button {
padding: 10px 20px;
border: none;
border-radius: 5px;
background-color: green;
color: #fff;
cursor: pointer;
float: right;
}
CSS
/* app.component.css */
.container {
display: flex;
flex-direction: column;
align-items: center;
background-color: darkslategrey;
color: white;
text-decoration: underline;
}
h1 {
font-weight: bold;
padding: 1.2vmax;
}
Javascript
// game-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Game } from '../../models/game';
import { GameService } from '../../services/game.service';
import { MatDialog } from '@angular/material/dialog';
import { GameDialogComponent } from '../game-dialog/game-dialog.component';
import { CommonModule } from '@angular/common';
import { Router } from '@angular/router';
@Component({
selector: 'app-game-list',
standalone: true,
imports: [GameDialogComponent, CommonModule],
templateUrl: './game-list.component.html',
styleUrl: './game-list.component.css'
})
export class GameListComponent implements OnInit {
games: Game[] = [];
game: Game | undefined;
constructor(private gameService: GameService,
private dialog: MatDialog,
private router: Router) { }
ngOnInit(): void {
this.getAllGames();
}
getAllGames(): void {
this.gameService.getAllGames().subscribe(
(data: Game[]) => {
this.games = data;
},
(error) => {
console.error('Error fetching games:', error);
}
);
}
getGameById(id: string): void {
this.gameService.getGameById(id).subscribe(
(data: Game) => {
this.game = data;
this.openDialog(this.game);
},
(error) => {
console.error('Error fetching game:', error);
}
);
}
openDialog(game: Game): void {
const dialogRef = this.dialog.open(GameDialogComponent, {
width: '30%',
data: game
});
dialogRef.afterClosed().subscribe(() => {
console.log('The dialog was closed');
});
}
updateGame(id: string): void {
this.gameService.updateGame(id).subscribe(
(data: Game) => {
this.game = data;
this.openUpdateDialog(this.game);
},
(error) => {
console.error('Error updating game:', error);
}
);
}
openUpdateDialog(game: Game): void {
const dialogRef = this.dialog.open(GameDialogComponent, {
width: '30%',
data: {
game: game,
type: 'update'
}
});
dialogRef.afterClosed().subscribe(() => {
console.log('The dialog was closed');
});
}
endGame(id: string): void {
this.gameService.endGame(id).subscribe(
(data: Game) => {
console.log('End Game:', data);
},
(error) => {
console.error('Error in ending game:', error);
}
);
}
confirmDelete(gameId: string): void {
const confirmDelete = window.confirm
('Are you sure you want to delete this game?');
if (confirmDelete) {
this.deleteGame(gameId);
}
}
deleteGame(id: string): void {
this.gameService.deleteGame(id).subscribe(
(data: Game) => {
console.log('Game Deleted:', data);
this.gameService.getAllGames().subscribe(
(data: Game[]) => {
this.games = data;
},
(error) => {
console.error('Error fetching games:', error);
}
);
},
(error) => {
console.error('Error in deleting game:', error);
}
);
}
}
Javascript
// game-dialog.component.ts
import { CommonModule } from '@angular/common';
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';
@Component({
selector: 'app-game-dialog',
standalone: true,
imports: [CommonModule],
templateUrl: './game-dialog.component.html',
styleUrl: './game-dialog.component.css'
})
export class GameDialogComponent {
isUpdateDialog: boolean = false;
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
private dialogRef: MatDialogRef<GameDialogComponent>,
private router: Router) { }
ngOnInit(): void {
this.isUpdateDialog = this.data.type === 'update';
}
closeDialog(): void {
this.dialogRef.close();
}
playGame(id: string): void {
this.router.navigate(["/create"], { queryParams: { id } });
this.dialogRef.close({ action: 'play' });
}
}
Javascript
// start-game.component.ts
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { HandComponent } from '../hand/hand.component';
import { GameService } from '../../services/game.service';
import { Game } from '../../models/game';
import { ActivatedRoute, Router } from '@angular/router';
import { skip, take } from 'rxjs';
import { WinnerDialogComponent } from '../winner-dialog/winner-dialog.component';
@Component({
selector: 'app-start-game',
standalone: true,
imports: [FormsModule, CommonModule, HandComponent, MatDialogModule],
templateUrl: './start-game.component.html',
styleUrl: './start-game.component.css',
})
export class StartGameComponent {
game: Game = {
_id: '',
playerHand: [],
dealerHand: [],
playerTotal: 0,
dealerTotal: 0,
winner: '',
status: 'ongoing',
};
gameId: string = '';
startAfresh: boolean = false;
constructor(
private dialog: MatDialog,
private gameService: GameService,
private router: Router,
private route: ActivatedRoute
) { }
ngOnInit(): void {
this.startNewGame();
}
checkStart(): void {
this.startAfresh = true;
this.startNewGame();
}
startNewGame(): void {
this.route.queryParams.subscribe((params) => {
console.log(params);
this.gameId = params['id'];
if (this.gameId && this.gameId != '' && !this.startAfresh) {
console.log(this.gameId);
this.gameService.getGameById(this.gameId).subscribe(
(data: Game) => {
this.game = data;
},
(error) => {
console.error('Error fetching game:', error);
}
);
} else {
console.log('Starting game from scratch');
this.gameService.createGame().subscribe(
(data: Game) => {
console.log('New game started:', data);
this.game = data;
},
(error: any) => {
console.error('Error starting new game:', error);
}
);
}
});
}
dealCards(id: string): void {
this.gameService.dealCards(id).subscribe(
(data: Game) => {
console.log('Cards dealt:', data);
this.game = data;
if (this.game.winner !== '') {
this.openWinnerDialog(this.game.winner);
}
},
(error) => {
console.error('Error dealing cards:', error);
}
);
}
hitAction(id: string): void {
this.gameService.hitAction(id).subscribe(
(data: Game) => {
console.log('Hit Action:', data);
this.game = data;
if (this.game.winner !== '') {
this.openWinnerDialog(this.game.winner);
}
},
(error) => {
console.error('Error in hit action:', error);
}
);
}
standAction(id: string): void {
this.gameService.standAction(id).subscribe(
(data: Game) => {
console.log('Stand Action:', data);
this.game = data;
if (this.game.winner !== '') {
this.openWinnerDialog(this.game.winner);
}
},
(error) => {
console.error('Error in stand action:', error);
}
);
}
homePage(): void {
this.router.navigate(['/']);
}
endGame(id: string): void {
this.gameService.endGame(id).subscribe(
(data: Game) => {
console.log('End Game:', data);
if (this.game.winner !== '') {
this.openWinnerDialog(this.game.winner);
}
this.router.navigate(['/']);
},
(error) => {
console.error('Error in ending game:', error);
}
);
}
openWinnerDialog(winner: string): void {
if (winner !== 'Tie') {
this.dialog.open(WinnerDialogComponent, {
width: '30%',
data: { message: `${winner} wins!` },
});
} else {
this.dialog.open(WinnerDialogComponent, {
width: '20%',
height: '50%',
data: { message: `${winner}` },
});
}
}
}
Javascript
// hand.component.ts
import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import { Card } from '../../models/card';
@Component({
selector: 'app-hand',
standalone: true,
imports: [CommonModule],
templateUrl: './hand.component.html',
styleUrl: './hand.component.css'
})
export class HandComponent {
@Input() playerHand?: Card[] = [];
@Input() isPlayer: boolean = true;
@Input() dealerHand?: Card[] = [];
constructor() { }
calculateScore(hand: Card[]): number {
let total = 0;
let numAces = 0;
for (const card of hand) {
total += card.value;
if (card.rank === 'A') numAces++;
}
while (total > 21 && numAces > 0) {
total -= 10;
numAces--;
}
return total;
}
}
Javascript
// winner-dialog.component.ts
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';
@Component({
selector: 'app-winner-dialog',
standalone: true,
imports: [],
templateUrl: './winner-dialog.component.html',
styleUrl: './winner-dialog.component.css'
})
export class WinnerDialogComponent {
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
private dialogRef: MatDialogRef<WinnerDialogComponent>,
private router: Router) { }
closeDialog(): void {
this.dialogRef.close();
this.router.navigate(["/"]);
}
}
Javascript
// winner-dialog.component.ts
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';
@Component({
selector: 'app-winner-dialog',
standalone: true,
imports: [],
templateUrl: './winner-dialog.component.html',
styleUrl: './winner-dialog.component.css'
})
export class WinnerDialogComponent {
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
private dialogRef: MatDialogRef<WinnerDialogComponent>,
private router: Router) { }
closeDialog(): void {
this.dialogRef.close();
this.router.navigate(["/"]);
}
}
Javascript
// app.routes.ts
import { Routes } from '@angular/router';
import { GameListComponent }
from './components/game-list/game-list.component';
import { StartGameComponent }
from './components/start-game/start-game.component';
export const routes: Routes = [
{ path: '', component: GameListComponent },
{ path: 'create', component: StartGameComponent },
{ path: ':id', component: GameListComponent },
{ path: 'deal/:id', component: StartGameComponent },
{ path: 'update/:id', component: GameListComponent },
{ path: 'hit/:id', component: StartGameComponent },
{ path: 'stand/:id', component: StartGameComponent },
{ path: 'end/:id', component: StartGameComponent }
];
Javascript
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { StartGameComponent } from './components/start-game/start-game.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { GameListComponent } from './components/game-list/game-list.component';
import { MatDialogModule } from '@angular/material/dialog';
@NgModule({
declarations: [
AppComponent,
GameListComponent,
StartGameComponent
],
imports: [
BrowserModule,
HttpClientModule,
CommonModule,
FormsModule,
MatDialogModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
To start the frontend run the following command.
ng serve
Output:
OUTPUT GIF FOR BLACKJACK GAME PROJECT USING MEAN STACK
Share your thoughts in the comments
Please Login to comment...