Build a C# Guessing Game

Build a C# console game with input validation, hints, and difficulty levels.

Introduction

30 Second Summary

Guessing a hidden number feels simple at first. The fun comes from using each clue to get closer without knowing the answer.

In this project, you will build a playable number-guessing game with C#. Your game keeps asking for guesses until the player wins.

What You'll Build

Your finished terminal game guides the player toward a hidden number before revealing how many valid attempts the win took.

By the end of this project, you'll have:

  • A one-file C# game you can run from Program.cs to see its title in the terminal.
  • Higher-or-lower hints that respond to repeated guesses until you uncover the hidden number.
  • Crash-resistant input validation that rejects letters or values outside 1 through 100 while counting only valid attempts.
  • Secret Mission: Add Easy, Medium, and Hard ranges that change the challenge before each round.

Are there any prerequisites?

No previous programming experience is required. You need Visual Studio Code with C# Dev Kit plus the .NET SDK version 10 on Windows.

Before We Start

You are about to build a playable C# number-guessing game in the console. This quick commitment connects that result to your reason for learning C# before the hands-on work begins.

Set Up Your One-File C# App

A first C# program can disappear under project files before you see it run. A file-based app keeps this game in one source file.

You will confirm that the Microsoft .NET SDK can run a file-based app. Visual Studio Code will hold Program.cs while its integrated terminal runs the title.

In this step, get ready to:
  • Confirm the installed .NET SDK version.
  • Prepare the GuessTheNumber workspace with Program.cs.
  • Run Program.cs to display the game title.
Verify .NET

File-based apps use one *.cs source file without a matching *.csproj file. They require the .NET 10 SDK.

  • Press the Windows key to open the search bar.
  • Type Visual Studio Code into the search field.
  • Press Enter to open Visual Studio Code.
  • Press Ctrl+` to open the integrated terminal.
  • Check the installed SDK version by running this command:
dotnet --version

What Does This Command Do?

The dotnet --version command reports the .NET SDK version used by dotnet commands.

The output should begin with 10.0. The current guide target is 10.0.400.

Version Check Not Matching?

  • Restart Visual Studio Code if the terminal does not display a version.
  • Install .NET 10 from the official .NET download page if the version begins with an older number.

Help me check why my installed .NET SDK is not reporting a 10.0 version.

Your SDK check is complete. Your computer can run the single-file game.

Create Program.cs

The GuessTheNumber folder gives Visual Studio Code one workspace for the game. The Program.cs file holds all of its C# code.

A Workspace Trust dialog may appear after the folder opens.

  • Click File in the top menu bar.
  • Click Open Folder....
  • Select Desktop in the Windows file picker.
  • Click New Folder.
  • Type GuessTheNumber as the folder name.
  • Press Enter.
  • Click Select Folder.
  • Select Yes, I trust the authors if Workspace Trust appears.

You should see GuessTheNumber at the top of the Explorer view.

  • Select New File... in the Explorer view.
  • Type Program.cs as the file name.
  • Press Enter.

You should see Program.cs inside GuessTheNumber. The file opens in the editor.

  • Return to the integrated terminal by pressing Ctrl+`.

The terminal starts inside the opened GuessTheNumber folder.

Program.cs Missing from Explorer?

  • Confirm that GuessTheNumber appears at the top of the Explorer view.
  • Select New File... inside the GuessTheNumber folder again.

Help me create Program.cs in the correct Visual Studio Code folder.

Run Program.cs

C# 14 supports top-level statements that place executable code directly in Program.cs. This keeps the first version focused on visible terminal output.

Why a One-File App?

A project-based console app adds project structure before you write the game logic. That structure becomes useful as an application grows.

One source file gives you a shorter path to working C# output. You can focus on each language feature as the game develops.

  • Add the game title to Program.cs by entering this code:
Console.WriteLine("=== GUESS THE NUMBER ===");

What Does This Code Do?

  • The Console.WriteLine method writes text to the terminal.
  • The method finishes the output with a new line.
  • The quoted text becomes the title the player sees.
  • Save Program.cs by pressing Ctrl+S.

Before you run it, what exact line do you expect the terminal to print?

  • Run the saved file from the GuessTheNumber terminal with this command:
dotnet Program.cs

What Does This Command Do?

The dotnet command uses the SDK to compile Program.cs. It runs the result in the same terminal.

You should see === GUESS THE NUMBER === in the terminal. Your game now has a running title.

Game Title Not Appearing?

  • Confirm that the terminal is inside the GuessTheNumber folder.
  • Save Program.cs before running the command again.
  • Compare every character in your statement with the full-code tab below.

Help me debug why dotnet Program.cs does not display the game title.

✔️ Awesome, I've got everything!

Your title is running from one C# file. You are ready to add player input.

ⓧ I'd like to double check the full code

  • Compare your saved Program.cs with this complete file:
Console.WriteLine("=== GUESS THE NUMBER ===");

What Should Match?

Your file should contain this single statement. The spelling and punctuation must match exactly.

The title is live. Next, you will give the game a secret number for the player to guess.

Build a One-Guess Prototype

Your one-file app already runs and displays its title.

A title cannot react to a player. This step builds the smallest playable round from a single player guess.

A Random object supplies the hidden number. Console input captures the guess.

Conditional branches compare both numbers. The matching branch prints the result.

In this step, get ready to:
  • Generate a hidden whole number from 1 through 100.
  • Convert one line of player input into a whole number.
  • Print the matching result for the player's guess.
Generate a hidden number

The game needs a secret value before it can judge the player's guess. Next(1, 101) can return any whole number from 1 through 100.

  • Add the range message plus hidden-number setup below Console.WriteLine("=== GUESS THE NUMBER ==="); in Program.cs by pasting this code:
Console.WriteLine("I'm thinking of a whole number from 1 to 100.");

Random random = new();
int secretNumber = random.Next(1, 101);

What does this code do?

  • The opening message tells the player which numbers the game uses.
  • The random variable stores the random-number generator.
  • Next(1, 101) can return 1.
  • Next(1, 101) can return values up to 100 because 101 is excluded.
  • The secretNumber variable stores the selected value.
  • Save Program.cs.
  • Rerun the saved file in the integrated terminal by running this command:
dotnet Program.cs

What does this command do?

The command runs Program.cs as a file-based app. Each run selects a new hidden number.

You'll see === GUESS THE NUMBER === followed by I'm thinking of a whole number from 1 to 100.

Your game now announces its range. Its hidden choice is ready for a guess.

Range message missing?

  • Confirm the new lines sit below the title in Program.cs.
  • Save Program.cs before rerunning the command.

Help me fix the missing range message.

Read one guess

Terminal input arrives as text. The game converts that text into a whole number before making a comparison.

  • Add the input code below the secretNumber declaration in Program.cs by pasting this code:
Console.Write("Enter your guess: ");
string? input = Console.ReadLine();
int guess = int.Parse(input);

How does the input flow work?

  • The first line prints the prompt without adding a line break.
  • Console.ReadLine waits until the player presses Enter.
  • The input variable stores the typed text.
  • int.Parse converts that text into the whole number stored by guess.
  • Save Program.cs.
  • Run the updated game in the integrated terminal by running this command:
dotnet Program.cs

What happens during this run?

The app reaches the input code after selecting its hidden number. The terminal waits at the player prompt.

  • Enter 50 at the prompt.

You'll see the prompt accept 50. The terminal returns to its normal command line after storing the number.

That input path is working. Your game now pauses for a whole-number guess.

Prompt does not wait?

  • Confirm the input code sits below the secretNumber declaration.
  • Select the integrated terminal before typing 50.
  • Save Program.cs before starting another run.

Help me debug the player prompt.

Compare the guess

The game needs three possible paths. A lower guess receives one message. A higher guess receives another message.

The two comparison signs are easy to mix up. Each branch must match its message.

  • Add the result branches below the guess declaration in Program.cs by pasting this code:
if (guess < secretNumber)
{
    Console.WriteLine("Too low.");
}
else if (guess > secretNumber)
{
    Console.WriteLine("Too high.");
}
else
{
    Console.WriteLine("Correct!");
}

How do the branches choose a result?

  • The if branch handles a guess below secretNumber.
  • The else if branch handles a guess above secretNumber.
  • The else branch runs when both values are equal.
  • Save Program.cs.

Before you run it, predict which of the three result paths your guess will take.

  • Test the complete one-guess prototype in the integrated terminal by running this command:
dotnet Program.cs

What does this run test?

This run reaches the complete comparison chain. Exactly one branch prints a result for the entered guess.

  • Enter any whole number from 1 through 100.

You'll see Too low., Too high., or Correct! in the terminal.

You now have a playable one-guess prototype. The game chooses a number before responding to the player's guess.

Result message missing?

  • Confirm the branch chain sits below the guess declaration.
  • Check that < belongs to the Too low. branch.
  • Check that > belongs to the Too high. branch.

Help me debug the result branches.

✔️ Awesome, I've got everything!

Your one-guess game is saved. You are ready to turn it into a full round.

ⓧ I'd like to double check the full code

Compare your complete Program.cs file with this version:

Console.WriteLine("=== GUESS THE NUMBER ===");
Console.WriteLine("I'm thinking of a whole number from 1 to 100.");

Random random = new();
int secretNumber = random.Next(1, 101);

Console.Write("Enter your guess: ");
string? input = Console.ReadLine();
int guess = int.Parse(input);

if (guess < secretNumber)
{
    Console.WriteLine("Too low.");
}
else if (guess > secretNumber)
{
    Console.WriteLine("Too high.");
}
else
{
    Console.WriteLine("Correct!");
}

Your game can now judge one guess. Next up, you'll keep the round running until the player finds the hidden number.

Keep Guessing and Count Attempts

Your one-guess prototype can judge a number. A full game needs to keep asking until the player finds the secret.

A while loop keeps the prompt active while a counter records each numeric attempt. The final test checks what direct parsing does when the player types letters.

In this step, get ready to:
  • Repeat guesses until the player finds the secret number.
  • Count each numeric attempt during the round.
  • Test direct parsing with letters.
Track state inside a guessing loop

Game state stores values that change while the program runs. Your counter tracks guesses while a Boolean records whether the player has won.

  • In Program.cs, find the line containing Console.Write("Enter your guess: ").
  • Select that line through the final closing brace.
  • Replace the selected code by pasting this looping game logic:
int attempts = 0;
bool hasWon = false;

while (!hasWon)
{
    Console.Write("Enter your guess: ");
    string? input = Console.ReadLine();
    int guess = int.Parse(input);

    attempts++;

    if (guess < secretNumber)
    {
        Console.WriteLine("Too low.");
    }
    else if (guess > secretNumber)
    {
        Console.WriteLine("Too high.");
    }
    else
    {
        hasWon = true;
        Console.WriteLine($"Correct! You found {secretNumber} in {attempts} attempts.");
    }
}

What does this loop do?

  • The attempts variable starts at 0 before the first guess.
  • The hasWon variable starts as false because the player has not found the secret number.
  • The while (!hasWon) condition repeats the game logic while hasWon remains false.
  • The attempts++ statement adds one after each numeric guess is parsed.
  • The winning branch changes hasWon to true so the loop ends.
  • The interpolated winning message inserts secretNumber plus attempts into the terminal output.
  • Save Program.cs.

Before you run the game, do you expect one wrong guess to end the program?

  • Run the updated game by running this command:
dotnet Program.cs

What does this run check?

The command runs your saved Program.cs file. The program waits for guesses until hasWon becomes true.

  • Enter a whole number from 1 to 100.
  • Use each hint to enter another whole number.
  • Continue entering whole numbers until you find the secret number.

You should see Too low. or Too high. after each wrong guess. The winning line reports the secret number plus your total attempts.

That is the playable loop working. The game stays active until your correct guess.

Game stops after one guess?

  • Check that the prompt through the result branches sits inside the braces for while (!hasWon).
  • Check that hasWon = true appears only inside the final else branch.

Help me debug why my guessing game stops after one guess.

✔️ Awesome, I've got everything!

Your saved Program.cs file now contains the complete looping version.

ⓧ I'd like to double check the full code

The complete file is below for a character-by-character comparison:

Console.WriteLine("=== GUESS THE NUMBER ===");
Console.WriteLine("I'm thinking of a whole number from 1 to 100.");

Random random = new();
int secretNumber = random.Next(1, 101);
int attempts = 0;
bool hasWon = false;

while (!hasWon)
{
    Console.Write("Enter your guess: ");
    string? input = Console.ReadLine();
    int guess = int.Parse(input);

    attempts++;

    if (guess < secretNumber)
    {
        Console.WriteLine("Too low.");
    }
    else if (guess > secretNumber)
    {
        Console.WriteLine("Too high.");
    }
    else
    {
        hasWon = true;
        Console.WriteLine($"Correct! You found {secretNumber} in {attempts} attempts.");
    }
}
Test a non-numeric guess

