using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Media;
using static System.Console;
/// <summary>
/// # # # # # # # # # # # # #
/// # Mark Hesser #
/// # Jan 9, 2018 #
/// # Hangman Console App #
/// # # # # # # # # # # # # # # # # # # # # #
/// # Create a game similar to Hangman #
/// # in which a Player guesses letters #
/// # to try to replicate a hidden word. #
/// # # # # # # # # # # # # # # # # # # # # #
/// </summary>
namespace Hangman
{
class Game
{
#region Static Declarations
static string[] words = { "JOINT", "LINUX", "GROAN", "LASER", "CLASS", "DAVID", "HARSH", "WATER",
"COMPANY", "CUNNING", "ABSENSE", "URGENCY", "HIGHWAY", "PUDDING", "RAYMOND", "LIABILITY", "POTENTIAL", "INFECTION",
"INTERFACE", "NETWORK", "BRAODCAST", "OFFENSIVE", "IGNORANCE", "OVERWHELM", "TECHNOLOGY", "COFFEEHOLIC" };
static char[] validLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
static char[] letters, prevGuess;
static bool[] answers, wordsPlayed;
static string wordToGuess;
static char guess;
static bool isPlaying = true, gamePlay = true, win = false, escape = false, debug = false;
static int index, numWordsPlayed = 0, numGuess = 0, lives = 6, lettersFound = 0, message = 0, wins = 0, games = 0;
static Random rand = new Random();
static ConsoleKeyInfo kb;
#endregion
/// <summary>
/// This is the Main Game Method.
/// It uses 2 control loops to trigger game play and quit.
/// At Launch the Title is set and the Directions are displayed.
/// </summary>
static void Main()
{
#region Initializers
Title = "Hangman by Mark Hesser";
Array.Sort(words); //Sorts the Array in Alphbetical order because I was too lazy to do it myself
ResetWordsPlayed(); //Initalizes the Words Played Array at Start
Directions();
NewGame();
#endregion
while (isPlaying) //Play or Quit Loop
{
while (gamePlay) //Main Game Loop
{
#region Set Window Size
try
{
if (debug == true)
SetWindowSize(Convert.ToInt32(LargestWindowWidth / 1.5), Convert.ToInt32(LargestWindowHeight / 1.5));
else
SetWindowSize(55, 30);
}
catch (Exception) { }
#endregion
Clear();
#region Hangman
WriteLine("\n _ _ " +
"\n | | | | __ _ _ __ __ _ _ __ ___ __ _ _ __ " +
"\n | |_| |/ _` | ‘_ \\ / _` | ‘_ ` _ \\ / _` | ‘_ \\ " +
"\n | _ | (_| | | | | (_| | | | | | | (_| | | | |" +
"\n |_| |_|\\__,_|_| |_|\\__, |_| |_| |_|\\__,_|_| |_|" +
"\n |___| ");
#endregion
DisplayNoose();
DisplayLetters();
#region GameOver Event
//if you run out of lives or you find all the letters a GameOver is triggered
if (lives <= 0 || win == true)
{
games++;
GameOver();
gamePlay = false;
break;
}
#endregion
DisplayMessage();
#region Debug Event
//Press F1 to Enable Debug Mode
if (debug == true)
Debug();
#endregion
PlayerGuess();
#region Escape Event
//Breaks the Game Loop if the Escape key is pressed.
if (escape == true)
{
escape = false;
gamePlay = false;
break;
}
#endregion
CheckGuess();
}
Quit();
}
}
/// <summary>
/// This Method Displays Directions to the player on how to play the game.
/// It is displayed at start and can be recalled by pressing Tab during the game.
/// </summary>
static void Directions()
{
#region Set Window Size
try
{
SetWindowSize(120, 25);
}
catch (Exception) { }
#endregion
Clear();
WriteLine("\n Welcome to Hangman by Mark Hesser! \n" +
"\n Press Tab at any time to bring up these directions. " +
"\n Press Escape at any time to Start a new Game or Quit. \n" +
"\n Directions: A word will be randomly selected at the beginning of each game." +
"\n Press a key corrosponding to a letter from a-z. If you guessed correctly, the letter will" +
"\n be added to the board. If you guess incorrectly, a body part will be drawn on the screen." +
"\n You have 6 Lives to guess correctly. Head, Body, Left Arm, Right Arm, Left Leg, Right Leg." +
"\n The game is over when either you guess all the correct letters, or you lose all your lives." +
"\n You will not lose a life if you accidentally guess the same word twice. There are 25 words that" +
"\n can be chosen randomly, the same word will not appear twice until all 25 games. Have been played." +
"\n After all the words have been played, the list is shuffled and words are used again.\n" +
"\n Press Any Key to Continue."); ReadKey();
}
/// <summary>
/// This Method Initializes the variables required to play the game.
/// It also Selects a random word from the list and initializes the letter array.
/// </summary>
static void NewGame()
{
//This Randomly Selects a number between 0 and max length of word array so new words can be added.
index = rand.Next(words.Length);
while (wordsPlayed[index])
{
if (numWordsPlayed == words.Length)
{
ResetWordsPlayed();
}
index = rand.Next(words.Length);
}
numWordsPlayed++;
//This sets the word found based on index to the wordToGuess string.
wordToGuess = words[index];
wordsPlayed[index] = true;
//This creates a character array from the selected word.
letters = wordToGuess.ToCharArray();
//This creates a parallel array that will hold true/false.
//If the letter of that index has been found, it is true, default is all false.
answers = new bool[wordToGuess.Length];
//This for loop initializes each index of the array to false.
for (int i = 0; i < answers.Length; i++)
{
answers[i] = false;
}
//This creates the Previous Guess array.
prevGuess = new char[validLetters.Length];
//This resets the counter for number of guess attempts.
numGuess = 0;
//Counter that determines if the Player has won,
//when this counter equals the length of the letters array.
//The word has been found and the Player wins.
lettersFound = 0;
//This counts down the number of lives remaining
//When this counter reaches zero, the GameOver method is triggered.
lives = 6;
//This is a bool value that tells program the Player has won.
//Which also triggers the GameOver Method
win = false;
//This resets the message to 0
message = 0;
}
/// <summary>
/// Resets the WordsPlayed Array to all false.
/// Helpful if all words have been played.
/// </summary>
static void ResetWordsPlayed()
{
wordsPlayed = new bool[words.Length];
for (int i = 0; i < words.Length; i++)
{
wordsPlayed[i] = false;
}
numWordsPlayed = 0;
}
/// <summary>
/// What this Method does is in the Title.
/// It simply displays the appropiate noose based on the number of lives remaining.
/// </summary>
static void DisplayNoose()
{
switch (lives) //Draws the appropiate noose based on number of lives remaining.
{
case 6: //Empty Noose, 6 Lives remaining.
WriteLine("" +
"\n\t ________" +
"\n\t || |" +
"\n\t ||" +
"\n\t ||" +
"\n\t ||" +
"\n\t ||" +
"\n\t ||" +
"\n\t ___||___"); break;
case 5: //Head, 5 Lives remaining.
WriteLine("" +
"\n\t ________" +
"\n\t || |" +
"\n\t || O" +
"\n\t ||" +
"\n\t ||" +
"\n\t ||" +
"\n\t ||" +
"\n\t ___||___"); break;
case 4: //Body, 4 Lives remaining.
WriteLine("" +
"\n\t ________" +
"\n\t || |" +
"\n\t || O" +
"\n\t || |" +
"\n\t || |" +
"\n\t ||" +
"\n\t ||" +
"\n\t ___||___"); break;
case 3: //Left Arm, 3 Lives remaining.
WriteLine("" +
"\n\t ________" +
"\n\t || |" +
"\n\t || O" +
"\n\t || \\|" +
"\n\t || |" +
"\n\t ||" +
"\n\t ||" +
"\n\t ___||___"); break;
case 2: //Right Arm, 2 Lives remaining.
WriteLine("" +
"\n\t ________" +
"\n\t || |" +
"\n\t || O" +
"\n\t || \\|/" +
"\n\t || |" +
"\n\t ||" +
"\n\t ||" +
"\n\t ___||___"); break;
case 1: //Left Leg, 1 Life remaining.
WriteLine("" +
"\n\t ________" +
"\n\t || |" +
"\n\t || O" +
"\n\t || \\|/" +
"\n\t || |" +
"\n\t || /" +
"\n\t ||" +
"\n\t ___||___"); break;
case 0: //Right Leg, Game Over.
WriteLine("" +
"\n\t ________" +
"\n\t || |" +
"\n\t || O" +
"\n\t || \\|/" +
"\n\t || |" +
"\n\t || / \\" +
"\n\t ||" +
"\n\t ___||___"); break;
case 9: //You win!
WriteLine("" +
"\n\t" +
"\n\t" +
"\n\t" +
"\n\t _____" +
"\n\t || O" +
"\n\t || \\|/" +
"\n\t || |" +
"\n\t ___||___ / \\"); break;
default: WriteLine("An Error has occured… Code: LIVES-1 \a"); break;
//Error Code that triggers^ if for some reason lives is not between 0-6.
}
}
/// <summary>
/// This Method Displays the appropiate spaces based on letters that have been found.
/// </summary>
static void DisplayLetters()
{
WriteLine();
Write("\n\t ");
for (int i = 0; i < letters.Length; i++)
{
if (answers[i] == true)
{
//Write(" {0} ", answers[i]); //Uncomment to test answers array
Write(" {0}", letters[i]); //Display characters that have been guessed.
}
else Write(" *");
}
WriteLine("\n");
}
/// <summary>
/// This Method Displays the appropiate message to the player based on the situation.
/// It also displays the number of lives remaining.
/// </summary>
static void DisplayMessage()
{
switch (message)
{
case 1: WriteLine("\n You Guessed ‘{0}’ \n You have found a letter!", guess); break;
case 2: WriteLine("\n You Guessed ‘{0}’ \n That letter is not in the word!", guess); break;
case 3: WriteLine("\n You Guessed ‘{0}’ \n You have already guessed that letter.", guess); break;
case 4: WriteLine("\n You Guessed ‘{0}’ \n That is not a Letter…\a", guess); break;
case 5: WriteLine("\n Your Previous Guess was ‘{0}’ \n Please don’t hit enter after you input your guess.", guess); break;
default: break;
}
//Displays Lives remaining.
WriteLine("\n You have {0} Lives remaining.", lives);
//This resets the message so that it doesn’t display after next redraw.
message = 0;
}
/// <summary>
/// This Method Prompts the Player for their Guess.
/// It also allows you to enter keys that trigger other events.
/// </summary>
static void PlayerGuess()
{
Write("\n Guess: ");
//Input Keyboard character to input a guess
kb = ReadKey();
//Press Excape to Prompt the Quit Method.
//This allows you to Start a New Game or Quit the Program.
if (kb.Key == ConsoleKey.Escape)
escape = true;
else if (kb.Key == ConsoleKey.Enter)
message = 5;
//Press Tab to Bring up the Directions
else if (kb.Key == ConsoleKey.Tab)
{
Directions();
}
//Press F1 to Bring up the Debug Display
else if (kb.Key == ConsoleKey.F1)
{
if (debug == true)
debug = false;
else if (debug == false)
debug = true;
}
//Press F2 to Reset Words Played.
else if (kb.Key == ConsoleKey.F2)
{
ResetWordsPlayed();
}
//Valid Characters are A-Z, if the input is valid it will
else if (validLetters.Contains(kb.KeyChar))
{
guess = kb.KeyChar;
guess = Char.ToUpper(guess);
}
//If all else fails and no playable key is pressed,
//Trigger the Not Valid Error.
else
{
message = 4;
}
}
/// <summary>
/// This Method compares the Player’s guess against the arrays,
/// to determine if they have found a letter or not.
/// </summary>
static void CheckGuess()
{
//This Method is skipped if an event key is pressed.
if (validLetters.Contains(kb.KeyChar))
{
//This for loop compares the Player’s guess against letters in the word.
for (int i = 0; i < letters.Length; i++)
{
//If a letter is found, the parallel answers[i] array is set to true.
if (letters[i] == guess && answers[i] != true)
{
answers[i] = true;
lettersFound++;
message = 1;
if (prevGuess.Contains(guess) == false)
{
//This Stores the current guess in the previous guess array.
prevGuess[numGuess] = guess;
//This increase the index to the next spot.
numGuess++;
}
}
//If a letter is found, but it has already been found once.
else if (letters[i] == guess && answers[i] == true)
{
message = 3;
}
}
//If the Player’s guess is not contained in the word,
//they lose a life and a sound is played.
if (letters.Contains(guess) == false && prevGuess.Contains(guess) == false)
{
lives–;
SystemSounds.Hand.Play();
message = 2;
//This Stores the current guess in the previous guess array.
prevGuess[numGuess] = guess;
//This increase the index to the next spot.
numGuess++;
}
//If the Player’s guess is wrong, but it has already been guessed, you don’t lose a life.
else if (letters.Contains(guess) == false && prevGuess.Contains(guess) == true)
{
message = 3;
}
//The Player wins when all the letters have been found.
if (lettersFound == letters.Length)
{
win = true;
lives = 9;
wins++;
}
}
}
/// <summary>
/// This Method is triggered if either the Player has found all the letters,
/// the Player has run out of lives, or if the Escape key is pressed.
/// </summary>
static void GameOver()
{
//The game is over when lives is less than or equal to 0
if (lives <= 0)
{
WriteLine("\n You Lose! The word was {0}!", wordToGuess);
WriteLine("\n You have Won {0} out of {1} Games.", wins, games);
}
//You win when you find all the letters
else if (win == true)
{
WriteLine("\n You Win! You Found the Word!");
WriteLine("\n You have Won {0} out of {1} Games.", wins, games);
SystemSounds.Asterisk.Play();
}
}
/// <summary>
/// This Method is triggered after a GameOver or if the Escape Key is Pressed.
/// </summary>
static void Quit()
{
//Asks the Player if they would like to play again.
Write("\n\n Would you like to Play Again? (y/n) ");
#region Quit?
kb = ReadKey(); //Listens for Key
if (kb.Key == ConsoleKey.N) //Ends the Program
{
isPlaying = false;
}
else if (kb.Key == ConsoleKey.Y) //Restarts the program
{
NewGame(); gamePlay = true;
}
else //Tells the Player they are stupid and then Quits the program
{
WriteLine("\n You didn’t hit Y or N. " +
"\n I’m going to assume you said No. " +
"\n Press Any Key Quit…\a");
ReadKey();
isPlaying = false; //Ends Program
}
#endregion
}
/// <summary>
/// This Method Writes Game information to the Console.
/// Available information:
/// * The Word to Guess.
/// * The Number of Letters that have been found.
/// * Letters that the Player has already guessed.
/// * The Number of Words that have been played.
/// * A list of words that have already been played.
/// </summary>
static void Debug()
{
WriteLine("\n The word is {0}", wordToGuess);
WriteLine("\n {0} of {1} Letters Found.", lettersFound, wordToGuess.Length);
WriteLine(" Letters that have already been guessed: ");
Write(" -");
for (int i = 0; i < numGuess; i++)
{
Write(" {0},", prevGuess[i]);
}
WriteLine("\n\n {0} of {1} Words have been played.", numWordsPlayed, words.Length);
WriteLine(" The Words that have been played are: ");
Write(" -");
for (int i = 0; i < wordsPlayed.Length; i++)
{
if (wordsPlayed[i] == true)
{
Write(" {0},", words[i]);
}
}
WriteLine();
}
}
}
[/code]