The guessing game uses everything from the previous pages: variables,
a loop, conditionals, functions, and two standard library calls you
have not seen yet — rand() and srand().
Random numbers
rand() from <stdlib.h> returns a pseudo-random integer between
0 and RAND_MAX (at least 32767). Pseudo-random means the sequence
is deterministic — the same seed produces the same sequence every time.
srand(seed) sets the seed. Seeding with time(NULL) (the current
Unix timestamp from <time.h>) produces a different sequence each run:
srand((unsigned int)time(NULL));
n = rand() % 100 + 1; /* 1 to 100 */rand() % 100 gives a remainder in the range 0–99. Adding 1 shifts
it to 1–100. This is not a cryptographically uniform distribution, but
it is sufficient for a guessing game.
guess.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int target;
int guess;
int attempts;
srand((unsigned int)time(NULL));
target = rand() % 100 + 1;
attempts = 0;
printf("I'm thinking of a number between 1 and 100.\n");
while (1) {
printf("Your guess: ");
if (scanf("%d", &guess) != 1)
break;
attempts++;
if (guess < target)
printf("Too low.\n");
else if (guess > target)
printf("Too high.\n");
else {
printf("Correct — %d attempt", attempts);
if (attempts != 1)
printf("s");
printf(".\n");
break;
}
}
return (0);
}Compile and play:
gcc -Wall -Wextra guess.c -o guess
./guessA few things worth noting:
while (1)loops forever. The only exits arebreak— on a correct guess or an input error. This is the idiomatic C pattern for "loop until a condition inside the loop is met."scanf("%d", &guess) != 1checks whether scanf successfully read one integer. If the user types a non-number or sends EOF (Ctrl+D), scanf returns a value other than 1 and the loop exits cleanly.- The attempt pluralisation (
attemptvsattempts) is a small detail. Small details in text output are visible to users.
Reading Doom's random number generator
You now know enough C to read a real codebase. Clone the Doom[1] source code — released by id Software in December 1997:
git clone https://github.com/id-Software/DOOM.gitOpen linuxdoom-1.10/m_random.c. It is 76 lines. The core is a table
of 256 bytes and two index variables:
static unsigned char rndtable[256] = {
0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 ,
/* ... 242 more values ... */
};
static int rndindex = 0;
static int prndindex = 0;
int M_Random (void)
{
rndindex = (rndindex+1)&0xff;
return rndtable[rndindex];
}This is not rand(). It is a lookup table — the same 256-value
sequence, every time, on every platform, in every game session. Doom
records demos by saving only the player inputs, then replaying them
against the same random sequence. If rand() varied between runs, the
demo would desync. The determinism is intentional.
P_Random() (gameplay random — for enemy AI, projectile spread, and
everything that affects game state) uses prndindex. M_Random()
(misc random — for menu effects and things outside gameplay) uses
rndindex separately so it does not disturb the gameplay sequence.
You wrote a guessing game that picks a number nobody can predict. id Software wrote a game that picks numbers everyone can reproduce. Both are the same idea — a sequence of integers that behaves like random — solved for different constraints.
Read the file. You understand it now.