Every input so far has been a whole number. A player can also enter text that has no numeric value.

Before you run this test, do you think int.Parse can convert hello into a whole number?

  • Start a new round by running this command:
dotnet Program.cs

What does this test check?

This run starts with the same direct conversion through int.Parse(input). The program waits for your first guess.

  • Enter hello as your first guess.

The terminal should stop with an unhandled FormatException.

The Crash Is the Lesson

The int.Parse call expects text in a numeric format. The value hello does not match that format.

C# throws a FormatException. The program stops before another guess.

This failure is intentional. It gives you a concrete input problem to solve in the next step.

No FormatException?

  • Confirm that Program.cs still contains int.Parse(input).
  • Enter hello before any numeric guess.

Help me reproduce the invalid-input crash.

Your looping game now counts numeric attempts. Next, you'll protect its input boundary so letters cannot end the round.

Make the Game Crash-Proof

Your C# game now keeps asking for guesses. The deliberate FormatException from hello exposed the final weakness: unexpected input can still stop the round.

Input validation gives the loop a safe gate for every guess. This step rejects non-numeric input plus numbers outside 1 through 100 before they reach the attempt counter.

In this step, get ready to:
  • Replace direct parsing with safe input validation.
  • Reject numbers outside the game range.
  • Test every response in a complete round.
Replace direct parsing with safe validation

The int.TryParse method reports whether a conversion succeeds without throwing an exception. A failed conversion can use continue to return directly to the next loop iteration.

  • Find the line containing int.Parse(input) in Program.cs.
  • Delete that line.
  • Paste the following validation block directly below string? input = Console.ReadLine():
    if (!int.TryParse(input, out int guess))
    {
        Console.WriteLine("That is not a whole number. Try again.");
        continue;
    }

What does this validation do?

  • The int.TryParse method checks whether input contains a whole number.
  • The out int guess argument stores the converted number when parsing succeeds.
  • The ! operator sends failed conversions into the validation block.
  • The continue statement starts the next loop iteration before the attempt counter changes.
  • Save Program.cs.

Before you test it, consider this question: will hello end the game or return you to the prompt.

  • Run the updated game from the integrated terminal with this command:
dotnet Program.cs

What does this command do?

This runs the saved Program.cs file. The game waits at its prompt for your first test input.

  • Enter hello at the guess prompt.

You'll see That is not a whole number. Try again. in the terminal. The game asks for another guess instead of stopping.

Does the game still stop?

Check that the new validation block replaced the line containing int.Parse(input). Confirm that the closing brace sits before attempts++.

Ask for help with the exact code around your input conversion: help me fix my safe parsing block

  • Finish the current round with whole-number guesses.
Reject guesses outside the range

A successfully parsed integer can still fall outside the rules of the game. A second validation gate protects the range before attempts increases.

  • Find attempts++ inside the while loop in Program.cs.
  • Paste the following range check directly above it:
    if (guess < 1 || guess > 100)
    {
        Console.WriteLine("Enter a number from 1 to 100.");
        continue;
    }

How does the range check work?

  • The condition checks for a value below 1 or above 100.
  • The guidance message reminds the player which values the game accepts.
  • The continue statement returns to the prompt before attempts++ runs.
  • Save Program.cs.

Before you run the game, predict whether 0 or 101 increases the attempt total.

  • Run the game again with this command:
dotnet Program.cs

What does this run check?

This run uses both validation gates in the saved game. The attempt counter is reached only after an input passes both checks.

  • Enter 0 at the first prompt.
  • Enter 101 at the next prompt.
  • Count each in-range guess as you finish the round.
  • Finish the round by following the higher-or-lower hints.

Both out-of-range values produce Enter a number from 1 to 100. in the terminal. The prompt returns after each message.

The winning total matches the number of valid in-range guesses you counted. Invalid entries no longer inflate attempts.

Does the attempt total include bad entries?

Check that the range validation block appears above attempts++. Make sure both failed validation branches contain continue.

Share the relevant loop code for targeted help: help me debug my attempt counter

✔️ Awesome, I've got everything!

Great. Program.cs now contains both validation gates.

ⓧ I'd like to double check the full code

The complete file should match this version:

Console.WriteLine("=== GUESS THE NUMBER ===");
Console.WriteLine("I'm thinking of a whole number from 1 to 100.");

Random random = new();
int secretNumber = random.Next(1, 101);
int attempts = 0;
bool hasWon = false;

while (!hasWon)
{
    Console.Write("Enter your guess: ");
    string? input = Console.ReadLine();

    if (!int.TryParse(input, out int guess))
    {
        Console.WriteLine("That is not a whole number. Try again.");
        continue;
    }

    if (guess < 1 || guess > 100)
    {
        Console.WriteLine("Enter a number from 1 to 100.");
        continue;
    }

    attempts++;

    if (guess < secretNumber)
    {
        Console.WriteLine("Too low.");
    }
    else if (guess > secretNumber)
    {
        Console.WriteLine("Too high.");
    }
    else
    {
        hasWon = true;
        Console.WriteLine($"Correct! You found {secretNumber} in {attempts} attempts.");
    }
}
Test every game response

A final test checks the whole input path from the prompt to the winning message. Testing each response proves the game stays active until a valid guess wins.

Before you start the final round, predict which three inputs the attempt counter should ignore.

  • Start the final test run with this command:
dotnet Program.cs

What does this test run prove?

This run exercises the complete saved game. The test inputs below cover conversion failure, range failure, hints, and a successful guess.

  • Enter hello.
  • Enter 0.
  • Enter 101.
  • Enter 1.
  • Enter 100 if the round is still active.
  • Follow the hints with in-range whole numbers until you win.
  • Repeat the complete test if a boundary value wins before both hint messages appear.

For hello, you'll see That is not a whole number. Try again..

For 0, you'll see Enter a number from 1 to 100.. The same guidance appears for 101.

Across your test rounds, valid wrong guesses produce both Too low. and Too high..

Your final correct guess produces a winning message with the secret number. Its attempt total includes only valid in-range guesses.

Missing one of the responses?

  • Check that both validation blocks sit before attempts++.
  • Check that the hint chain still compares guess with secretNumber.
  • Check that only the final else branch sets hasWon to true.

Get help comparing the full loop: help me find a missing game response

You did it. Your game now survives invalid input while counting only valid guesses.

Secret mission

Add Difficulty Levels

Add Easy, Medium, and Hard modes before each round begins. One dynamic maximum will update the hidden number, prompts, and accepted guesses for every difficulty.

Clean Up Your Resources

Clean Up Your Resources

Your finished game runs entirely on your computer, so there are no ongoing cloud costs. Choose whether to keep the GuessTheNumber folder, pause your session, or delete the folder entirely.

Resources you used:

  • The local GuessTheNumber folder containing Program.cs.

Keep everything running

No action is needed. Choose this if you want to replay the game or keep improving its code.

  • Keep the GuessTheNumber folder on your computer.

Pause - I'll come back to this later

Stop any active game to free the terminal. Your files stay on disk for your next coding session.

  • Press Ctrl+C in the integrated terminal if the game is still waiting for input.
  • Close Visual Studio Code when you finish.

Delete - I don't want to use this again

Deleting a folder can feel final. This option removes only the local GuessTheNumber folder.

  • Copy Program.cs to another location if you want to preserve your code.
  • Move out of the GuessTheNumber folder and delete it by running these commands:
cd ..
Remove-Item -Recurse -Force .\GuessTheNumber

What Does This Cleanup Do?

  • The first line moves the terminal to the folder that contains GuessTheNumber.
  • The second line permanently removes the game folder with all of its contents.
  • Confirm the cleanup in the integrated terminal. You should see a new prompt with no deletion error.

Folder Still There?

  • Press Ctrl+C if the game is still waiting for input.
  • Close Program.cs in Visual Studio Code.
  • Run the cleanup commands again.

Help me troubleshoot why Windows cannot remove my GuessTheNumber folder.

Nice Work!

Nice Work!

You did it! Your playable C# console game now handles every guess safely across three difficulty levels.

You've learned how to:

  • Build a runnable one-file C# 14 console app in Program.cs with the .NET 10 SDK.
  • Use variables to track the changing game state. Drive each response through conditionals inside a while loop.
  • Reproduce the invalid-input failure caused by int.Parse. Fix it with input validation that combines int.TryParse with range checks.
  • Secret Mission: Add selectable difficulty levels that use maxNumber to switch among Easy, Medium, or Hard ranges.

Ready to quiz yourself?