Problem
I am learning javascript with the course Javascript Getting started by Mark Zaymota.
Currently the code is in a script.js file, and I was wondering how could we refactor it to extract classes from it.
The code in a file, until cards’ generation and shuffle is:
index.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Blackjack game</h1>
<h6>By Yone Moreno</h6>
<br>
<p id='text-area'>Welcome to BlackJack!!!!!</p>
<button id='new-game-button'>Start a New Game!!!</button>
<button id='hit-button'>Hit!</button>
<button id='stay-button'>Stay...</button>
<script src="script.js"></script>
</body>
</html>
script.js
// Blackjack
let textArea = document.getElementById('text-area');
let newGameButton = document.getElementById('new-game-button');
let hitButton = document.getElementById('hit-button');
let stayButton = document.getElementById('stay-button');
let gameStarted = false,
gameOver = false,
playerWon = false,
dealerCards = [],
playerCards = [],
dealerScore = 0,
playerScore = 0,
deck = [];
hitButton.style.display = 'none';
stayButton.style.display = 'none';
showStatus();
newGameButton.addEventListener('click', function() {
gameStarted = true;
gameOver = false;
playerWon = false;
deck = createDeck();
shuffleDeck(deck);
dealerCards = [getNextCard(), getNextCard()];
playerCards = [getNextCard(), getNextCard()];
newGameButton.style.display = 'none';
hitButton.style.display = 'inline';
stayButton.style.display = 'inline';
showStatus();
})
let suits = ['Hearts', 'Clubs', 'Diamonds', 'Spades'];
let values = ['As', 'King', 'Queen', 'Jack',
'Ten', 'Nine', 'Eight', 'Seven', 'Six',
'Five', 'Four', 'Three', 'Two'
]
function createDeck() {
let deck = [];
for (suitIndex = 0; suitIndex < suits.length; suitIndex++) {
for (let valueIndex = 0; valueIndex < values.length; valueIndex++) {
let card = {
suit: suits[suitIndex],
value: values[valueIndex]
};
deck.push(card);
}
}
return deck;
}
function getNextCard() {
return deck.shift();
}
function getCardString(card) {
return card.value + ' of ' + card.suit;
}
function showStatus() {
if (!gameStarted) {
textArea.innerText = 'Welcome to BlackJack!!!!!!!!!!';
return;
}
for (let i = 0; i < deck.length; i++) {
textArea.innerText += 'n' + getCardString(deck[i]);
}
}
function shuffleDeck(deck) {
for (let i = 0; i < deck.length; i++) {
let swapIndex = Math.trunc(Math.random() * deck.length);
let randomCard = deck[swapIndex];
let currentCard = deck[i];
deck[swapIndex] = currentCard;
deck[i] = randomCard;
}
}
How could we refactor it?
I thought we should create some classes:
Deck, with createDeck() and shuffleDeck()
Card with getNextCard() and getCardString()
GameManager with buttons, text area and vriables, style logic, call to functions and event listeners.
Until now I have extracted Deck class:
Deck.js
class Deck{
createDeck() {
let deck = [];
for (let suitIndex = 0; suitIndex < suits.length; suitIndex++) {
for (let valueIndex = 0; valueIndex < values.length; valueIndex++) {
let card = {
suit: suits[suitIndex],
value: values[valueIndex]
};
deck.push(card);
}
}
this.deck = deck;
return deck;
}
shuffleDeck() {
for (let i = 0; i < deck.length; i++) {
let swapIndex = Math.trunc(Math.random() * deck.length);
let randomCard = deck[swapIndex];
let currentCard = deck[i];
deck[swapIndex] = currentCard;
deck[i] = randomCard;
}
}
}
new script.js
// Blackjack
let textArea = document.getElementById('text-area');
let newGameButton = document.getElementById('new-game-button');
let hitButton = document.getElementById('hit-button');
let stayButton = document.getElementById('stay-button');
let gameStarted = false,
gameOver = false,
playerWon = false,
dealerCards = [],
playerCards = [],
dealerScore = 0,
playerScore = 0,
deck = [];
hitButton.style.display = 'none';
stayButton.style.display = 'none';
showStatus();
let myClassDeck = new Deck();
newGameButton.addEventListener('click', function () {
gameStarted = true;
gameOver = false;
playerWon = false;
myClassDeck.createDeck();
myClassDeck.shuffleDeck(myClassDeck);
dealerCards = [getNextCard(), getNextCard()];
playerCards = [getNextCard(), getNextCard()];
newGameButton.style.display = 'none';
hitButton.style.display = 'inline';
stayButton.style.display = 'inline';
showStatus();
})
let suits = ['Hearts', 'Clubs', 'Diamonds', 'Spades'];
let values = ['As', 'King', 'Queen', 'Jack',
'Ten', 'Nine', 'Eight', 'Seven', 'Six',
'Five', 'Four', 'Three', 'Two'
]
function getNextCard() {
let nextCard = myClassDeck.deck.shift();
console.log('card given is:', nextCard);
return nextCard;
}
function getCardString(card) {
return card.value + ' of ' + card.suit;
}
function showStatus() {
if (!gameStarted) {
textArea.innerText = 'Welcome to BlackJack!!!!!!!!!!';
return;
}
for (let i = 0; i < myClassDeck.deck.length; i++) {
textArea.innerText += 'n' + getCardString(myClassDeck.deck[i]);
}
}
How could we improve it?
I think we should use constructor, and create get and set to deck into Deck.js
Solution
If you start going the OOP way (which works great for this type of game), your next step should be to make the main game a class as well. That prevents having all your variables and functions in the global scope.
You can use arrow functions to prevent nested functions in a class. It’s clearer to just call a method of the class. Now you can call newGame
from other places as well.
class Game {
constructor(){
this.suits = ['Hearts', 'Clubs', 'Diamonds', 'Spades']
this.textArea = document.getElementById('text-area')
this.newGameButton = document.getElementById('new-game-button')
this.myDeck = new Deck()
this.newGameButton.addEventListener('click',()=>this.newGame())
this.showStatus()
}
showStatus(){
}
newGame(){
this.myDeck.createDeck()
this.myDeck.shuffleDeck()
}
}
new Game()