Blackjack Basic Project

Hi! I completed a simple Blackjack script as part of a beginner course.

How can i improve this code?

I know that i can use objects for some things, but i wanto to keep it basic.

// BlackJack App

//Card Vars
let suits = ['Hearts', 'Clubs', 'Diamonds', 'Spades'];
let values = ['Ace', 'King', 'Queen', 'Jack', 'Ten', 'Nine', 'Eight',
             'Seven', 'Six', 'Five', 'Four', 'Three', 'Two'];

//Dom Vars
let paragraph = document.getElementById('text-area');
let playerCardsParagraph = document.getElementById('player-cards');
let dealerCardsParagraph = document.getElementById('dealer-cards');
let playerScoreParagraph = document.getElementById('player-score');
let dealerScoreParagraph = document.getElementById('dealer-score');
let newGameButton = document.getElementById('new-game');
let hitButton = document.getElementById('hit');
let stayButton = document.getElementById('stay');
let winnerText = document.getElementById('winner');

//Game vars
let gameStarted = false,
    gameOver = false,
    gameWon = false,
    playerWon = false,
    dealerWon = false,
    dealerCards = [],
    playerCards = [],
    deck = [],
    playerScore = 0,
    dealerScore = 0;

//New Game Button
  newGameButton.addEventListener('click', function() {
    gameStarted = true;

    deck = createDeck();
    playerCards = [getNextCard(), getNextCard()];
    dealerCards = [getNextCard(), getNextCard()];

    paragraph.innerText = 'New Game Started';
    playerCardsParagraph.innerText = `${getCardString(playerCards[0])}
      ${getCardString(playerCards[1])}`;
    playerScoreParagraph.innerText = `Score: ${getScore(playerCards)}`;

    dealerCardsParagraph.innerText = `${getCardString(dealerCards[0])}
      ${getCardString(dealerCards[1])} `;
    dealerScoreParagraph.innerText = `Score: ${getScore(dealerCards)}`;
      
    hideNewGameButton();
  });

//Hit Button
  hitButton.addEventListener('click', function() {
    playerCards.push(getNextCard());
    playerCardsParagraph.innerText += `\n ${getCardString(playerCards[playerCards.length - 1])}`;
    playerScoreParagraph.innerText = `Score: ${getScore(playerCards)}`;
    dealerScoreParagraph.innerText = `Score: ${getScore(dealerCards)}`;
    gameOver = false;
    checkEndGame();
  });

//Stay Button
  stayButton.addEventListener('click', function() {
    gameOver = true;
    checkEndGame();
  });

//Show New Game Button
function showNewGameButton() {
  newGameButton.style.display = 'inline';
  hitButton.style.display = 'none';
  stayButton.style.display = 'none';
}

//Hide New Game Button
function hideNewGameButton() {
  newGameButton.style.display = 'none';
  hitButton.style.display = 'inline';
  stayButton.style.display = 'inline';
}

//Winner Text
function returnWinner(winner) {
  return winnerText.innerText = `${winner} Won`;
}

//Create the Deck
function createDeck() {
  let deck = [];
  for (let suitIdx = 0; suitIdx < suits.length; suitIdx++) {
    for (let valueIdx = 0; valueIdx < values.length; valueIdx++) {
      let card = {
        suit: suits[suitIdx],
        value: values[valueIdx]
      };
      deck.push(card);
    }
  }
  return deck;
}

//Get next card
function getNextCard() {
  let cardNum = Math.round(Math.random() * 51);
  return deck[cardNum];
}

//Convert Card obj to String
function getCardString(card) {
  return card.value + ' of ' + card.suit;
}

//Get Card Values
function getCardvalues(card) {
  switch(card.value) {
    case 'Ace':
      return 1;
      break;
    case 'Two':
      return 2;
      break;
    case 'Three':
      return 3;
      break;
    case 'Four':
      return 4;
      break;
    case 'Five':
      return 5;
      break;
    case 'Six':
      return 6;
      break;
    case 'Seven':
      return 7;
      break;
    case 'Eight':
      return 8;
      break;
    case 'Nine':
      return 9;
      break;
    default: 
      return 10;
      break;
  }
}

//Calculate Score
function getScore(cardArray) {
  let score = 0;
  let hasAce = false;
  for (let i = 0; i < cardArray.length; i++) {
    score += getCardvalues(cardArray[i]);
    if (cardArray[i].value === 'Ace') {
      hasAce = true;
    }
  }
  if (hasAce && score + 10 <= 21) {
    return score + 10;
  }
  return score;
}

