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.
//index.jsconstexpress=require('express');constcors=require('cors');constbodyParser=require('body-parser');constpath=require('path');const{setupGameRoutes}=require('./game');const{connectToDatabase}=require('./db');constapp=express();constPORT=process.env.PORT||5000;app.use(cors());app.use(bodyParser.json());// Connect to the databaseconnectToDatabase();// Setup game routessetupGameRoutes(app);// Serve static files from the Angular appapp.use(express.static(path.join(__dirname,'..','client','dist')));app.get('*',(req,res)=>{res.sendFile(path.join(__dirname,'..','client','dist','index.html'));});// Start the serverapp.listen(PORT,()=>{console.log(`Server is running on port ${PORT}`);});
JavaScript
//game.jsconst{Card,Player,Game}=require('./db');// Helper function to create a new deckfunctioncreateDeck(){constsuits=['Hearts','Diamonds','Clubs','Spades'];constranks=['2','3','4','5','6','7','8','9','10','J','Q','K','A'];constdeck=[];for(constsuitofsuits){for(constrankofranks){constcard=newCard({suit:suit,rank:rank,value:rank==='A'?11:isNaN(rank)?10:parseInt(rank),});deck.push(card);}}returndeck;}// Helper function to calculate the score of a handfunctioncalculateScore(hand){letscore=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;}});returnscore;}// Helper function to determine the winnerfunctiondetermineWinner(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}elseif(playerScore<dealerScore){return'Dealer';// Dealer has a higher score}else{return'Draw';// Scores are equal, it's a draw}}// Setup game routesfunctionsetupGameRoutes(app){// Endpoint to start a new gameapp.post('/game/start',async(req,res)=>{try{constnewDeck=createDeck();// Shuffle the deck (Fisher-Yates algorithm)for(leti=newDeck.length-1;i>0;i--){constj=Math.floor(Math.random()*(i+1));[newDeck[i],newDeck[j]]=[newDeck[j],newDeck[i]];}constnewGame=newGame({deck:newDeck,player:{name:'Player',hand:[],score:0},dealer:{name:'Dealer',hand:[],score:0},});// Deal the initial two cards to the player and the dealernewGame.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 scoresnewGame.player.score=calculateScore(newGame.player.hand);newGame.dealer.score=calculateScore(newGame.dealer.hand);// Save the new game to the databaseawaitnewGame.save();res.status(201).json(newGame);}catch(error){console.error(error);res.status(500).send('Internal Server Error');}});// Endpoint to handle player hitsapp.post('/game/hit',async(req,res)=>{try{constgameId=req.body.gameId;// Fetch the game from the databaseconstgame=awaitGame.findById(gameId);// Draw a card from the deck and add it to the player's handconstdrawnCard=game.deck.pop();game.player.hand.push(drawnCard);// Update the player's scoregame.player.score=calculateScore(game.player.hand);// Set the winner fieldgame.winner=determineWinner(game.player.score,game.dealer.score);// Save the updated game to the databaseawaitgame.save();res.json({...game.toObject(),winner:game.winner});}catch(error){console.error(error);res.status(500).send('Internal Server Error');}});// Endpoint to handle player standingapp.post('/game/stand',async(req,res)=>{try{constgameId=req.body.gameId;// Fetch the game from the databaseconstgame=awaitGame.findById(gameId);// Dealer draws cards until their score is 17 or higherwhile(game.dealer.score<17){constdrawnCard=game.deck.pop();game.dealer.hand.push(drawnCard);game.dealer.score=calculateScore(game.dealer.hand);}// Set the winner fieldgame.winner=determineWinner(game.player.score,game.dealer.score);// Save the updated game to the databaseawaitgame.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};
//src/app/game/game.component.tsimport{Component,OnInit}from'@angular/core';import{HttpClient}from'@angular/common/http';@Component({selector:'app-game',templateUrl:'./game.component.html',styleUrls:['./game.component.css']})exportclassGameComponentimplementsOnInit{gameState:any;winnerMessage:string='';constructor(privatehttp: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);}}