by Forest J. Handford

In this chapter we will develop a Hangman game.

To start off this chapter, we are going to learn about the ++ and the -- operator. They are unique operators that can come in very handy. If ++ is written after a variable then the variable becomes 1 more than it's previous value. If -- is written after a variable the variable becomes 1 less than it's previous value. The operators can also come before the variable, but that changes what they do. Consider the following code:
int Var = 3;
Var--;

Var will equal 2 after the second statement.  The developers of C++, Bell Labs, were fans of fancy code.  Look at the following code:
int Count = 0;
while(Count < StringSize)
{
    Array[Count++] = Count;
}

The above code in the loop is equivalent to the following:
Array[Count] = Count;
Count = Count + 1;

The ++ and -- operator when preceding the variable has the highest precedence in the order of operations.  The ++ and -- operator following the variable has the lowest precedence in the order of operations and is executed after all other operations including the assignment operation!

Type casting is a way to change the type of a variable.  Consider the following code:
char Answer[20] = "34";
int Number = 34;
if (Number == Answer)
{
}

The above code will produce a compiler error because they are of different types.  To get around this we type cast.  To type cast write the type you want to change a variable to in parenthesis and in front of the variables name like this:
if(Number == (int) Answer)

Now let's go over the for loop.  The for loop is a very readable loop that has the following syntax:
for(code; condition; code)
{
}

The initial code is executed first and then the condition is checked.  If the condition is true the code within the braces is executed.  Finally the part of the code that is written after the condition is executed.  Then the condition is reevaluated.  After that it will execute the code within the braces again and then check the condition.  Here is an example:
for(int Count = 0; Count < 5; Count++)
{
    cout<<Count<<endl;
}

output:
0
1
2
3
4

Access to variables and constants can be confusing in C++ to beginner programmers.  Their are three access types dynamic, global and auto.

Type Definition Examples
auto Auto access variables and constants are created and accessible on declaration and are destroyed after the closing brace for the pair of braces that it was created in.  They are only accessible inside of the function they were declared unless they are passed to another function. void main()  
 
     int Var 
}
dynamic Dynamic variables and constants are created when a person uses the new operator.  They are destroyed when the program exits or when the user uses the delete operation on it.  Dynamic structures are accessible through pointers.  Pointer = new int;  
delete Pointer;
global Global variables are accessible anywhere in the program and exists from declaration and are destroyed when the program exits.  Any variable or constant in a header file or that is outside of braces is global. #define MAX 
int Variable  
void main()  
 
}


We won't use pointers or dynamic variables until a later chapter so don't worry about them.

In C++ you can create your own types using typedef.  Here is the syntax:
typedef type type Name;
Name Variable;

Here is an example:
typedef char String[15];
String Words[23];

The above makes an array of arrays.  In games this allows us to place strings in arrays as we will do in Hangman.

File use in C++ is our next topic.  We will use fstream.h to access files.  We can use two file streams in and out.  We need to declare a variable to access them.  For a stream that takes input from a file we need type ifstream and ofstream for file output.  These streams are classes.  We will learn how to use classes in the next chapter.  Classes can have special functions accessible using the class name followed by a period and the function name.  For file streams we use the open, close and peek functions.  open takes a file name and opens the file.  close closes a file.  peek returns the next character.  All ascii files end with a character known as the EOF or end of file character.  peek is usually used in a while condition to exit when EOF is found.  With input files we can use the right bit shift operator and with output files we can use the left bit shift operator.  Consider we have a file Numbers.dat that has the following:
123 35 234 88
980 65

We could use the following code to output the above numbers to the screen:
ifstream DatFile;
DatFile.open(Numbers.dat)
int Number;
while((C=Datfile.peek()) != EOF)
{
    DatFile>>Number;
    cout<<Number<<endl;
}
DatFile.close();

output:
123
35
234
88
980
65

DON'T FORGET TO CLOSE THE FILE!  Having files open uses a large amount of system resources and if you don't close it the file will be inaccessible till the program ends.
 
Escape sequences are used in strings to add special characters to a string.  \0 is an escape sequence for the null string and \n for a new line character.  To write the '\' character you must type '\\' .

Now here is Hangman:

< Code > < Data > < Program >

// main4a.cpp
// This is the source to hangman by Forest J. Handford
// Copyright (c) 1998
//////////////////////////////////////////////////////

#include <iostream.h> // For input/output
#include <fstream.h> // For file input/output
#include <string.h>  // For strcpy
#include <time.h>  // For time
#include <stdlib.h>  // For toupper and tolower

#define MAX_WORD_SIZE 15 // The max size for words
#define MAX_WORDS 255  // The max number of words

void LoadFile(); // Prototypes for our functions
void RunGame();
void DrawGallows(int State);
 
typedef char String[MAX_WORD_SIZE]; // Make a char type with 15 chars
String Words[MAX_WORDS - 1];  // This array will hold our words
int Count;       //word count

// the main function of our hangman game
void main()
{
 char Continue = 'Y'; // end game if = to 'N'
 cout<<"Welcome to Hangman . . . Don't lose your head!"<<endl;

 LoadFile(); // Load the data file

 // Continue the game as long as the player wants
 while(Continue == 'Y')
 {
  RunGame(); // Run the game
  cout<<endl<<"Do you want to play again (Yes or No)? ";
  cin>>Continue;
  Continue = toupper(Continue);
 }

 // say good bye
 cout<<endl<<"Thanks for playing."<<endl;
}

// This will load our file
void LoadFile()
{
 char C;    //Used to find EOF
 ifstream Datfile; //The in file

 Count=0; //Set count to 0
 
 // Open the data file
 Datfile.open("words.dat");

 //Loop till the end of file
 while((C=Datfile.peek()) != EOF)
 {
  // Get the next word and then increment Count
  Datfile>>Words[Count++];

  // If we surpass the max exit and tell the user
  if(Count > MAX_WORDS - 1)
  {
   cout<<endl<<"Too many words in the file, stopping with "
    <<MAX_WORDS<<" Words."<<endl;
   Count = MAX_WORDS;
   break;
  }

 }

 // We need to subtract one to get the correct number of words
 Count--;

 // Close the data file
 Datfile.close();

}

// This function will run the game
void RunGame()
{
 int Word;   // This will hold the subscript of our word
 int Size;   // This will hold the length of our word
 int State=1;  // This holds the game state
 int Subscript=0; // This will hold subscripts
 char Guess[MAX_WORD_SIZE]; // this will hold their current word
 char Copy[MAX_WORD_SIZE]; // This will hold a copy of the word
 char Letter;  //This will be their letter guess
 int Correct=0;  // This is a True/False value deciding if they got a good answer
 
 // Seed and create a random number
 srand((unsigned)time( NULL )); // use time as a seed
 Word = rand() % Count;

 // Make a local copy of the word
 strcpy(Copy,Words[Word]);

 Size = strlen(Words[Word]); // Get the word's size

 // Create a null terminated string to represent the word as the
 // player knows it.
 for(; Subscript < Size; Subscript++)
 {
  Guess[Subscript] = '-';
 }

 // insert the null character
 Guess[Subscript] = '\0';

 // Go till the player is hung
 while(State!=6)
 {
  DrawGallows(State); //Draw the gallows
  cout<<Guess<<endl; // Draw Guess

  cout<<"Guess a letter :";
  cin>>Letter;

  // We will use only lower case letters
  Letter = tolower(Letter);
 
  // Loop through the word
  for(Subscript = 0; Subscript < Size; Subscript++)
  {

   //if the guess is good tell the user and update Guess
   if(Copy[Subscript] == Letter)
   {
    Guess[Subscript] = Letter;
    Correct = 1;
    cout<<endl<<"Good Guess!";

    // If guess equals the word they won so exit
    if(strcmp(Words[Word],Guess) == 0)
    {
     cout<<endl<<"Yea, You survived and won!";
     return;
    }
   }
  }

  // If they didn't get aletter correct chide the user
  if(Correct == 0)
  {
   cout<<endl<<"Sorry, bad guess!";
   State++;
  }

  Correct = 0; // reset Correct

 }

 DrawGallows(State); //Draw the gallows at end of game
 //They lost if they are here so tell them the answer.
 cout<<"The word was : "<<Words[Word]<<endl<<endl;

}

