OS X games from the ground up: solution to challenge project 1

You can see an index of all the posts in this series: go to index.

In the previous post I set you the challenge of writing a C version of the BASIC game Acey Ducey. If you managed to get a working programme, consider that a significant achievement – although the game is simple, the challenge is not.

In this post I’ll walk you through my solution. My solution will almost certainly differ from yours in several respects, especially as I’ll be using this solution to introduce some new concepts to you. However, it’s always the case with programming that there will be many different possible approaches to any problem – provided that your code works and is easy to understand and maintain, then your solution is valid.

Here’s the built programme:Acey Ducey.

Here are the project files for the finished solution:challenge project 1.

Here’s the code in full:

I’ve created four functions, other than main, for this project. You can see the declarations for these functions in lines 18 to 21. Remember that you have to declare all functions you create before you call them. We’ll look at the detail of each of these functions later in this post.

Note that I’ve also taken the parameters out of this version of main. Getting command line arguments in main is optional. It won’t hurt to leave them in, but removing them from the function definition lets other people viewing the code know that they’re not used.

The next thing you should note is that the main function itself does not contain much code.  As you begin to write more complex programmes, you should find this is generally the case. Most of your programme’s functionality will be put into functions and main‘s job will be to do a bit of initialisation and then call one or more of these functions.

I have main displaying the instructions on line 27, as that is done once, when the programme is first entered, initialising some variables on lines 29 and 30, which I’ll explain in a moment, seeding the random number generator on line 33, and then entering a loop, between lines 35 and 44, which plays the game as many times as the player wishes.

Note that the entire game logic is contained in a single function called runGame. When this function is called on line 37, it plays a single game, then returns when the game is over. It’s always a good idea to break your programme up into functions representing logical chunks. It makes it easier to read and maintain.

When a game has finished, we need to ask the player if they want to  play again. Unlike Buzzword, which looks for a single letter ‘y’, Acey Ducey wants the answer to be “yes”. If you implemented your solution to look for a single letter, that’s fine. In my solution, I show you how to do this when you want to scan for a complete word.

In line 29, we create a character array that we will use to store the raw input. In line 30 we create another character array that we will use to store the word that we’ll extract from that input.

In line 41 we use the fgets function, as we did with Buzzword, to get all the player’s input into the rawInput array. In line 42 we use the sscanf function, once again, to extract the bit we want from the player’s input and store it in the inputString array. Note the initial space in the format string and recall that this will get sscanf to ignore any white space that comes before the word. There are two differences from the way we used this function in Buzzword. The first is that I’ve used the conversion specifier %s instead of %c because we are now scanning for a full string rather than a single character. You’ll also note that the name of the variable, inputString, doesn’t have the reference operator, &, in front of it. Why is this?

You’ll remember that, when we pass a character variable as an argument, what actually gets passed is the value in that variable. But sscanf wants the address of the variable, not the value, because it is going to place a value at that address. When we pass an array as an argument, however, we don’t pass all the values in the array – we pass a pointer to the first element of the array. In this case we do want the variable’s value because that’s the address of the array. This is why we don’t use the reference operator, &, when we are using sscanf to read a string. This diagram shows the difference between an argument passed by value, e.g. a char variable, an argument passed by an explicit reference, e.g. a char variable with a reference operator, and an argument passed by implicit reference, e.g. a char array:

reference and value arguments

Note that, once sscanf finds the start of a word, it will only read as far as the first white space character, e.g. a space. So even if the player were to enter something like “yes I’d like to play again”,  after the sscanf function, only “yes” would be stored in inputString.

Now we need to check if the player has entered “yes”. This is done with the while command on line 44. You might have been expecting to see something like while (inputString == “yes”). Recall from earlier in this post that inputString is an array and when we use the name of an array as an argument, it is passed by reference, not value. In other words, we would be trying to compare a pointer to the first element of the array with a string literal. This just doesn’t make sense. Be aware that C will quite happily let you try to do this, but thankfully Xcode will warn you if you do.

Instead we use a new function, strncmp, which, now you’re getting used to C naming conventions, you have probably worked out is short for string compare. The strncmp function is part of the string library, so I’ve imported this on line 12. This function has three parameters. The first two are either pointers to strings or string literals to be compared (although obviously only one would ever be a string literal as there wouldn’t be much point in comparing two string literals). The third parameter is the maximum number of characters to compare. Note there is a related function, strcmp, which doesn’t have this third parameter. I’ve used strncmp and limited the characters to 3, so that a response like “yessir” or “yesssss” would also match.

The result returned by strncmp (or strcmp) is a negative number if string 1 is less than string 2, 0 if the strings are the same and a positive number if string 1 is greater than string 2. How is “greater than” or “less than” calculated in this context? Each character is actually represented by a number. The actual number used depends on the text encoding standard used by the system on which the machine runs. For the sake of this discussion, let’s assume the encoding standard is one called ASCII (American Standard Code for Information Interchange). In ASCII, the letter ‘a’ is represented by the number 97, ‘b’ by the number 98, ‘c’ by 99 and so on. The strncmp function compares the strings one character at a time until it finds a difference. For example, “cat” is less than “dog” because the value of the first character in “cat” is 99, but the value of the first character in “dog” is 100. Note though that “BIG” is actually less than “big” because the value of an upper case ‘B’ is 66, while the value of a lower case ‘b’ is 98.

Because strncmp returns a zero when the strings are equal, and a zero result for a condition is false, note that I have used the logical NOT, !, operator to negate the result returned by strncmp. This means the programme will loop again if the strings are equal and exit if not.

In that discussion we discovered that “BIG” is not the same as ‘big’ as far as strncmp is concerned. The same will go, for “yes”, “Yes” and “YES”, which are all considered to be different strings. Clearly though, the programme should accept all of them. We could do something like this:

But not only is that quite an unwieldy and error-prone statement, it also doesn’t cater for mixed case responses like “YEs” or “YeS”. You can easily work out that there are eight different combinations of upper and lower case letters we’d have to check for. A more elegant solution is to use a function to convert any upper case letters to lower case first, then we only have to check for a single response, “yes”.

This function is declared on line 21 and defined on lines 169 to 174. Here’s what it looks like:

Note that this function has a single parameter, which is the character array to be converted. Because the function modifies the character array directly, it doesn’t return a value, so it’s return type is void.

Inside the function, we loop through all the characters in the string. Previously, when we have used a for loop, you’ve seen loop conditions that look something like i  < numberOfPhrases, meaning that the loop continues while i is less than the value of the variable numberOfPhrases, but here our condition is string[i]. What’s going on here?

Well, recall that, when evaluating a condition, zero is taken as false and a non-zero number is taken as true. In this loop, each time we get to the condition, string[i] gets the value of the character at position i. Let’s assume that our string contains the text “yes”. The first time through the loop i is equal to 0, so we will get the character at position 0, ‘y’, which has the value 121. That’s non-zero so we repeat the loop. Next time through, i is 1, and position 1 has the character ‘e’ with the value 101, so again we repeat the loop. Third time through i is 2, and position 2 has the character ‘s’, which has a value of 115, so once again we go through the loop. On the next iteration i is 3. Wait a moment, you might be saying, there is no character at position 3. But remember, all strings are terminated with the null character, ‘\0’, which happens to have a value of 0. So at this point the loop ends. This is a convenient facet of strings – every character in a string is guaranteed to have a non-zero value, except for the terminating null character at the end of the string. Note though that this only works because the condition is always tested at the top of a for loop, so execution ends as soon as the null character is found. If you tried to do the same with a do … while loop, it wouldn’t work, because the character would already have been processed by the time you checked it.

Within the for loop we make use of another C library function, tolower, which returns the lower case equivalent of the character passed as an argument. So ‘y’ and ‘Y’ would both be returned as ‘y’. If you pass a non-alphabetic character into tolower, e.g. ‘%’, that character will be returned unchanged. The tolower function is part of the ctype library, which is included on line 13.

We call this function on line 43, just before the loop condition is tested, like this:

Note that this function is only called once, so you might be wondering why I bothered to use a function at all. Why not just include the code to convert to lower case directly at this point? Firstly, as I mentioned before, it’s always a good idea to split your programme up into logical pieces, and for each of those pieces to live in its own function. Secondly, converting a string to lower case is something I’m likely to do again in future programmes. If I have that functionality nicely wrapped up in a function, it’s much easier for me to reuse it again in other programmes.

If the player choses not to continue the game, by entering a value other than “yes”, we display a farewell message and end the programme with an error code of 0, indicating success.

So that’s main‘s part in all this taken care of – now let’s look at the function runGame, which is responsible for the main game logic.

I begin by declaring and initialising a couple of variables, a char array called rawInput, which will be used to capture the player’s input when they bet, an int called bet, which will hold the numeric value of the bet and an unsigned int called money, which holds the amount of money remaining to the player.

You might have noticed that we also have a char array called rawInput in the main function, but remember, from the discussion on variable scope in an earlier post, that variables declared within functions are local to those functions. As far as C is concerned, the rawInput in main and the rawInput in runGame are two entirely different variables.

Most of the rest of the function is contained in a while loop from line 60 to 136. This checks that the player still has money remaining and continues picking cards and taking bets while they do have money. Once the player is out of money an ‘out of cash’ message is displayed, on line 137 and the function ends (and returns to main).

Each time through this loop, the first thing to do is show the player how much money they have left.

Next we pick the first card, which is stored in the unsigned integer variable called cardA:

Note that I’ve created another function for picking a card, called pickCard. This is because we need to pick three different cards each time through the game, so it make sense to have this functionality wrapped up in a function.

The pickCard function, which starts on line 142, looks like this:

This code should look very familiar to you by now. We are picking at random from a range of 13 cards, but starting them with a value of 2, so the actual range will be 2 to 14, with 11 representing a Jack, 12 a Queen, 13 a King and 14 an Ace (Aces are high in this game). Now we return to the runGame function:

This next section of code might look a little odd, so let’s look at it in detail. We start by initialising the second card, cardB to be the same as the first card, cardA. What we now want to do is replace the current cardB with randomly picked cards until cardA and cardB are different. This is achieved by the loop between line 69 and line 71. Note that when you have a single statement in a loop like this, (or between the braces in an if or if … else statement), you can optionally omit the braces and do something like this:

In my opinion, that is not very readable code, so I always use braces, even if the braces contain only a single statement. It’s up to you whether you chose to omit braces for single statements, but whatever you decide, be consistent.

Once cardA and cardB are different, we then have to get them into the correct order, so that cardA is the lower of the two. This is achieved with the if statement from lines 74 to 79. If cardB is the lower card, this simply swaps the two cards over. Note that we have to use a third temporary variable here, called tmp, to preserve the value of cardA while we copy cardB to cardA.

The next bit of code converts the card values into card names. Again, I have created a function to do this, because the same thing has to be done for three cards for each bet. Note that I create two arrays to hold the card names in lines 82 and 83 and these are passed to the getCardName function in lines 84 and 85, along with the value of each card. The function is declared on line 20 and defined on lines 148 to 166.

The function takes two parameters  – the first is the numeric value of the card, the second is a pointer to the character array where the name of the card is to be stored. This should have space for at least six characters. Why six, when the longest card name, “Queen” only has five? Remember that all strings are terminated with a null character, ‘\0’, so we need to allow space for that as well.

If the card value is less than 11, i.e. one of the cards from two to ten, the number of the card will also be its name. To get this into our cardName array, on line 151 we use  a variant on the printf function called sprintf. The sprintf function is to the printf function, what the sscanf function is to the scanf function. In other words, instead of sending its output to stdout, like printf does, it sends it to a string. A pointer to the destination string is the first parameter, and then the remaining parameters work in exactly the same way as the parameters in printf. You probably won’t be surprised to learn that sprintf is part of the stdio library.

The switch command that follows else on line 152 is new to you.  Switch can often be used as a more compact alternative to a long chain of if … else if … else statements. What switch does is follow one of several paths depending on the value of the expression in parentheses. In this case the expression we are checking is simply the value of card. The different paths are enclosed within a pair of braces, and each path begins with the keyword case, followed by a value and then a colon. So you can see from lines 153, 156, 159, and 162 that we are checking for the cases where the value of card is  11, 12, 13, and 14, i.e. the values of all the picture cards and the Ace. You may also have a final path called default: (note this doesn’t have the word case in front of it). This path will be selected for any value not already matched by one of the other cases. For this function though, we don’t need the default case.

In each case we call another function that’s new to you, called strcpy. It should take too much effort to work out that this is short for string copy. This function is defined in the string library and takes two parameters, a string pointer and then either another string pointer or a string literal. The function copies the contents of string 2 into string 1. You must always take care with this function that there is sufficient room in string 1 for the string you are trying to copy, if not you could overwrite other important values in memory and cause your programme to stop working. We use this function to copy the relevant card name into the cardName char array.

