OS X games from the ground up: functions and random numbers

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

If you are starting from this point, or need a fresh set of files, here are the starter files from the end of the previous post: more fun with variables and arrays

In the previous post you learned what pointers are and how to use them to create an array of strings. I also left you with a challenge. The challenge was to display the phrase “flexible cognitive algorithm” using the string arrays you created in the previous post. If you completed the challenge successfully, your code should now look similar to this:

When you run this programme you should see this output:

String array challenge

Before we carry on building our programme we need to introduce an important new concept: functions. A function is some code that another part of the programme can ask to do a particular job.

You’ve already seen one example of a function, printf. The printf function displays information on the standard output device. When we’re running the programme within Xcode, the standard output goes to both the debug pane at the bottom of the window and the log (which we can view in the Log Navigator).

We get a function to do some work for us by putting its name into the programme at the point we want to use it. The name is followed by parentheses and the parentheses include any parameters (or arguments) that we want to send to the function, separated by commas. This is known as calling a function.

Some functions, like printf, simply do their job and then return control to the code that called them. Other functions do their job and then return a value to the code that called them. Like variables, functions that return values always return values of a particular type, such as int, float or char.

In a later post you’ll learn how to write your own functions. C also comes with lots of ready made functions, of which printf is one.

These functions are organised into logical groups, called libraries.  Whenever you want to use a function from a particular library, you have to let c know. If you look towards the top of your programme you’ll see this line:

This line tells C that we want to make use of a library called stdio, which contains standard input/output functions like printf.

Why do you need to know all this now? Because we’re about to introduce a new function that will help us construct random phrases from our word lists. But first, let’s have a look at the original BASIC programme and see how it handles the task:

You’ve seen PRINT used previously to display string literals. Like c’s printf, BASIC’s PRINT can also display the values held in variables. You should recognise A$ as being the  name of the array that was used to hold all the words. The bracketed material that comes after it is generating a subscript at random. Let’s break it down to see how it works.

BASIC also has functions. One of these is RND(). RND(1) generates a random number between 0 and 1. However there are 13 words in each list, so we really need this number to be in the range 1 to 13.  Multiplying the random number by 13 and adding 1 will give us this, so this is achieved with 13 * RND(1) + 1. However, the resulting number is a floating point number, and array subscripts are always integers. INT(x)  is another function that will drop any fractional part of x to give us an integer, so INT(13*RND(1)+1) will give us a random integer subscript between 1 and 13.

In the second and third statements we add a larger number because the second and third lists of words start later in the array.

Don’t be confused by the semi-colons in these statements. They are not used for terminating the statement, as they are in C. In BASIC they are used to chain together items to be displayed. A semi-colon in a BASIC PRINT command means “put the next thing to be displayed immediately after the last thing I displayed (i.e. don’t go to a new line)”.

So how do we get random numbers in C? We use the function rand(). This is an example of a function that returns a value. Wheres the BASIC function RND(1) generates a random floating point number between 0 and 1, C’s rand() generates a random integer number between 0 and a maximum number called RAND_MAX. The actual value of RAND_MAX will depend on the machine you are running your programme on. However, we don’t need to know what this number is to get the random number into the correct range. We can do this by dividing the random number by the size of our range and then using the remainder. This is achieved with the following statement:

This statement has introduced you to four new things:

1) Notice how I’ve named the variable. Why didn’t I just call it random number? Well spaces aren’t allowed in variable names. In fact the only characters you can use are the lower case letters a to z, the upper case letters A to Z, the underscore character, _, and the digits 0 to 9 (but digits are not allowed as the first character of a variable name). So, randomNumber, random_number, and random13 would all be valid variable names, but random number, 13random, and random*number would cause errors in your programme. The style I’ve used, where an upper case letter is used to denote the start of each word other than the first, is called camel case. It doesn’t really matter what style you use, as long as you are consistent.

2) When a function returns a value, we can use it wherever we would normally use a literal or a variable of the same type that the function returns.

