Hey there, Go enthusiasts! How's the roadmap to that desired job going? Today we're covering an interesting exercise I found on exercism.
We'll solve the first turn of a BlackJack game. In this exercise, we're covering Go fundamentals and the underestimated switch statement, which isn't used frequently—even in technical interviews.
Problem Statement
Simulate the first turn of a BlackJack game. You'll receive two cards and see the dealer's face-up card. All cards are represented as strings like "ace", "king", "two", etc.
The value of each card is:
Card | Value |
---|---|
ace | 11 |
two | 2 |
three | 3 |
four | 4 |
five | 5 |
six | 6 |
seven | 7 |
eight | 8 |
nine | 9 |
ten | 10 |
jack | 10 |
queen | 10 |
king | 10 |
other | 0 |
Note: Commonly, aces can take the value of 1 or 11 but for simplicity we will assume that they can only take the value of 11.
Depending on your two cards and the card of the dealer, there is a strategy for the first turn of the game, in which you have the following options:
- Stand (S)
- Hit (H)
- Split (P)
- Automatically win (W)
Although not optimal yet, you will follow the strategy your friend Alex has been developing, which is as follows:
- If you have a pair of aces you must always split them.
- If you have a Blackjack (two cards that sum up to a value of 21), and the dealer does not have an ace, a face card (Jack/Queen/King) or a ten then you automatically win. If the dealer does have any of those cards then you'll have to stand and wait for the reveal of the other card.
- If your cards sum up to a value within the range [17, 20] you should always stand.
- If your cards sum up to a value within the range [12, 16] you should always stand unless the dealer has a 7 or higher, in which case you should always hit.
- If your cards sum up to 11 or lower you should always hit.
Breaking Down the Problem
At first glance, the problem looks complex because there are too many constraints and scenarios. But if you break it down as we've been doing throughout this series, you'll find it's not as hard as it looks.
Let's start by identifying our input. We'll receive three strings with card representations: two for our cards and one for the dealer.
Next, we need to determine each card's value—its number representation—so we can sum them up and decide our move.
Finally, the desired output is the first turn move based on Alex's strategy from the problem statement.
This algorithm consists of two crucial steps: convert the input strings to their number representations, and determine which move corresponds to them.
Parse Input Cards
The first step is converting input string cards into their number representation. While the exercism exercise focuses on learning how to work with switches, we're using maps to simplify this solution. In the next part, we'll use the switch statement as intended in the original exercise.
Let's define the ParseCard function using a map with string keys and int values:
func ParseCard(card string) int {
return map[string]int{
"ace": 11, "two": 2, "three": 3, "four": 4, "five": 5,
"six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10,
"jack": 10, "queen": 10, "king": 10,
}[card]
}
And voilà! You've solved the first part in just one line. There are other approaches for this function, but I think this is the most effective way to demonstrate the use of maps.
For example, you can use constants if you want to impress your interviewer:
const (
Ace = "ace"
Two = "two"
Three = "three"
// ... another values
)
var cardValues = map[string]int{
Ace: 11,
Two: 2,
Three: 3,
// ...other values
}
func ParseCard(card string) int {
return cardValues[card] // default value is 0 for a int in Go
}
Algorithm to Solve the First Turn
Now that we've mapped each card name to its corresponding value, we need to determine our first move.
We'll create a simple switch statement with the different constraints from the problem statement.
First, we need to parse our input cards.
parsedCard1 := ParseCard(card1)
parsedCard2 := ParseCard(card2)
parsedDealerCard := ParseCard(dealerCard)
Now that we know how much each card is worth, we need to sum the values of our two cards to check if it's a blackjack.
sumCards := parsedCard1 + parsedCard2
Now, with the elegance and magic of Go's syntax, let's validate each scenario to determine our first move. The first constraint is if you have a pair of aces, you must split
—this means if our cards sum to 22 (each ace values 11), then our first turn is a split and we should return the letter "P"
.
switch{
case sumCards == 22:
return "P"
}
The second constraint: if you have a Blackjack (21) and the dealer doesn't have an ace, face card (Jack/Queen/King), or ten, you automatically win. In other words, if you have 21 and the dealer's card is less than 10, you win.
case sumCards == 21 && parsedDealerCard < 10:
return "W"
However, if you have 21 and the dealer has any of these cards (an ace or a face card), you must stand this turn.
case sumCards == 21 && parsedDealerCard >= 10:
return "S"
Next, if your cards sum to a value within the range [17, 20], you should always stand.
case sumCards >= 17 && sumCards <= 20:
return "S"
If your cards sum to a value between 12 and 16, you should always stand—unless the dealer has a 7 or higher, in which case you should hit.
The first condition looks like this:
case sumCards >= 12 && sumCards <= 16 && parsedDealerCard >= 7: //dealers card equal or higher than 7
return "H"
The next condition handles when the dealer's card is less than 7:
case sumCards >= 12 && sumCards <= 16 && parsedDealerCard < 7:
return "S"
If your cards sum to 11 or lower, you should always hit. This means any uncovered scenario equals a hit, since we've covered all possible scenarios. So our default is "H"
.
default:
return "H"
And voilà! You've created an algorithm to calculate the first turn in a Blackjack game. You've also learned about maps, switch statements, and conditionals.
Final Thoughts
This exercise demonstrates how breaking down complex problems into smaller, manageable steps makes them much easier to solve. What initially seemed like a complicated set of rules turned into a straightforward algorithm by identifying the key components: parsing card values and evaluating conditions systematically.
The switch statement in Go proves to be incredibly powerful for handling multiple conditional branches in a clean, readable way. Combined with maps for value lookup, we created an elegant solution.
As you continue your Go journey, remember that most real-world problems can be tackled the same way—break them down, identify patterns, and leverage Go's simple yet powerful features.
Keep practicing, keep coding, and most importantly—have fun with it! If you enjoyed this exercise, explore more challenges on exercism to continue building your Go skills. As always, you can find the complete code in my GitHub repo: https://github.com/RubenOAlvarado/algorithms
Happy coding and see you in the next one!
Top comments (0)