Skip to content

Games programming from the ground up with C: Functions and random numbers

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

If you want to review where I got to by this point, here are the starter files from the end of the previous post: more fun with variables and arrays.

In the previous post I introduced pointers and how I use them to create an array of strings. I also considered a challenge. The challenge was to display the phrase “flexible cognitive algorithm” using the string arrays I created in the previous post. This is my solution:

/*******
 * Buzzword
 *
 * A program to create amusing fake jargon
 *******/

 #include <stdio.h>

 int main(int argc, const char * argv[])
 {
   printf("Buzzword Generator\nBased on a program from Creative Computing, Morristown, New Jersey\n\n\nThis program prints highly acceptable phrases in 'educator-speak' that you can work into reports and speeches. Whenever a question mark is printed, type a 'Y' for another phrase or 'N' to quit.\n\n\nHere's the first phrase:\n");

   const char * adjectives1[] = {"ability", "basal", "behavioral", "child-centered", "differentiated", "discovery", "flexible", "heterogeneous", "homogenous", "manipulative", "modular", "tavistock", "individualized" };
   const char * adjectives2[] = {"learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative"};
   const char * nouns[] = {"grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment"};

   printf("\n%s %s %s\n", adjectives1[6], adjectives2[3], nouns[5]);

   return 0;
 }

When I saved, compiled and ran this program, I saw this output:

Output from the buzzword program after completing the challenge.
Output from the challenge

Before I carry on building my program I need to introduce an important new concept: functions. A function is some code that another part of the program 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 I’m running the program in a terminal window, the standard output goes to the current cursor position in the terminal window.

I get a function to do some work for me by putting its name into the program at the point I want to use it. The name is followed by parentheses and the parentheses include any parameters (or arguments) that I 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 I’ll write my 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 I want to use a function from a particular library, I have to let C know. If you look towards the top of my program you’ll see this line:

#include <stdio.h>

This line tells C that I 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 I’m about to introduce a new function that will help me construct random phrases from my word lists. But first, I’ll have a look at the original BASIC program and see how it handles the task:

110 PRINT A$(INT(13*RND(1)+1));" ";
120 PRINT A$(INT(13*RND(1)+14));" ";
130 PRINT A$(INT(13*RND(1)+27)) : PRINT

I’ve seen PRINT used previously to display string literals. Like C’s printf, BASIC’s PRINT can also display the values held in variables. I 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 an index into the array at random. Let me 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 the programmer really needs this number to be in the range 1 to 13.  Multiplying the random number by 13 and adding 1 will give them this, so this is achieved with 13 * RND(1) + 1. However, the resulting number is a floating point number, and array indexes are always integers. INT(x)  is another function that will drop any fractional part of x to give an integer, so INT(13*RND(1)+1) will give a random integer index between 1 and 13. In the second and third statements the programmer adds a larger number because the second and third lists of words start later in the array.

*OPTIONAL TECHNICAL SIDE NOTE: You would be forgiven for thinking that the argument of 1 given to RND sets the upper limit of numbers it will generate. In which case, you might wonder, why doesn’t the programmer just use RND(13) instead of multiplying by 13. However, in this dialect of BASIC, RND always generates random numbers between 0 and 1 and the argument determines how that number is generated: an argument of less than 0 starts the number generation from a particular seed (I’ll be talking about seeding random number generators later); an argument of 0 repeats the last number generated; and an argument of 1 generates a new random number.

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 I get random numbers in C? I use the function rand(). This is an example of a function that returns a value. Whereas 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 the program is running on. However, I don’t need to know what this number is to get the random number into the correct range. I can do this by dividing the random number by the size of my range and then using the remainder. This is achieved with the following statement:

int randomNumber = rand() % 13;

This statement has introduced 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 I 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 (note that 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 my program. 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 I use, as long as I am consistent.

2) When a function returns a value, Ie can use it wherever I 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 I still have to include the parentheses.

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

If I was to try and use this statement in my program right now, it would throw up an error. This is because, like printf, rand is part of a library and I first need to tell C that I want to use that library.

Below the line that includes the stdio library, I added the following line:

#include <stdlib.h>

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

Now I can add the code to display my randomised phrase. I replaced the printf statement I created for the challenge earlier, so that my code looked like this:

/*******
 * Buzzword
 *
 * A program to create amusing fake jargon
 *******/

 #include <stdio.h>
 #include <stdlib.h>

 int main(int argc, const char * argv[])
 {
   printf("Buzzword Generator\nBased on a program from Creative Computing, Morristown, New Jersey\n\n\nThis program prints highly acceptable phrases in 'educator-speak' that you can work into reports and speeches. Whenever a question mark is printed, type a 'Y' for another phrase or 'N' to quit.\n\n\nHere's the first phrase:\n");

   const char * adjectives1[] = {"ability", "basal", "behavioral", "child-centered", "differentiated", "discovery", "flexible", "heterogeneous", "homogenous", "manipulative", "modular", "tavistock", "individualized" };
   const char * adjectives2[] = {"learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative"};
   const char * nouns[] = {"grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment"};

   printf("\n%s %s %s\n\n", adjectives1[rand() % 13], adjectives2[rand() % 13], nouns[rand() % 13]);

   return 0;
 }

When I compiled and ran the program, I saw this:

Output from the buzzword program showing a randomly generated phrase.
Output showing a randomly generated phrase

Great. I finally have something approaching a functional version of the original program. That’s an achievement worth celebrating, so I’ll run it again. And again. And one more time for good measure. That’s odd? I’m getting exactly the same phrase each time I run the program. 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 I start your program it starts generating the numbers from the beginning of this series, so I always get the same result.

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

srand(1066);

When I tried building and running the program, I saw a completely different phrase. OK I’ll try running it again. And again. Hmm – it seems like I still have exactly the same problem – I’m always getting the same phrase. What I 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 I 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 I can get a value which is going to be different each time I run the program. Can you think what this might be?

The answer is the current date and time, which is going to be different each time I run the program. You might be wondering how I use two different things – the date and the current time – to generate a single number for srand. Luckily for me, 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 (although on other machine or operating systems it might use a different date and time).

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

#include <time.h>

The I replaced the current srand statement with the following one:

srand((unsigned int)time(NULL));

There are a couple of things here that are new:

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

2) You are probably wondering what the (unsigned int) 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 unsigned int. This is similar to an int, but it only allows positive numbers, whereas int allows both positive and negative numbers. When I have this sort of situation in C, I can use something called a typecast to change the value from one type to another. To do this I put the desired type in parentheses to the left of the argument. So here I am saying to C, “I know what I’m giving you isn’t an unsigned int, but could you please treat it like it is an unsigned int – thanks very much”.

This time when I saved, compiled and ran the program I got a different phrase. I ran it one more time and the phrase was different yet again. Finally I ran it once more for good measure – again I got a different phrase.

That’s it for this post. In the next post I’ll see how I can get my program to print multiple phrases in one run. In the meantime, here’s a couple of tricky challenges to consider :

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

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

The answers will be given in the next post.

Published inGamesProgramming

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *