Skip to content

C Project: Number Guessing Game

Updated 12 min read Aimerscrew

The number guessing game in C is a fun project that brings together loops, functions, input checking and one new tool: random numbers. The computer thinks of a number from 1 to 100, you guess, and it replies "Too high!" or "Too low!" until you find it or run out of attempts. You'll build it in small steps, then combine everything into a polished game with a score.

🌍 Real-life example

Ever played "guess the price" on a game show, or guessed how many sweets are in a jar at a school fair? Each answer of "higher" or "lower" narrows things down. Your C program plays the host: it hides a number and gives hints after each guess.

What you'll build

The finished game:

  • picks a random secret number from 1 to 100,
  • gives the player 7 attempts and counts each guess,
  • rejects letters and out-of-range numbers without using up an attempt,
  • prints "Too low!" or "Too high!" after each wrong guess,
  • asks "Play again?" and shows a final score.

Here's a quick round. The player typed the numbers after each Your guess (1-100): prompt and n at the end. The secret is random, so your numbers will differ.

=== Number Guessing Game ===

I'm thinking of a number between 1 and 100.
You have 7 attempts. Good luck!
Your guess (1-100): 50
Too low!
Attempts left: 6
Your guess (1-100): 75
Too high!
Attempts left: 5
Your guess (1-100): 63
Correct! You found 63 in 3 attempts.

Play again? (y/n): n
Thanks for playing! Final score: 1 won, 0 lost.

Concepts used in this project

ConceptWhere it's usedLesson
while and do...while loopsRepeating guesses and roundswhile loop, do-while loop
if / else ifToo low, too high or correctif...else
scanf() return valueRejecting input that isn't a numberuser input
break and continueLeaving or restarting a loopbreak and continue
Functions returning valuesget_guess(), play_round()return values
% (remainder)Squeezing a random number into 1 to 100arithmetic operators
Ternary operator"attempt" or "attempts"ternary operator
#defineThe MAX_ATTEMPTS constantconstants

How the game works (program flow)

The game has two loops, one inside the other. The inner loop handles guesses within a round. The outer loop starts a new round each time the player says yes.

Flowchart of the C number guessing game: seed the generator once, pick a secret, read a valid guess, compare it with the secret, loop while attempts are left, then ask to play again or print the final score
The guessing loop sits inside the "play again" loop. srand() runs only once, before both.

Step 1: How do you generate a random number in C?

C's random number tools live in <stdlib.h>:

  • rand() returns a pseudo-random whole number from 0 up to RAND_MAX (at least 32767, often much larger). "Pseudo-random" means it's calculated by a formula, so it only looks random.
  • srand(seed) sets the starting point, or seed, of that formula. The same seed always gives the same sequence.

To get a different game every time, seed with the current time. time(NULL) from <time.h> returns the number of seconds since 1 January 1970, which changes every second:

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

int main(void) {
    srand((unsigned int) time(NULL));

    printf("Dice roll: %d\n", rand() % 6 + 1);
    printf("Secret number: %d\n", rand() % 100 + 1);
    return 0;
}

One run printed this. The numbers are random, so yours will differ every time you run it:

Dice roll: 4
Secret number: 63

How does rand() % 100 + 1 work? The remainder after dividing by 100 is always between 0 and 99. Adding 1 shifts the range to 1 through 100. In general, rand() % n + 1 gives a number from 1 to n. The (unsigned int) cast converts the time to the type srand() expects.

⚠️ Call srand() only once

Seed at the start of main(), never inside a loop. If you call srand(time(NULL)) before every rand(), all the calls made within the same second restart the formula from the same seed and return the same "random" number.

Step 2: Keep guessing with a loop

Now pick a secret and let the player guess until they're right. An attempts counter goes up by one with every guess, and break leaves the loop on a correct answer:

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

int main(void) {
    int secret;
    int guess;
    int attempts = 0;

    srand((unsigned int) time(NULL));
    secret = rand() % 100 + 1;

    while (1) {
        printf("Your guess: ");
        if (scanf("%d", &guess) != 1) {
            printf("That's not a number. Bye!\n");
            break;
        }
        attempts++;

        if (guess < secret) {
            printf("Too low!\n");
        } else if (guess > secret) {
            printf("Too high!\n");
        } else {
            printf("Correct! You needed %d attempts.\n", attempts);
            break;
        }
    }
    return 0;
}

Here the player typed 50, 25, 40, 45 and 42 (your secret number will differ):

Your guess: 50
Too high!
Your guess: 25
Too low!
Your guess: 40
Too low!
Your guess: 45
Too high!
Your guess: 42
Correct! You needed 5 attempts.

💡 Tip: the smartest strategy

Always guess the middle of the range that's left: 50, then 25 or 75, and so on. Each guess halves the possibilities, so any number from 1 to 100 takes at most 7 guesses (27 = 128). That's why the final game allows 7 attempts. It's the same idea as binary search.

Step 3: Validate the guess

The Step 2 version simply quits when someone types a letter. A friendlier game asks again. We'll move that job into a function, get_guess(), which loops until it gets a whole number from 1 to 100:

#include <stdio.h>

/* Throw away the rest of the current input line. */
void clear_input(void) {
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
    }
}

/* Ask until the player types a number from 1 to 100.
   Returns the guess, or 0 if the input has ended. */
int get_guess(void) {
    int guess;
    int status;

    while (1) {
        printf("Your guess (1-100): ");
        status = scanf("%d", &guess);
        if (status == EOF) {
            return 0;
        }
        clear_input();
        if (status != 1) {
            printf("Please type a whole number, like 42.\n");
            continue;
        }
        if (guess < 1 || guess > 100) {
            printf("Out of range! Pick a number from 1 to 100.\n");
            continue;
        }
        return guess;
    }
}

int main(void) {
    int number = get_guess();
    printf("You picked %d\n", number);
    return 0;
}

The player typed ten, -5, 250 and finally 73:

Your guess (1-100): ten
Please type a whole number, like 42.
Your guess (1-100): -5
Out of range! Pick a number from 1 to 100.
Your guess (1-100): 250
Out of range! Pick a number from 1 to 100.
Your guess (1-100): 73
You picked 73

Here's what each check does:

  • scanf() returns 1 when it reads a number, 0 when the input isn't a number (like ten), and EOF when there's no input left at all. Returning 0 in that last case lets the caller stop the game instead of looping forever.
  • clear_input() runs after every read. It removes leftovers such as ten, or the abc in 42abc, so they can't confuse the next scanf().
  • continue jumps back to the prompt. Only return guess leaves the function, and only with a valid number.

Step 4: One round with an attempts limit

Next, wrap a full round in play_round(). The loop now runs only while (attempts < MAX_ATTEMPTS), and the function returns 1 for a win and 0 for a loss:

#define MAX_ATTEMPTS 7

/* Play one round. Returns 1 if the player wins, 0 if not. */
int play_round(void) {
    int secret = rand() % 100 + 1;
    int attempts = 0;
    int guess;

    printf("\nI'm thinking of a number between 1 and 100.\n");
    printf("You have %d attempts. Good luck!\n", MAX_ATTEMPTS);

    while (attempts < MAX_ATTEMPTS) {
        guess = get_guess();
        if (guess == 0) {
            return 0;    /* input ended */
        }
        attempts++;

        if (guess < secret) {
            printf("Too low!\n");
        } else if (guess > secret) {
            printf("Too high!\n");
        } else {
            printf("Correct! You found %d in %d %s.\n", secret, attempts,
                   attempts == 1 ? "attempt" : "attempts");
            return 1;
        }

        if (attempts < MAX_ATTEMPTS) {
            printf("Attempts left: %d\n", MAX_ATTEMPTS - attempts);
        }
    }

    printf("Out of attempts! The number was %d.\n", secret);
    return 0;
}

Notice the ternary operator inside printf(): it picks "attempt" for a lucky first guess and "attempts" otherwise. The secret is chosen once at the top of the function, before the loop, so it stays the same for the whole round.

Step 5: Play again and keep score

Finally, a small function asks whether to continue, and main() uses a do...while loop, which is perfect here because the game must run at least once:

/* Returns 1 if the player wants another round. */
int ask_play_again(void) {
    char answer;

    printf("\nPlay again? (y/n): ");
    if (scanf(" %c", &answer) != 1) {
        return 0;
    }
    clear_input();
    return answer == 'y' || answer == 'Y';
}

