First steps with Swift: Drawing from the deck

So previously we’ve created a simple deck of cards. Now its time to go a bit further and actually do something useful with those. Lets add the Hand!

We’re still doing the kata, of which the rules say the following about a hand:

A poker hand consists of 5 cards dealt from the deck

Cool, so cards are dealt from the deck! Bet you didn’t saw that one coming 🙂 How do we capture this in a test?

What we can do is test if the number of cards go down when we draw a card. Let’s take our singleDeck() of cards, draw a card, and test if the amount of cards go down:

How would we make that test pass? Easy enough:

Oh wait! Remember from the previous blog post that when we are mutating, we should use ‘var’ and not ‘let’ right? The compiler will tell us that cardsInPile is immutable, so let’s change it to var.

Now, we don’t get a card back, let alone test if that card is actually removed from the deck. It’s not very useful yet. Let’s try to improve on that by writing another test, that allows us to verify if a card is still in the deck or not.

Next, our implementation, which is pretty straightforward. Array has a contains function which takes a closure which provides an element and returns a boolean:

We can simplify this by refactoring a bit. We can actually make use of a protocol called Equatable. If we make Card adopt Equatable, we can move this piece of code there. Lets create an extension on Card and adopt Equatable:

XCode will give an error, and will tell us to implement the curious looking function ==(lhs: Card, rhs: Card) -> Bool. How cool is this!? We can actually define the == operator for our Card!

Having implemented this, we can now simplify our contains function:

Cool! Now we can also check if the card we’ve drawn is no longer in the deck:

For that to work we need to rewrite our drawCard() method a bit, as we now need to return a card. Theres a little caveat, because what if the deck has been emptied? Lucky we can define this in Swift, returning an optional Card (Card?). When we don’t have any cards, we just return nil, else we return the card we remove:

Our test needs to deal with this as well. Our card is now of type Card?, not Card. The contains method takes a Card, so how do we match that up? There are two ways to do it; the safe way and the unsafe way. Since this is a test, we want a failure as soon as possible, so we’ll go with the unsafe way; we force cast our optional Card? to a Card by using an exclamation mark:

Great! The test passes! We’ll discuss how to go about optionals in a safe way later, as force casting an optional that is nil will result in a crash. Something we would really like to avoid in our real application.

Note that the compiler currently warns us that for the testDrawCard() test, the result of call to ‘drawRandomCard()’ is unused. We can easily fix this by prefixing the method with @discardableResult:

Running the test again will now no longer show the compiler warning, awesome!

So let’s recap. We can draw a card and check if a card is in the deck (or not), but we still always get the first card that is still in the deck. We would first get a ♦️2, then ♦️3, etc. To get a random card, we need to either randomise our draws or shuffle our deck. Intuitively, randomising our draws seems least effort. We just pick a random number the size of the deck and take that card out of the array. But how would we test it? Random does imply that you don’t know which card comes next, right? Well, we can go around that by using the same seed for the random number generator in our test. We’re going to utilise GKARC4RandomSource and as you can see, it takes a Data object as a seed. Let’s define our test:

Let’s see if we can change the code to accommodate this. Our singleDeck() method doesn’t change much, we add a seed parameter of type Data?, and we default it to nil:

Next, we add the seed parameter to the initialiser, also defaulting to nil. We then add a property randomNumberGenerator of type GKARC4RandomSource, which we create in the initialiser using the seed. The so-called nil coalescing operator (??) picks between our optional parameter or the current date if our optional parameter is nil:

Finally, we are going to change our drawCard() method. We ask the random number generator for a new Int at every draw. Notice that we pass in an upper bound that is the size of the array of cards, this makes sure that we pick a number in the right range, and not for example 53 while our deck is only 52 cards:

And now the test passes again, great!

But how do I knew what cards to check against? I didn’t. I tried the test and printed randomCards to see what the outcome was, which arguably isn’t that good of strategy. So how can we make sure it’s actually random? We could try different seeds and check if the cards are different:

Pfew, the test passes, nice! Testing for random is generally not a good idea, as you can never know what to test against. There are some techniques to help test code that use random numbers, but that involves a more advanced technique called mocking. I probably won’t cover mocking in this series.

That is it for today! As you can see, we took some smaller steps and some bigger steps. TDD is all about thinking up that next step. If you feel confident, you can take a bigger step. If you feel that you’re not in control or working on something intricate, you can choose to take smaller steps. Meanwhile, we’re getting to know Swift better and better.

Next time we’re going to take a look at how we can evaluate hands of cards!

Leave a Reply