You’ll notice beneath each of these statements there is another unfamiliar command, break. The break command causes the switch statement to end. It’s needed because, without it, control would fall through to the cases following the current one. In other words, without the break statements, every card would end up being called “Ace” because regardless of which case was initially executed, all the subsequent cases would also be executed. Strictly speaking, the final break statement on line 164 is not needed, because the switch statement ends at this point anyway. However, it’s always a good idea to include it, because you may add more cases at a later date and forget to put the break statement back in. This diagram should help clarify how the switch statement works:

switch command

Now, you might be wondering why this function had a second parameter – an array pointer for the character array where the string is to be stored. Why not simply return a string? Wouldn’t it have been better if our function looked something like this:

And then in runGame, we could get the cardNames like this:

To answer this question, we need to take a more detailed look at how the stack works. You’ll remember from an earlier post that the stack is an area of memory where variables are stored. So let’s consider what happens as we create variables in this programme. The first function to be run is main. In main we declare two variables, rawInput and inputString. After these variables have been declared, the stack looks like this:

stack explanation 1

A little later we call runGame from main and runGame begins declaring its local variables. By the time we are about to call getCardName, the stack looks like this:

stack explanation 2

Note that main‘s variables are still there, because main is still running. Even though we have two char arrays called rawInput in the stack, they are local variables belonging to different functions, so C considers them to be different. Any reference to rawInput in runGame will be taken to be a reference to runGame‘s variable, not main‘s.

You might be able to see now why this part of the memory is called the stack, because variables are stacked in order as they are created.

So now we call our new version of getCardName and it declares its local variable, which is a char array called cardName. The stack now looks like this:

stack explanation 3

So far, so good. At this point getCardName does its job of putting the relevant name into it’s local variable cardName. Then it returns a pointer to cardName. This brings the getCardName function to an end, so C tidies up by removing any local variables owned by getCardName. So now control has returned to the runGame function, which now tries to copy the string pointed to by the returned pointer into cardAName. But guess what, that string was stored in cardName, which has just been deleted by C. So the return value points to a variable that no longer exists:

stack explanation 4

Now, what can cause further complications with this sort of erroneous programming, is that, often C will mark the memory occupied by cardName as being free for reuse, but it won’t actually erase what was there. So sometimes, especially in cases like this where you are using the returned pointer straight away, the data it points to will still be intact, even though, as far as C is concerned, the variable no longer exists. What this means is that the programme could appear to work, but when you run it on a different machine, or a different operating system, or compile it with a different compiler, it can then suddenly fail for no apparent reason. But this will most likely be because, in the different environment, the data has been overwritten.

This is why you should never attempt to return pointers to local variables. Fortunately Xcode will always warn you if you try to do this, but it’s worth understanding why it’s a bad idea. As you will discover in future posts, pointers are a very powerful feature of C. But, as a certain superhero was once told, “with great power comes great responsibility”. That’s certainly true of pointers. They can be dangerous if not used with care. The issue we’ve just been looking at is known as a “dangling pointer” and is one of many ways in which pointers can get you into trouble. We’ll look at some others in later posts.

Compare this to our original version of the getCardName function, where we ask the calling function, i.e. runGame, to provide its own location for the string to be saved. Think of it like taking your own sturdy bags to the shop instead of relying on the flimsy paper bags the shop provides – the paper bag might get your shopping back safe, but it could just as easily break and you’ll lose your groceries. We know our version of sturdy bags, the variables created in runGame, will exist before the getCardName function is called, will exist while the getCardName function is running and will still exist when the getCardName function returns. It’s a much safer strategy. So now we continue with the runGame function:

The next thing we do is display the card names to the player. Now, the only thing I ever use the card names for is to display them to the player like this and each card only ever gets displayed once. So you might be wondering why I didn’t just write a function that displays the appropriate card name when it’s passed the card value, something like this:

While that’s a functionally valid solution, there are two reasons why I chose not to do it that way. The first has to do with reusability. It’s highly likely that I will create more card games in the future. In other card games I create I might not simply want to display the card name. I might, for example, want to combine the name of the card with another string containing the suit. A generalised function that returns the card name as a string is much more likely to be reusable in a future programme than one that displays the card name directly. As you will have already discovered, programming is an intensive and time consuming task. It’s often worth spending a bit more time to make your code reusable today, if it saves you twice as much time not having to reinvent the wheel tomorrow.

