Skip to content

Instantly share code, notes, and snippets.

@keraf
Last active March 16, 2025 00:38
Show Gist options
  • Save keraf/b3d5bacc7be1d4e681bfbac91722957e to your computer and use it in GitHub Desktop.
Save keraf/b3d5bacc7be1d4e681bfbac91722957e to your computer and use it in GitHub Desktop.
Simple hangman game written in C# (.NET 6). This was done as an example for a friend who's learning programming.

Hangman

This is a simple hangman game written in C# (.NET 6). This was done as an example for a friend who's learning programming. Hopefully this can be useful to others as well.

How it works

  1. A word is selected from the list of words (value assigned to choosenWord).
  2. We initialize an empty array that will contain all the letters that have been submitted. This array is called letters.
  3. We also store a count of how many lives are left. This variable is called lives.
  4. The main game loop runs as long as the lives have not reached 0.
  5. Inside the loop, the following happens:
    1. We loop through each letter of the choosen word.
    2. We compare if the letter has been submitted (using the letters array). If yes, we write out the letter and if not, we write the blank character (_).
    3. In this process, we also count all the remaining letters (everytime _ is written, this value gets incremented, telling us how far the player is from finding the word or if he's done).
    4. After the loop, we check if there are any characters left. If there aren't any (value is 0), the player won and we can break out of the loop.
    5. Now we read an input character and assign it to a value. We also need to make sure it's in lowercase as this will avoid the same character in different casings from being submitted.
    6. We also need to validate that the given character is valid. For this, a Regex (declared higher) is used. We are only checking for single letters from a to z. If the character is invalid, we show a message and skip the rest of the loop (continue here will put us to the top of the loop again).
    7. Next, we check the submitted letter against our letters array to know if it has already been submitted. If that's the case, we just show a message and cut to the top again.
    8. The letter being valid and not already in our array, we can now add it to our letters array.
    9. Lastly, we need to know if the letter that was submitted is used in our word or not. If it's not, we decrease the lives by 1 and tell the user how many lives he has left.
  6. The last statement will only be reached once the loop has been exited which happens when the lives reach 0 or if it has been broken out (see point 5. iv.).
  7. If the user still have lives left, we display the win message, otherwise it's a loose message.

Code

// Hangman example in C# (.NET 6 console application)
using System.Text.RegularExpressions;

// List of words that can be used for the game.
var words = new[]
{
    "coffee",
    "banana",
    "airport",
    "cryptography",
    "computer",
};

// Pick a random word from the array.
var chosenWord = words[new Random().Next(0, words.Length - 1)];

// Regex with valid characters (single character between a and z).
var validCharacters = new Regex("^[a-z]$");

// Number of lives the player has before loosing.
var lives = 5;

// Empty array that will contain all the letters submitted by the player.
var letters = new List<string>();

// As long as there are lives left, the loop continues
while (lives != 0)
{
    // Counter of characters left to guess.
    var charactersLeft = 0;
    
    // Loop through all the characters of the word
    foreach (var character in chosenWord)
    {
        // Make the letter a string (easier for conditions).
        var letter = character.ToString();
        
        // If the letter in the loop is in our array of used letters, we write
        // the letter, otherwise we show an underscore (as a letter left to
        // guess).
        if (letters.Contains(letter))
        {
            Console.Write(letter);
        }
        else
        {
            Console.Write("_");
            
            // We also increase the count of letters left, used to track whether
            // the game is finished or not.
            charactersLeft++;
        }
    }
    Console.WriteLine(string.Empty);

    // If there are no characters left, the game is over and we can break out
    // of the loop.
    if (charactersLeft == 0)
    {
        break;
    }

    Console.Write("Type in a letter: ");
    
    // Read the input character and transform it to lowercase to make it easier
    // to compare to already used letters (we could fall into the scenario of a
    // player inputting a capitalized letter that has already been submitted). 
    var key = Console.ReadKey().Key.ToString().ToLower();
    Console.WriteLine(string.Empty);

    // Using the declared Regex above, we check if the the submitted character
    // is valid or not.
    if (!validCharacters.IsMatch(key))
    {
        // If the character is invalid, we loop back to the beginning using the
        // "continue" statement and let the user know of the error.
        Console.WriteLine($"The letter {key} is invalid. Try again.");
        continue;
    }

    // Is the letter already in our array of used letters? 
    if (letters.Contains(key))
    {
        // Only show a message to the user that the letter has already been
        // used. We don't want to do anything else (for example storing it)
        // with that.
        Console.WriteLine("You already entered this letter!");
        continue;
    }

    // If the letter has not already been used, we add it to our array of
    // letters.
    letters.Add(key);

    // If the chosen word doesn't contain the given letter, we reduce the
    // number of lives left by one.
    if (!chosenWord.Contains(key))
    {
        lives--;

        // If all the lives ran out, there's no point saying how many lives
        // are left as the loose message will be shown.
        if (lives > 0)
        {
            // Here, a ternary is used in the string to either how a pluralized
            // version of "try" or the singular one, depending on how many
            // lives are left.
            Console.WriteLine($"The letter {key} is not in the word. You have {lives} {(lives == 1 ? "try" : "tries")} left.");
        }
    }
}

// If the user has lives left, we display the win message, otherwise we don't
if (lives > 0)
{
    // This uses a ternary to pluralize the word lives unless there only one
    // life left.
    Console.WriteLine($"You won with {lives} {(lives == 1 ? "life" : "lives")} left!");
}
else
{
    Console.WriteLine($"You lost! The word was {chosenWord}.");   
}
@IronicBanana
Copy link

IronicBanana commented Mar 1, 2023

Thanks, I think Regex is what I have been missing this whole time. My attempts were like an amalgamation of all these little tips but I wasn't able to figure it out. Do you guys think that instead of the array of random words, I could use user input from another player and store their input the same way, still comparing the strings and player2 inputs?

edit: never mind, i did it.

` // Hangman example in C# (.NET 6 console application)

    // Pick a random word from the other character/player.

    var chosenWord = Console.ReadLine();

    //loop for word spaces and no cheating
    for (int a = 0; a <= 50; a ++)
    {
        Console.WriteLine(".");
    }`

this is all i changed at the top for a two player console app game

@asatillayev
Copy link

class Program
{
// O'yin uchun ishlatilishi mumkin bo'lgan so'zlar ro'yxati
static List wordList = new List
{
"wares",
"soup",
"mount",
"extend",
"brown",
"expert",
"tired",
"humidity",
"backpack",
"crust",
"dent",
"market",
"knock",
"smite",
"windy",
"coin",
"throw",
"silence",
"bluff",
"downfall",
"climb",
"lying",
"weaver",
"snob",
"kickoff",
"match",
"quaker",
"foreman",
"excite",
"thinking",
"mend",
"allergen",
"pruning",
"coat",
"emerald",
"coherent",
"manic",
"multiple",
"square",
"funded",
"funnel",
"sailing",
"dream",
"mutation",
"strict",
"mystic",
"film",
"guide",
"strain",
"bishop",
"settle",
"plateau",
"emigrate",
"marching",
"optimal",
"medley",
"endanger",
"wick",
"condone",
"schema",
"rage",
"figure",
"plague",
"aloof",
"there",
"reusable",
"refinery",
"suffer",
"affirm",
"captive",
"flipping",
"prolong",
"main",
"coral",
"dinner",
"rabbit",
"chill",
"seed",
"born",
"shampoo",
"italian",
"giggle",
"roost",
"palm",
"globe",
"wise",
"grandson",
"running",
"sunlight",
"spending",
"crunch",
"tangle",
"forego",
"tailor",
"divinity",
"probe",
"bearded",
"premium",
"featured",
"serve",
"borrower",
"examine",
"legal",
"outlive",
"unnamed",
"unending",
"snow",
"whisper",
"bundle",
"bracket",
"deny",
"blurred",
"pentagon",
"reformed",
"polarity",
"jumping",
"gain",
"laundry",
"hobble",
"culture",
"whittle",
"docket",
"mayhem",
"build",
"peel",
"board",
"keen",
"glorious",
"singular",
"cavalry",
"present",
"cold",
"hook",
"salted",
"just",
"dumpling",
"glimmer",
"drowning",
"admiral",
"sketch",
"subject",
"upright",
"sunshine",
"slide",
"calamity",
"gurney",
"adult",
"adore",
"weld",
"masking",
"print",
"wishful",
"foyer",
"tofu",
"machete",
"diced",
"behemoth",
"rout",
"midwife",
"neglect",
"mass",
"game",
"stocking",
"folly",
"action",
"bubbling",
"scented",
"sprinter",
"bingo",
"egyptian",
"comedy",
"rung",
"outdated",
"radical",
"escalate",
"mutter",
"desert",
"memento",
"kayak",
"talon",
"portion",
"affirm",
"dashing",
"fare",
"battle",
"pupil",
"rite",
"smash",
"true",
"entrance",
"counting",
"peruse",
"dioxide",
"hermit",
"carving",
"backyard",
"homeless",
"medley",
"packet",
"tickle",
"coming",
"leave",
"swing",
"thicket",
"reserve",
"murder",
"costly",
"corduroy",
"bump",
"oncology",
"swatch",
"rundown",
"steal",
"teller",
"cable",
"oily",
"official",
"abyss",
"schism",
"failing",
"guru",
"trim",
"alfalfa",
"doubt",
"booming",
"bruised",
"playful",
"kicker",
"jockey",
"handmade",
"landfall",
"rhythm",
"keep",
"reassure",
"garland",
"sauna",
"idiom",
"fluent",
"lope",
"gland",
"amend",
"fashion",
"treaty",
"standing",
"current",
"sharpen",
"cinder",
"idealist",
"festive",
"frame",
"molten",
"sill",
"glisten",
"fearful",
"basement",
"minutia",
"coin",
"stick",
"featured",
"soot",
"static",
"crazed",
"upset",
"robotics",
"dwarf",
"shield",
"butler",
"stitch",
"stub",
"sabotage",
"parlor",
"prompt",
"heady",
"horn",
"bygone",
"rework",
"painful",
"composer",
"glance",
"acquit",
"eagle",
"solvent",
"backbone",
"smart",
"atlas",
"leap",
"danger",
"bruise",
"seminar",
"tinge",
"trip",
"narrow",
"while",
"jaguar",
"seminary",
"command",
"cassette",
"draw",
"anchovy",
"scream",
"blush",
"organic",
"applause",
"parallel",
"trolley",
"pathos",
"origin",
"hang",
"pungent",
"angular",
"stubble",
"painted",
"forward",
"saddle",
"muddy",
"orchid",
"prudence",
"disprove",
"yiddish",
"lobbying",
"neuron",
"tumor",
"haitian",
"swift",
"mantel",
"wardrobe",
"consist",
"storied",
"extreme",
"payback",
"control",
"dummy",
"influx",
"realtor",
"detach",
"flake",
"consign",
"adjunct",
"stylized",
"weep",
"prepare",
"pioneer",
"tail",
"platoon",
"exercise",
"dummy",
"clap",
"actor",
"spark",
"dope",
"phrase",
"welsh",
"wall",
"whine",
"fickle",
"wrong",
"stamina",
"dazed",
"cramp",
"filet",
"foresee",
"seller",
"award",
"mare",
"uncover",
"drowning",
"ease",
"buttery",
"luxury",
"bigotry",
"muddy",
"photon",
"snow",
"oppress",
"blessed",
"call",
"stain",
"amber",
"rental",
"nominee",
"township",
"adhesive",
"lengthy",
"swarm",
"court",
"baguette",
"leper",
"vital",
"push",
"digger",
"setback",
"accused",
"taker",
"genie",
"reverse",
"fake",
"widowed",
"renewed",
"goodness",
"featured",
"curse",
"shocked",
"shove",
"marked",
"interact",
"mane",
"hawk",
"kidnap",
"noble",
"proton",
"effort",
"patriot",
"showcase",
"parish",
"mosaic",
"coil",
"aide",
"breeder",
"concoct",
"pathway",
"hearing",
"bayou",
"regimen",
"drain",
"bereft",
"matte",
"bill",
"medal",
"prickly",
"sarcasm",
"stuffy",
"allege",
"monopoly",
"lighter",
"repair",
"worship",
"vent",
"hybrid",
"buffet",
"lively",
};

static string GetWord()
{
    Random random = new Random();
    int randomIndex = random.Next(wordList.Count);
    return wordList[randomIndex].ToUpper();
}

static void Play(string word)
{
    string wordCompletion = new string('_', word.Length);
    bool guessed = false;
    List<char> guessedLetters = new List<char>();
    List<string> guessedWords = new List<string>();
    int tries = 6;

    Console.WriteLine("Let's play Hangman!");
    DisplayHangman(tries);
    Console.WriteLine(wordCompletion);
    Console.WriteLine();

    while (!guessed && tries > 0)
    {
        string guess = Console.ReadLine().ToUpper();

        if (guess.Length == 1 && char.IsLetter(guess[0]))
        {
            if (guessedLetters.Contains(guess[0]))
            {
                Console.WriteLine($"You already guessed the letter {guess}");
            }
            else if (!word.Contains(guess))
            {
                Console.WriteLine($"{guess} is not in the word.");
                tries--;
                guessedLetters.Add(guess[0]);
            }
            else
            {
                Console.WriteLine($"Good job, {guess} is in the word!");
                guessedLetters.Add(guess[0]);
                char[] wordArray = wordCompletion.ToCharArray();
                for (int i = 0; i < word.Length; i++)
                {
                    if (word[i] == guess[0])
                    {
                        wordArray[i] = guess[0];
                    }
                }
                wordCompletion = new string(wordArray);

                if (!wordCompletion.Contains("_"))
                {
                    guessed = true;
                }
            }
        }
        else if (guess.Length == word.Length && guess.All(char.IsLetter))
        {
            if (guessedWords.Contains(guess))
            {
                Console.WriteLine($"You already guessed the word {guess}");
            }
            else if (guess != word)
            {
                Console.WriteLine($"{guess} is not the word.");
                tries--;
                guessedWords.Add(guess);
            }
            else
            {
                guessed = true;
                wordCompletion = word;
            }
        }
        else
        {
            Console.WriteLine("Not a valid guess.");
        }

        DisplayHangman(tries);
        Console.WriteLine(wordCompletion);
        Console.WriteLine();
    }

    if (guessed)
    {
        Console.WriteLine("Congrats, you guessed the word! You win!");
    }
    else
    {
        Console.WriteLine(
            $"Sorry, you ran out of tries. The word was {word}. Maybe next time!"
        );
    }
}

static void DisplayHangman(int tries)
{
    string[] stages =
    {
        // final state: head, torso, both arms, and both legs
        @"
               --------
               |      |
               |      O
               |     \|/
               |      |
               |     / \
               -
            ",
        // head, torso, both arms, and one leg
        @"
               --------
               |      |
               |      O
               |     \|/
               |      |
               |     / 
               -
            ",
        // head, torso, and both arms
        @"
               --------
               |      |
               |      O
               |     \|/
               |      |
               |      
               -
            ",
        // head, torso, and one arm
        @"
               --------
               |      |
               |      O
               |     \|
               |      |
               |     
               -
            ",
        // head and torso
        @"
               --------
               |      |
               |      O
               |      |
               |      |
               |     
               -
            ",
        // head
        @"
               --------
               |      |
               |      O
               |    
               |      
               |     
               -
            ",
        // initial empty state
        @"
               --------
               |      |
               |      
               |    
               |      
               |     
               -
            ",
    };

    Console.WriteLine(stages[tries]);
}

// static void Main() dastur ishga tushganda birinchi ishlaydigan funksiya
static void Main()
{

    string word = GetWord();
    Console.WriteLine(word);
    Play(word);
    do
    {
        Console.WriteLine("Play Again? (Y/N) ");
        string answer = Console.ReadLine().ToUpper();
        if (answer == "Y")
        {
            word = GetWord();
            Play(word);
        }
        else if (answer == "N")
            break;
        else
            Console.WriteLine("Please give correct answer");
    } while (true);
}

}

@RockingSNP
Copy link

Wow!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment