in Code

Each week, I offer up a JavaScript code challenge. Want more? You can find others here.

This week’s challenge comes from Coderbyte, which has been a fun resource. The instructions are a bit of a doozy:

Have the function BlackjackHighest(strArr) take the strArr parameter being passed which will be an array of numbers and letters representing blackjack cards. Numbers in the array will be written out. So for example strArr may be [“two”,”three”,”ace”,”king”]. The full list of possibilities for strArr is: two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace. Your program should output below, above, or blackjack signifying if you have blackjack (numbers add up to 21) or not and the highest card in your hand in relation to whether or not you have blackjack. If the array contains an ace but your hand will go above 21, you must count the ace as a 1. You must always try and stay below the 21 mark. So using the array mentioned above, the output should be below king. The ace is counted as a 1 in this example because if it wasn’t you would be above the 21 mark. Another example would be if strArr was [“four”,”ten”,”king”], the output here should be above king. If you have a tie between a ten and a face card in your hand, return the face card as the “highest card”. If you have multiple face cards, the order of importance is jack, queen, then king.

It sounds a bit confusing at first glance, but if you know how to play blackjack, the rules make sense, particularly the rules surrounding aces. Here are some test cases:

  • Input = “four”,”ace”,”ten”; Output = “below ten”
  • Input = “ace”,”queen”; Output = “blackjack ace”

For me, it was helpful to separate out the challenge into a few distinct parts:

  1. Turn the string values into actual numbers.
  2. Disregarding the possibility of an ace, create a function that could return the highest card in the hand.
  3. Disregarding the possibility of an ace, modify the function to also figure out the sum of the hand and whether or not it was above, below, or equal to blackjack.
  4. Deal with the possibility of an ace.

Here’s my solution collapsed:

var cardValue = {
	"two": 2,
	"three": 3,
	"four": 4,
	"five": 5,
	"six": 6,
	"seven": 7,
	"eight": 8,
	"nine": 9,
	"ten": 10,
	"jack": 10,
	"queen": 10,
	"king": 10
};

function BlackjackHighest(hand) {
	// Set variable for sum of hand
	var sum = 0;
	// Set variable for highest card in hand to two
	var highest = "two";
	// Set a result variable (what we"ll return)
	var result;
	// Set two variables for dealing with an ace in the hand
	var acePresent;
	var aceValue;
	// Determine the highest card in the hand including hierarchy of face cards
	for (var i=0; i<hand.length; i++) {
		// Set them both to lowercase so I don"t have to worry about that
		var currentCard = hand[i].toLowerCase();
		highest = highest.toLowerCase();

		// We want to know if the card values are equal so we can establish hierarchy
		var cardsEqual = function () {
			if (cardValue[currentCard] == cardValue[highest]) {
				return true;
			};
		};

		if (cardValue[currentCard] > cardValue[highest]) {
			highest = currentCard;
		} else if (cardsEqual() && highest == "ten" && currentCard == "jack" || currentCard == "queen" || currentCard == "king") {
			highest = currentCard;
		} else if (cardsEqual() && cardValue == "queen" && highest == "jack") {
			highest = currentCard;
		} else if (cardsEqual() && cardValue == "king" && highest == "jack") {
			highest = currentCard;
		} else if (cardsEqual() && cardValue == "king" && highest == "queen") {
			highest = currentCard;
		};

		// If there is an ace, let's know that and move on
		if (currentCard == "ace") {
			var acePresent = true;
		} else {
			sum += cardValue[currentCard];
		};

	};

	// If an ace was present and the sum is less than 11
	if (acePresent == true && sum < 11) {
		// The ace is worth 11 and it's the highest card
		aceValue = 11;
		highest = "ace";
		// Add the ace value to the sum
		sum += aceValue;
	} else if (acePresent == true) {
		// Otherwise, it's worth one
		aceValue = 1;
		sum += aceValue;
	};

	// Determine if we busted
	if (sum < 21) {
		result = "below";
	} else if (sum = 21) {
		result = "blackjack";
	} else {
		result = "above";
	};

	// Build the result and return it
	result += " " + highest;
	return result;	
};

I’ll walk through it in a series of steps:

Turn the string values into actual numbers.

Originally, I used a crazy long set of if statements. Thankfully, my buddy Nate reminded me I could create an object of key/value pairs.

var cardValue = {
	"two": 2,
	"three": 3,
	"four": 4,
	"five": 5,
	"six": 6,
	"seven": 7,
	"eight": 8,
	"nine": 9,
	"ten": 10,
	"jack": 10,
	"queen": 10,
	"king": 10
};

Create a function that could return the highest card in the hand.

Next, I wanted a way to determine the highest card value in the hand so I could return it later and sum the cards. At this point, we’re still not dealing with aces at all. I setup a variable called highest and assigned it the value of ‘two’ to start. I setup another called sum that held the total for the hand. Then, I cycled over the cards. I grabbed the currentCard from the hand and compared it to the value of the highest card. Then, I ran several if statements to tease out the highest card in the hand based on the hierarchy. Finally, I added the value to the sum.

var sum = 0;
var highest = "two";
for (var i=0; i<hand.length; i++) {
	// Set them both to lowercase so I don"t have to worry about that
	var currentCard = hand[i].toLowerCase();
	highest = highest.toLowerCase();

	// We want to know if the card values are equal so we can establish hierarchy
	var cardsEqual = function () {
		if (cardValue[currentCard] == cardValue[highest]) {
			return true;
		};
	};

	if (cardValue[currentCard] > cardValue[highest]) {
		highest = currentCard;
	} else if (cardsEqual() && highest == "ten" && currentCard == "jack" || currentCard == "queen" || currentCard == "king") {
		highest = currentCard;
	} else if (cardsEqual() && cardValue == "queen" && highest == "jack") {
		highest = currentCard;
	} else if (cardsEqual() && cardValue == "king" && highest == "jack") {
		highest = currentCard;
	} else if (cardsEqual() && cardValue == "king" && highest == "queen") {
		highest = currentCard;
	};

	sum += cardValue[currentCard];
};

Modify the function to also figure out the sum of the hand and whether or not it was above, below, or equal to blackjack.

Now that we have the sum, we can figure out whether we’re at blackjack or not.

if (sum < 21) {
	result = 'below';
} else if (sum = 21) {
	result = 'blackjack';
} else {
	result = 'above';
};

At this point, we’re pretty darn close. We just need to figure out what to do with the ace.

Deal with the possibility of an ace.

While we’re cycling through the hand, we want to know whether or not an ace is present. Since the ultimate value of an ace is determined by the sum of the hand, we can’t do anything right away. I established two variables – acePresent and aceValue. Within the original for loop, I added a conditional:

if (currentCard == "ace") {
	var acePresent = true;
} else {
	sum += cardValue[currentCard];
};

If an ace is present, I just want to move on. However, if the card isn’t an ace, let’s add it to the sum. Then, once we have counted all of the cards, I added this if statement outside the for loop:

if (acePresent == true && sum < 11) {
	// The ace is worth 11 and it"s the highest card
	aceValue = 11;
	highest = "ace";
	// Add the ace value to the sum
	sum += aceValue;
} else if (acePresent == true) {
	// Otherwise, it"s worth one
	aceValue = 1;
	sum += aceValue;
};

sum += aceValue;

If an ace is present and the sum of all cards (minus the ace) is less than eleven, the ace is worth 11 and the highest card by default. Otherwise, the ace is worth one.

A big thank you goes out to Nate for pointing out areas of improvement in the comments section!

Leave a Thought

  1. Cool problem here, Your solution looks good. However you should take a look at the Map Data structure to shrink the size. Since the values of the cards are static with the exception of the ace. You can load the name value i.e(two,2) pairs in a Map to avoid the convertToNumber method.

    The reason you are having an issue with the ace value is because you are evaluating the state of the ace separately and receiving more than one ace will always result in both aces being a value of 1. Using some kind of Key value map mentioned above would allow you to change the value of ace for all of the aces easily.

    Hope that helps 🙂

    • I realize I read one of the assumptions as a rule of the game,
      >If an ace is present and the sum of all cards (minus the ace) is less than eleven, the ace is worth 11 and the highest card by default. Otherwise, the ace is worth one.

      This rule has a problem if there are two aces since they can be evaluated separately (right? even though it is a bad move with regular rules because you always want to split two aces I think you can count them as separate values)

      This would make it a little different since the value of an ace is independent from another ace.

    • I ended up turning the terribly long if statement at the top into an object with some key/value pairs that works much better. I also cleaned up the body of the `BlackjackHighest` function because it was looking a bit rough.