The complete number guessing game in C

Here's the full program. Save it as guess.c, compile it with gcc -std=c17 -Wall -Wextra guess.c -o guess and run ./guess.

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

#define MAX_ATTEMPTS 7

/* Throw away the rest of the current input line. */
void clear_input(void) {
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
    }
}

/* Ask until the player types a number from 1 to 100.
   Returns the guess, or 0 if the input has ended. */
int get_guess(void) {
    int guess;
    int status;

    while (1) {
        printf("Your guess (1-100): ");
        status = scanf("%d", &guess);
        if (status == EOF) {
            return 0;
        }
        clear_input();
        if (status != 1) {
            printf("Please type a whole number, like 42.\n");
            continue;
        }
        if (guess < 1 || guess > 100) {
            printf("Out of range! Pick a number from 1 to 100.\n");
            continue;
        }
        return guess;
    }
}

/* Play one round. Returns 1 if the player wins, 0 if not. */
int play_round(void) {
    int secret = rand() % 100 + 1;
    int attempts = 0;
    int guess;

    printf("\nI'm thinking of a number between 1 and 100.\n");
    printf("You have %d attempts. Good luck!\n", MAX_ATTEMPTS);

    while (attempts < MAX_ATTEMPTS) {
        guess = get_guess();
        if (guess == 0) {
            return 0;    /* input ended */
        }
        attempts++;

        if (guess < secret) {
            printf("Too low!\n");
        } else if (guess > secret) {
            printf("Too high!\n");
        } else {
            printf("Correct! You found %d in %d %s.\n", secret, attempts,
                   attempts == 1 ? "attempt" : "attempts");
            return 1;
        }

        if (attempts < MAX_ATTEMPTS) {
            printf("Attempts left: %d\n", MAX_ATTEMPTS - attempts);
        }
    }

    printf("Out of attempts! The number was %d.\n", secret);
    return 0;
}

/* Returns 1 if the player wants another round. */
int ask_play_again(void) {
    char answer;

    printf("\nPlay again? (y/n): ");
    if (scanf(" %c", &answer) != 1) {
        return 0;
    }
    clear_input();
    return answer == 'y' || answer == 'Y';
}

int main(void) {
    int wins = 0;
    int rounds = 0;

    srand((unsigned int) time(NULL));
    printf("=== Number Guessing Game ===\n");

    do {
        rounds++;
        if (play_round()) {
            wins++;
        }
    } while (ask_play_again());

    printf("Thanks for playing! Final score: %d won, %d lost.\n",
           wins, rounds - wins);
    return 0;
}

Sample run

This run plays two rounds: a win (with a typo and an out-of-range guess along the way) and a loss. Everything after a prompt's colon was typed by the player. The secret numbers are random, so your numbers will differ.

=== Number Guessing Game ===

I'm thinking of a number between 1 and 100.
You have 7 attempts. Good luck!
Your guess (1-100): 50
Too high!
Attempts left: 6
Your guess (1-100): abc
Please type a whole number, like 42.
Your guess (1-100): 25
Too low!
Attempts left: 5
Your guess (1-100): 150
Out of range! Pick a number from 1 to 100.
Your guess (1-100): 37
Too low!
Attempts left: 4
Your guess (1-100): 43
Too high!
Attempts left: 3
Your guess (1-100): 42
Correct! You found 42 in 5 attempts.

Play again? (y/n): y

I'm thinking of a number between 1 and 100.
You have 7 attempts. Good luck!
Your guess (1-100): 50
Too high!
Attempts left: 6
Your guess (1-100): 25
Too high!
Attempts left: 5
Your guess (1-100): 12
Too low!
Attempts left: 4
Your guess (1-100): 18
Too low!
Attempts left: 3
Your guess (1-100): 22
Too high!
Attempts left: 2
Your guess (1-100): 21
Too high!
Attempts left: 1
Your guess (1-100): 19
Too low!
Out of attempts! The number was 20.

Play again? (y/n): n
Thanks for playing! Final score: 1 won, 1 lost.