The second reason relates to how the different parts of your programme work together. You might think of this programme as having two fundamental parts. One part is the model – this is the data, such as the card values and the bet amount and the logic to deal with that data. The second part is the interface – this is the code to display information to the player and to get the player’s input. Best practice is to separate those two parts as much as you possibly can, and the more complex your programme is, the more important it is to maintain that separation. Obviously they can’t be completely separate – at some point your model needs to talk to your interface and vice versa, but you should try to make those communication points between the two as well-defined and as minimal as possible.

Why bother to do that? Well, at the moment you are creating programmes that run in a command line interface while you develop your skills. At some point in the near future your skills will be sufficient for you to progress to developing programmes that have a graphical user interface. At that point you might think “you know, wouldn’t it be great to have a version of that Acey Ducey programme that used images of the cards rather than just text.” Now if the code for the model and the code for the interface are horribly tangled up you may well just decide to give up and start developing the graphical version of your programme from scratch. But if you’ve been careful to maintain as much separation as possible between the model and the interface, it’s probably not going to be too difficult to unpick the text interface code and replace it with the code for the graphical interface. All being well, you will have very little additional work to do on the model code. This is an important concept in programming and we’ll be coming back to it again in future posts.

Now we’ve displayed the first two cards to the player, it’s time to get the player’s bet. There will be a number of checks we need to make on the player’s input to validate it, so we need a way of knowing when we have valid input. I’m going to track this with a type of variable you’ve not seen yet. It has the type bool, which is short for Boolean. Boolean variables are used in Boolean logic, which was invented by the 19th Century mathematician George Boole. You’ve already seen Boolean logic at work in the conditions we’ve used in if, while and for statements. Expressions in Boolean logic always evaluate as either true or false. You’ve seen that C considers zero to be false and any non-zero value to be true, however, you can also store these values in a bool variable.

Because bool is a late addition to the C language, you need to import the standard bool library, stdbool, if you want to make use of it. I import this on line 15. As well as defining the bool type, this also defines the values true and false. You can see an example of this on line 91 where I declare the bool variable validBet and initialise it to false.

Lines 94 to 111 are a loop that repeats while the bet is not valid – note that because I am testing for a false condition, I use the logical NOT operator, !.

Notice that I am combining the fgets statement that’s familiar to you, with the code we previously used to get a number from a command line argument, using the strtol function. There are three possible outcomes from this function:

  1. The user entered either a non-numeric value or a negative number. We test for this on line 104 and display an error message if this is the case.
  2. The user tried to bet more money then they have available. We test for this on line 106 and display an error message, including a reminder of the available amount, if this is the case.
  3. The user entered a valid bet. If this is the case, we set validBet to true and the loop exits.

Once we have the player’s bet, it will either be zero, indicating they don’t want to bet, or the amount they want to wager. We test for this on line 114, and if the bet is zero, we just display the “chicken” message and return to the top of the loop for the next round.

If the player has chosen to bet, we pick a third card and display its name. This code, between lines 118 and 125, should be familiar to you now, as it is almost very similar to the code for the first two cards. Finally, in lines 128 to 134, we check if the player has won or lost, display an appropriate message and either add or deduct the amount of the bet.

Lines 130 and 133 contain two operators you’ve not seen yet. This line:

is equivalent to this:

Assigning a variable with the value of itself added to another value or variable is something you will do a lot, so over time this operator will save you a fair few keystrokes. It’s also less error prone because you only have to type the destination variable once. As you will have worked out by now:

is equivalent to:

These are known as compound assignment operators. You can also use *=, /= and %= for multiplication, division and modulus operations respectively.

Well that’s been a lot to take in for one post, but hopefully it’s opened your eyes to some of the considerations you need to make when creating games in C. In the next post in this series we’ll begin developing a new game that will introduce you to some more advanced C skills. If you’d like a challenge in the meantime, see if you can use what you’ve learned in this post to make some improvements to your own version of Acey Ducey.

This entry was posted in Games, Programming Tutorials and tagged , , , , , . Bookmark the permalink.

Leave a reply