//Update Score
function updateScore() {
  dealerScore = getScore(dealerCards);
  playerScore = getScore(playerCards);
}

function winner(winner) {
  gameOver = true;
  returnWinner(winner);
  showNewGameButton();
}

//Check for a Blackjack
function hasBlackjack() {
    gameOver = true;
    returnWinner('Player has a Blackjack');
    showNewGameButton();
}

//Check for End of Game
function checkEndGame() {
  updateScore(); 

  if (gameOver) {
    while(dealerScore < 17) {
      dealerCards.push(getNextCard());
      updateScore(); 
      dealerCardsParagraph.innerText += `\n ${getCardString(dealerCards[dealerCards.length - 1])}`;
      dealerScoreParagraph.innerText = `Score: ${getScore(dealerCards)}`;
    }
  }

  if (playerScore > 21) {
    dealerWon = true;
    winner('Dealer');
  }

  else if (dealerScore > 21) {
    playerWon = true;
    winner('Player');
  }

  else if (playerScore === dealerScore) {
    gameOver = true;
    returnWinner('No one');
    showNewGameButton();
  }

  else if (playerScore ===  21) {
    hasBlackjack();
  }

  else if (gameOver) {
    if (playerScore > dealerScore) {
      playerWon = true;
      winner('Player');
    }
    else {
      dealerWon = true;
      winner('Dealer');
    }
  }
}


Hi there are a few things you can do, for example instead of using inline styles in js, u can add classes instead using .classlist.add that change the style in css without messong the speficity

Also you can refactor alot of your code, like ur game variables can be put into arrays or objects. Even if u havent learn objects b4 its a good time to learn it for basic usage. Objects are similar to arrays but uses .notation to access variables.

1 Like

I would probably split up the code in several files and use classes/modules to organize the code.

Not sure if your getNextCard work’s as indended. You never remove the card from the deck, you randomise a number and return a card from that index. Could possible return the same card more than once during a full game. You should use the .pop() method to pop a card of the deck, but to do that you need to make a shuffle function, you could look up Fisher-Yates Shuffle

I would probably make the deck a class that has a few functions like “shuffle” “deal” “resetDeck” and make each card an object with the props suit and value, where ace could have an array with [1,11] for value, since it can count both ways.

your functions for winner() and hasBlackjack() looks almost identical, you could make one function out of those.

I would remove the css styling from the JS and just add/remove classes from said node elements. Example, you could have a class for hiding and one for showing.

Overall Black Jack is a fun game to make where you can practise OOP.
If you want to take it further, the house second card should be hidded until the player stops. You could also add functions for betting, double up and split if you want to build on, see how seperate files and classes would keep things more clean here? :slight_smile:

1 Like

Thanks for your replies.

I started some classes tutorials and i managed to write this code so far:

class Deck {
    //Creates the Deck and shuffles it
    createDeck() {
      const suits = ['Hearts', 'Clubs', 'Diamonds', 'Spades'];
      const values = ['Ace', 'King', 'Queen', 'Jack', 'Ten', 'Nine', 'Eight',
        'Seven', 'Six', 'Five', 'Four', 'Three', 'Two'];
      const deck = values
        .map( value => suits
        .map( suit => `${value} of ${suit}`))
        .flat()
        .sort( () => Math.floor( Math.random() * 51 ) );
      return deck;

    }
    

    getCard() {
      let cardNum = Math.round(Math.random() * 51);
      return this.deck[cardNum];
    }
}

I can’t figure out how to access the deck inside the getCard() function

You really need to look at the Fisher-Yates shuffle algorithm. It is one of the only reliable means of shuffling an array of items. Yours is not guaranteed to do that.

Sorting in random order should shuffle as well as Fisher-Yates (though it’s much less efficient). However, this part is completely wrong:

 .sort( () => Math.floor( Math.random() * 51 ) );

The callback to .sort takes two arguments a and b, and only the sign of the result is significant. If it’s negative, a is sorted before b, if it’s positive, then b comes before a, and if it’s zero, the sort order is unchanged. All this is doing is reversing the deck, since the result of the sort function is always positive.

See also https://www.w3schools.com/js/js_array_sort.asp which also shows a working random sort.