Notice that abc and 150 didn't use up an attempt: the counter only goes up after get_guess() returns a valid number. And after the seventh wrong guess there's no "Attempts left: 0" line, because of the if (attempts < MAX_ATTEMPTS) check.

💡 Testing tip: make "random" repeatable

While testing, replace srand((unsigned int) time(NULL)); with srand(1);. A fixed seed gives the same secret numbers on every run, so you can check your logic step by step. The exact numbers still differ between compilers and operating systems. Put the time-based seed back when you're done.

Common mistakes in this project

1. Forgetting to call srand()

Without srand(), rand() behaves as if it was seeded with 1, so the game picks the same secret every time you run it.

2. Writing rand() % 100 and expecting 1 to 100

rand() % 100 gives 0 to 99. Players could never win when the secret is 0, and 100 would never come up. Remember the + 1.

3. Picking the secret inside the guessing loop

If secret = rand() % 100 + 1; sits inside the while loop, the answer changes after every guess and the hints become useless.

4. Not clearing input after a failed scanf()

Remove the clear_input() call and type abc: scanf() fails on the same letters forever, printing the error message in an endless loop.

5. Using a char for the getchar() result

getchar() returns an int so it can report EOF. Storing it in a char can make the ch != EOF test never succeed, which hangs the program at the end of input.

Ideas to extend it

🏋️ Challenges

  1. Difficulty levels. Ask for Easy (1-50, 10 attempts), Medium (1-100, 7) or Hard (1-1000, 10). Pass the range and the attempts to play_round() and get_guess() as parameters.
  2. "Very close!" hints. When the guess is within 5 of the secret, add "Very close!" after "Too low!" or "Too high!". Use abs() from <stdlib.h>.
  3. Best score. Track the fewest attempts in any winning round and print it at the end.
  4. No repeated guesses. Keep a 101-element array of flags and warn the player, without using an attempt, if they guess the same number twice.
  5. Swap roles. You think of a number and the computer guesses it using the halving strategy from the tip in Step 2.
Show a hint for challenge 2
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int secret = 42;
    int guesses[3] = {30, 39, 47};

    for (int i = 0; i < 3; i++) {
        printf("%d %s", guesses[i], guesses[i] < secret ? "Too low!" : "Too high!");
        if (abs(guesses[i] - secret) <= 5) {
            printf(" Very close!");
        }
        printf("\n");
    }
    return 0;
}
30 Too low!
39 Too low! Very close!
47 Too high! Very close!

What's next?

Your programs so far forget everything when they close. Next, you'll build a student records manager that uses structs and saves its data to a file, so it's still there the next time you run it.

Key takeaways

  • A number guessing game in C seeds once with srand(time(NULL)) and picks a secret with rand() % 100 + 1.
  • rand() % n gives 0 to n - 1, so add 1 to get a number from 1 to n.
  • Call srand() once at the start of main(), never inside a loop.
  • Check scanf() for 1 (a number), 0 (not a number) and EOF (no more input), and clear the line after each read.
  • Splitting the game into get_guess(), play_round() and ask_play_again() keeps each function small and easy to test.
  • A do...while loop is a natural fit for play again, because the game always runs at least once.

Frequently asked questions

How do you make a number guessing game in C?

Include stdlib.h and time.h, call srand(time(NULL)) once, pick a secret with rand() % 100 + 1, then loop: read a guess with scanf(), print too high or too low, and break when the guess equals the secret.

Why does my C program give the same random number every time?

You did not call srand(), or you seeded it with a fixed value. Without srand(), rand() acts as if seeded with 1. Call srand((unsigned int) time(NULL)) once at the start of main() to get a different sequence on each run.

How do you get a random number between 1 and 100 in C?

Use rand() % 100 + 1. The remainder after dividing by 100 is 0 to 99, and adding 1 shifts it to 1 to 100. In general, rand() % (max - min + 1) + min gives a number from min to max.

How many guesses does it take to find a number from 1 to 100?

At most 7 if you always guess the middle of the remaining range. Each guess halves the possibilities, and 2 to the power of 7 is 128, which covers all 100 numbers.

Is rand() good enough for real security?

No. rand() is fine for games and simple simulations, but its numbers are predictable. Passwords, tokens and encryption keys need a cryptographically secure random source provided by the operating system or a security library.