3) Notice that the rand() function takes no arguments, but we still have to include the parentheses.

4) I’ve used an operator you haven’t seen yet. The symbol % is called the modulus operator and it returns the remainder when the first argument is divided by the second argument.

If you were to try and use this statement in your programme right now, it would throw up an error. This is because, like printf, rand is part of a library and we first need to tell C that we want to use that library.

Below the line that includes the stdio library, add the following line:

This tells C we want to use the c standard library, which includes the rand function.

Now we can add the code to display our randomised phrase. Replace the printf statement you created for the challenge earlier, so that your code looks something like this:

Now build and run your programme. You should see something like this:

random phrases output

Great. You finally have something approaching a functional version of the original programme. That’s an achievement worth celebrating, so let’s run it again. And again. And one more time for good measure. Notice anything odd? Yes, you’re getting exactly the same phrase each time you run the programme. What’s up?

Well the random numbers generated by c are not truly random. It uses a clever algorithm to generate a fixed series of numbers that appear random. These are known as pseudo-random numbers. The problem is, each time you start your programme it starts generating the numbers from the beginning of this series, so you always get the same result.

Fortunately, C provides us with a way round this by using another function, srand(x). This does something called seeding the random number generator. The number x that you give to srand will determine where in the sequence the pseudo-random number generator begins. Let’s try it out. Add this statement just before the line containing the printf command that displays your random phrase:

Now try building and running your programme. This time you should see a completely different phrase. Ok try running it again. And again. Hmm – it seems like we still have exactly the same problem. What we really need is a way to give srand a random number, so it always starts from a different point in the sequence. At this point, stop and think about why we can’t use rand to generate a random number for srand.

The answer of course, is that rand will always generate the same number, so srand will always set the random number generation to the same point. It seems like a bit of a Catch-22 situation. Fortunately there is a way we can get a value which is going to be different each time we run the programme. Can you think what this might be?

The answer is the current date and time, which is going to be different each time we run the programme. You might be wondering how we use two different things – the date and the current time – to generate a single number for srand. Luckily for us, C stores the current date and time as a single number that represents the number of seconds that have elapsed since Midnight UTC on January 1st 1970. 

We can get this number using another function: time(). This comes from yet another library called time, so add the following line below the one that includes stdlib:

Now replace the current srand statement with the following one:

There are a couple of things here that will be new to you:

1) We’ve given time an argument of NULL. This is not the same as the null character, \0, that you looked at when we were discussing strings. The time function has an optional parameter, which is a pointer to an object that we want to store the current time in. When a pointer is not used, as in this example, we can set its value to NULL. This indicates to C that the pointer is not currently pointing to anything useful.

2) You are probably wondering what the (uint) bit is. You’ll recall from earlier that all functions that return a value must return a particular type of value. The time function returns a type called time_t. This is also an integer value, but it can be a much larger value than a standard int. The function srand though, wants its argument to be the type uint, which is short for unsigned integer. This is similar to an int, but it only allows positive numbers, whereas int allows both positive and negative numbers. When we have this sort of situation in C, we can use a typecast to change the value from one type to another. To do this we put the desired type in parentheses to the left of the argument. So here we are saying to C, “I know what I’m giving you isn’t a uint, but could you please treat it like it is a uint – thanks very much”.

Now try to build and run your programme again. You should get a different phrase. Run it one more time and the phrase should be different yet again. Now run it once more for good measure – again you should get a different phrase.

In the next post we’ll see how we can get our programme to print multiple phrases in one run. In the meantime, here’s a couple of tricky challenges for you:

1) Normally, when you are creating games in C, you will want to use srand with the time function so that the player gets a different game each time they play. However, while you are developing the game, you might sometimes want to replace time with an integer literal. Can you think why?

2) The method we are using to get the random number into a certain range will more often than not be slightly biased towards one part of the range. Why might this be?

The answers will be given in the next post.

 

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

Leave a reply