Leave a rating/review
Time to put your new Combine skills to the test, and make a handy blackjack card dealer while you’re at it. In case you’re not familiar with it, blackjack is a card game where the goal is to get 21 — or as close as possible without going over, which is called getting “busted.”
If that’s not the biggest oversimplification of the game of blackjack, I don’t know what is. But it’s enough for our purposes.
The starter playground for this challenge implements a passthrough subject to model a hand of cards.
let dealtHand = PassthroughSubject<Hand, HandError>()
In the Sources folder for this playground there is a SupportCode file. It contains code to create an array of tuples to represent a standard deck of cards, including the emoji character for each card and its point value.
public let cards = [
("🂡", 11), ("🂢", 2), ("🂣", 3), ("🂤", 4), ("🂥", 5), ("🂦", 6), ("🂧", 7), ("🂨", 8), ("🂩", 9), ("🂪", 10), ("🂫", 10), ("🂭", 10), ("🂮", 10),
("🂱", 11), ("🂲", 2), ("🂳", 3), ("🂴", 4), ("🂵", 5), ("🂶", 6), ("🂷", 7), ("🂸", 8), ("🂹", 9), ("🂺", 10), ("🂻", 10), ("🂽", 10), ("🂾", 10),
("🃁", 11), ("🃂", 2), ("🃃", 3), ("🃄", 4), ("🃅", 5), ("🃆", 6), ("🃇", 7), ("🃈", 8), ("🃉", 9), ("🃊", 10), ("🃋", 10), ("🃍", 10), ("🃎", 10),
("🃑", 11), ("🃒", 2), ("🃓", 3), ("🃔", 4), ("🃕", 5), ("🃖", 6), ("🃗", 7), ("🃘", 8), ("🃙", 9), ("🃚", 10), ("🃛", 10), ("🃝", 10), ("🃞", 10)
]
Aces are high, so an Ace of Spades has a value of 11 compared to a Queen of Hearts which has a value of 10. There are also two type aliases — Card and Hand, to model, well, a card and a hand of cards.
public typealias Card = (String, Int)
public typealias Hand = [Card]
An extension on Hand provides two helper computed properties — cardString and points. cardString will join together a hand of cards using their emoji characters. And points will return the sum of a hand’s cards’ points.
var cardString: String {
map { $0.0 }.joined()
}
var points: Int {
map { $0.1 }.reduce(0, +)
}
Finally, there’s an enum to model a HandError, in this case there’s only one error: Busted!.
case busted
HandError conforms to CustomStringConvertible and returns the string Busted when you print the busted error.
public var description: String {
switch self {
case .busted:
return "Busted!"
}
}
Back in the main playground page, there’s also a deal function that will create a hand of the number of cards that you pass in for the cardCount parameter.
func deal(_ cardCount: UInt) {
var deck = cards
var cardsRemaining = 52
var hand = Hand()
for _ in 0 ..< cardCount {
let randomIndex = Int.random(in: 0 ..< cardsRemaining)
hand.append(deck[randomIndex])
deck.remove(at: randomIndex)
cardsRemaining -= 1
}
Your first challenge is to add code immediately below the comment // Add code to update dealtHand here that evaluates the result returned from the hand’s points property. If the result is greater than 21, send the HandError.busted through the dealtHand subject. Otherwise, send the hand value.
// Add code to update dealtHand here
Next, add code immediately below the comment // Add subscription to dealtHand here to subscribe to dealtHand and handle receiving both values and an error.
// Add subscription to dealtHand here
For received values, print a string containing the results of the hand’s cardString and points properties. For an error, just print it out. A tip though: You can receive either a .finished or a .failure in the receivedCompletion block, so you’ll want to distinguish whether that completion is a failure or not.
The call to deal(_:) currently passes 3, so three cards are dealt each time you run the playground. In a real game of Blackjack, you’re initially dealt two cards, and then you have to decide to take one or more additional cards, called hits, until you either hit 21 or bust. For this simple example, you’re just getting three cards straight away.
See how many times you go bust versus how many times you stay in the game. Are the odds stacked up against you in Vegas or what? The card emoji characters are pretty small when printed in the console.
So you can temporarily increase the font size of the Executable Console Output for this challenge if you want to be able to see them better. To do so, select Xcode ▸ Preferences… ▸ Fonts & Colors/Console. Then, select Executable Console Output, and click the T button in the bottom right to change it to a larger font, such as 48.
So for task 1 it will go something like this: if points is greater than 21, send busted. Otherwise, send the hand.
And for task 2, you’ll subscribe to dealtHand and handle receive values and a failure event that contain an error. Alright that’s it. So, pause the video now and good luck!
Ok, here’s what I came up with. For the first task, I used an if-else statement, and if the result of hand.points is greater than 21, I send the busted error in a failure completion event. Otherwise, the hand did not bust and is good, so send it on dealtHand.
if hand.points > 21 {
dealtHand.send(completion: .failure(.busted))
} else {
dealtHand.send(hand)
}
For the second task, I subscribed to dealtHand, and in the receiveValue handler I printed the hand’s cardString and points values. And if an error occured, in other words, the hand busted, I just printed out the error.
_ = dealtHand
.sink(receiveCompletion: {
if case let .failure(error) = $0 {
print(error)
}
}, receiveValue: { hand in
print(hand.cardString, "for", hand.points, "points")
})