// This will Draw the gallows according to the state
void DrawGallows(int State)
{
 if(State==6)
 {
  // The \\ will translate as '\' because it is a special char
  cout<<endl<<endl
   <<"   +----+     "<<endl
   <<"   |    |     "<<endl
   <<"   |    O     "<<endl
   <<"   |   /|\\   "<<endl
   <<"   |   / \\   "<<endl
   <<"   |Your Dead "<<endl
   <<"  ============"<<endl<<endl;
 }
 else if(State==5)
 {
  cout<<endl<<endl
   <<"   +----+  "<<endl
   <<"   |    |  "<<endl
   <<"   |    O  "<<endl
   <<"   |   /|\\ "<<endl
   <<"   |     \\ "<<endl
   <<"   |       "<<endl
   <<"  ============"<<endl<<endl;
 }
 else if(State==4)
 {
  cout<<endl<<endl
   <<"   +----+  "<<endl
   <<"   |    |  "<<endl
   <<"   |    O  "<<endl
   <<"   |   /|\\ "<<endl
   <<"   |       "<<endl
   <<"   |       "<<endl
   <<"  ============="<<endl<<endl;
 }
 else if(State==3)
 {
  cout<<endl<<endl
   <<"   +----+  "<<endl
   <<"   |    |  "<<endl
   <<"   |    O  "<<endl
   <<"   |   /|  "<<endl
   <<"   |       "<<endl
   <<"   |       "<<endl
   <<"  ============="<<endl<<endl;
 }
 else if(State==2)
 {
  cout<<endl<<endl
   <<"   +----+  "<<endl
   <<"   |    |  "<<endl
   <<"   |    O  "<<endl
   <<"   |    |  "<<endl
   <<"   |       "<<endl
   <<"   |       "<<endl
   <<"  ============="<<endl<<endl;
 }
 else if(State==1)
 {
  cout<<endl<<endl
   <<"   +----+  "<<endl
   <<"   |    |  "<<endl
   <<"   |       "<<endl
   <<"   |       "<<endl
   <<"   |       "<<endl
   <<"   |       "<<endl
   <<"  ============="<<endl<<endl;
 }

}

Header files can be used to define functions.  Everything declared in headers and all the include files can be used in any file they are included in.  To include a header that is in the same directory as the cpp file use " marks instead of <> .  If we had a file called my.h that had:
#include <iostream.h>
int Size = 4;
void Hello()
{
    cout<<"Hello"<<endl;
}

and had the following in a cpp file:
#include "my.h"
void main()
{
    cout<<Size<<endl;
    Hello();
}

the output would be:
4
Hello

To use the output stream we need to declare the file with the ofstream type.  We can output to the file using the << operator.  When we output to a file we must separate the output so that we can retrieve it easily.  The /t escape sequence adds a tab to output and /n to adds a new line.

I think we are ready for a utility to add remove words from a word file.  The ofstream's and ifstream's open functions will create a file even if the file doesn't exist.  In the hangman game I didn't allow for file errors.  If you wanted to close the program you could check to see if the count was equal to 0 after loading the words.  Here is our hangman file utility:

< Header > < Main > < Program >

// loadfile.h
// This is the header file to include to load files for
// hangman
// Copyright (c) 1998 Forest J. Handford
///////////////////////////////////////////////////////

#include <iostream.h> // For input/output
#include <fstream.h> // For file input/output

#define MAX_WORD_SIZE 15 // The max size for words
#define MAX_WORDS 255  // The max number of words

typedef char String[MAX_WORD_SIZE]; // The typedef for string
String Words[MAX_WORDS - 1];  // This array will hold our words
int Count;      //word count

// This will load our file
void LoadFile()
{
 char C;    //Used to find EOF
 ifstream Datfile; //The in file

 Count=0; //Set count to 0
 
 // Open the data file
 Datfile.open("words.dat");

 //Loop till the end of file
 while((C=Datfile.peek()) != EOF)
 {
  // Get the next word and then increment Count
  Datfile>>Words[Count++];

  // If we surpass the max exit and tell the user
  if(Count > MAX_WORDS - 1)
  {
   cout<<endl<<"Too many words in the file, stopping with "
    <<MAX_WORDS<<" Words."<<endl;
   Count = MAX_WORDS;
   break;
  }

 }

 // We need to subtract one to get the correct number of words
 Count--;

 // Close the data file
 Datfile.close();

}


// main4b.cpp
// This contains the program for adding and removing words
// to the hangman datfile.
// Copyright (c) 1998 Forest J. Handford
//////////////////////////////////////////////////////////

#include "loadfile.h" // For LoadFile() and headers.
#include <stdlib.h>  // For toupper()
#include <string.h>  // For strcpy() and strcmp()

void WriteWords();  // Prototype

// The main function to our editer
void main()
{
 LoadFile(); // Load the file

 ofstream DatFile; //The out file
 char Answer; //A Y or N value
 char Word[MAX_WORD_SIZE]; //This will hold a word
 int Size;     //This will hold the strings size
 int Loop;     // This will hold a subscript

 WriteWords();

 cout<<endl<<endl<<"Would you like to add any words (Yes or No)? ";
 cin>>Answer;

 // Loop to take new words
 while (toupper(Answer) == 'Y')
 {
  cout<<"What word would you like to enter, Please keep it below "
   <<endl<<MAX_WORD_SIZE<<" Charachters: ";
  cin>>Word;

  Size = strlen(Word);

  // Make the word lower case
  for(int Loop = 0; Size + 1 > Loop; Loop++)
  {
   Word[Loop] = tolower(Word[Loop]);
  }

  strcpy(Words[Count++],Word);

  cout<<endl<<"Would you like to add another word (Yes or No)? ";
  cin>>Answer;
 }

 cout<<endl<<"Would you like to remove a word (Yes or No)? ";
 cin>>Answer;

 // Loop to remove words
 while (toupper(Answer) == 'Y')
 {
  cout<<"What word do you want to remove? ";
  cin>>Word;
 
  Size = strlen(Word);

  // Make the word lower case
  for(int Loop = 0; Loop < Size +1; Loop++)
  {
   Word[Loop] = tolower(Word[Loop]);
  }

  // Look for the word and if found remove it
  for (Loop = 0; Loop < Count; Loop++)
  {
   // if strings are = remove it
   if (strcmp(Words[Loop],Word) == 0)
   {
    // adjust where the words are and reduce count's size
    strcpy(Words[Loop],Words[--Count]);
    strcpy(Words[Count],""); // replace with nothing
    break;
   }
  }

  cout<<endl<<"Would you like to remove another word (Yes or No)? ";
  cin>>Answer;
 }

 DatFile.open("words.dat"); //Open the file
 
 //Save the words
 for(Loop=0; Loop < Count - 1; Loop++)
 {
  DatFile<<Words[Loop];
  DatFile<<"\t";
  if (++Loop > Count) break;
  DatFile<<Words[Loop];
  DatFile<<"\t";
  if (++Loop > Count) break;
  DatFile<<Words[Loop];
  DatFile<<"\t";
  if (++Loop > Count) break;
  DatFile<<Words[Loop];
  DatFile<<"\t";
  if (++Loop > Count) break;
  DatFile<<Words[Loop];
  DatFile<<"\n";
 }

 DatFile.close(); //Close the file

 WriteWords();  //Write the words

 cout<<endl<<endl<<"Good Bye"<<endl;

}

// This will display all the words
void WriteWords()
{
 
 cout<<"The following are words in the file: "<<endl<<endl;

 for(int Loop=0; Loop < Count - 1; Loop++)
 {
  cout<<Words[Loop]<<"\t";
  if (++Loop > Count) break;
  cout<<Words[Loop]<<"\t";
  if (++Loop > Count) break;
  cout<<Words[Loop]<<"\t";
  if (++Loop > Count) break;
  cout<<Words[Loop]<<"\t";
  if (++Loop > Count) break;
  cout<<Words[Loop]<<"\n";
 }
}

In the next Chapter we will make a role playing game to work on classes.  This will lead up to the chapter after that, where we will create a strategy game.

PREVIOUS CHAPTER       HOME       NEXT CHAPTER