This
chapter will cover queues and pointers..
In C++
we have the ability to access memory directly through
pointers. Pointers hold the address for the memory that it
points to. Pointers are made to only point to one
type. Consider the following code:
int
*Ptr;
int
Number = 32;
*Ptr =
Number;
cout<<*Ptr;
output:
32
The
above code sets up an integer pointer. It then is set to
point to Number. If you look at the address it will look
similar to:
0x0066fddc
The above may look like gibberish but it is actually a hexadecimal number. The hexadecimal number system is base 16, 0 - f. hexadecimal is used often because all binary values can easily be translated to hexadecimal. Consider the following:
Base 10 value | Base 2 or binary value | Base 16 or hexadecimal value |
45 | 101101 | 2d |
To access the values in the address a pointer points to, write an asterisk (*) in front of it. To declare a pointer write it's type followed by an *, followed by it's name.
To
access members of an object that a pointer uses you'll need the
-> operator. Consider the following:
class
CPlayer
{
int Score;
char name[25];
}
CPlayer
Player1;
Player1.Score
= 20;
"Hello">>Player1.name;
CPlayer
*Ptr;
Ptr =
Player1;
cout<<Ptr->Score;
output:
20
In England if you told somebody to get in line they would look at you like you have three heads. In England they use the word queue instead of line. Consider that the next time your in a queue to see a movie. |
A queue is a dynamic way to use memory. In our next game we could make an array of twenty players and even if their are only two players twenty players worth of memory will be used by the program. Instead of an array we will use a queue. When a player joins, the game memory will be allocated for them. When a player leaves the game the memory will be deallocated and returned to the operating system. A queue is structured as follows:
Most queues don't point
both ways but for our game they are necessary. Front, Rear,
Previous, and Link are all pointers to nodes. If the node
is at the front we make previous = 0 and Front = to the memory
address of the front node. To make a new node we will use
the new operator. To delete a node we will use the delete
operator like this:
CPlayer *Player1 = new
CPlayer;
delete Player1;
If the node is at the
end we have the rear point to the node and Rear point to the rear
node. Here's code from our queue:
class CCountries
{
class CPlayer
{
friend CCountries;
private:
CPlayer
*Link;
CPlayer
*Previous;
int Armies;
int Money;
int Territory;
int Technology;
int UpgradeCost;
char Name[15];
};
private:
CPlayer *Rear;
public:
CPlayer *Front;
//Set the
Front and rear to point to 0
//////////////////////////////////////
CCountries()
{
Front = 0;
//make front null
Rear = 0; //make
rear null
char Exit = 'N'; // Y or N value
AddPlayer(Total);
while(toupper(Exit) != 'Y')
{
Total++;
AddPlayer(Total);
cout<<"Are you the last player ((Y)es or (N)o)?
";
cin>>Exit;
}
}
//The
destructor that deletes any nodes on the queue
////////////////////////////////////////////////////
~CCountries()
{
CPlayer
*Current; //The current node to delete
CPlayer
*Next; //The next node to delete
Current = Front; //start Current in the front
//Until the
list is empty delete the nodes
while(Current!=0)
{
Next =
Current->Link; //setup the next node
delete
Current; //delete the current node
Current =
Next; //prepare for the next loop run
}
}
//Inserts a
Player at the rear
/////////////////////////////
void
Enqueue(CPlayer Player)
{
CPlayer
*Ptr; //Make a Ptr for the new node
Ptr = new
CPlayer; // create a queuenode and point to it with Ptr
*Ptr =
Player;
Ptr->Link =
0; //start the pointer at 0
Ptr->Previous
= Rear; //point to the previous player
//If the node
isn't empty set the last link to point to the new node
if (Front != 0)
Rear->Link = Ptr;
//else make the
Front point to it.
else
Front =
Ptr;
//Make the rear
point to the new node
Rear = Ptr;
}
//Delete the
first player and return the data
/////////////////////////////////////////////
CPlayer Dequeue()
{
CPlayer Player;
//the return value
CPlayer *Ptr;
//Will point to the first node
//If there
are nodes delete the first one
if (Front != 0)
{
Ptr =
Front; //Have the Pointer aim at the Front
Player =
*Ptr; //Copy the player
Front =
Ptr->Link; //Make Front point to the next node in line
delete
Ptr; //delete the node
Front->Previous = 0; // set to 0
if
(Front==0) //If the list is empty make the rear point to 0
Rear
= 0;
}
//else print an
error
else
cout<<"Hey man, there aren't any Players
here!"<<endl;
return
Player; //Return the deleted Player
}
private:
// This will
Delete a Player
////////////////////////////
bool Dead(CPlayer
*Victim)
{
CPlayer
*Current;
cout<<Victim->Name<<" you are dead!"<<endl;
// Check to
make sure is is not the first entry
if(Victim->Previous != 0)
{
Current =
Victim->Previous;
Current->Link = Victim->Link;
}
else // else
change the rear
{
Front =
Victim->Link;
}
// Check to
make sure it is not the last entry
if(Victim->Link != 0)
{
Current =
Victim->Link;
Current->Previous = Victim->Previous;
}
else // else
change the Rear
{
Rear =
Victim->Previous;
}
delete Victim;
return FALSE;
}
// This adds a
player
void
AddPlayer(int Total)
{
CPlayer
*Ptr; //Make a Ptr for the new node
Ptr = new
CPlayer; // create a queuenode and point to it with Ptr
Ptr->Armies =
5;
Ptr->Money =
5;
Ptr->Territory = 5;
Ptr->Technology = 1;
Ptr->UpgradeCost = 5;
cout<<"Player "<<Total<<" What
is your name? ";
cin>>Ptr->Name;
Ptr->Link = 0; //start the pointer at 0
//If the node
isn't empty, set the last link to point to the new node
if (Front != 0)
{
Ptr->Previous = Rear;
Rear->Link = Ptr;
}
//else make the
Front point to it.
else
{
Ptr->Previous = 0;
Front =
Ptr;
}
//Make the rear
point to the new node
Rear = Ptr;
}
};
The biggest concern of queues is memory leaks. A memory leak is when you create a node that isn't accessible. Say we dequeued a node but forgot to delete it. We would no longer have a pointer to it but it would still exist. This can slowly eat away your memory until either your program crashes, your program exits, or your computer crashes. If you use a Microsoft compiler the program will notice the problem and exit with an error. If you use the Borland compiler your other memory and then your code stored in RAM will be overwritten, this will lead to the computer crashing.
The first version of Windows 95, it was riddled with memory leaks. Your system would crash if you left it on for more than a day! This is one of the big reasons why I turn off my PC at work when I'm ready to go home. |
The easiest way I found to exit the game was through the void exit( int status ) function defined in stdlib.h. Exit must be passed an int value. All programs end with an integer value. EXIT_SUCCESS is defined in stdlib.h and equals 0. I'll use that to indicate that no error was encountered running the program.
To find out if pointers point to the same thing we can use the == operator. Higher pointers have higher memory addresses and lower pointers have lower memory addresses.
The queue allows us to have as many players as patience and memory allows. On my computer you would have to enter thousands of players before you ran out of memory. I doubt you'll ever reach the maximum capacity of players.
As you will see, very little of the code is changed from the last program. This saved me programming time. Reuse code as often as possible.
Now here is Multiplayer Conquest:
< Header > < Main > < Program >
// country6a.h
// This will hold the
class definitions for countries
// Copyright (c) 1998
Forest J. Handford
/////////////////////////////////////////////////////
#ifndef Country6a_h
#define Country6a_h
#include
<string.h>
#include
<stdlib.h>
#define TRUE 1
#define FALSE 0
int Total = 1;
// A global count of the total players
int AddArmies = 5; // A
global count of the number of armies to win after a kill
class CCountries
{
class CPlayer
{
friend CCountries;
private:
CPlayer
*Link;
CPlayer
*Previous;
int Armies;
int Money;
int Territory;
int Technology;
int UpgradeCost;
char Name[15];
// This will
check for to many territories
void
CountryLoss()
{
if(Armies
< Territory)
{
cout<<"Sorry "<<Name<<", you
don't have enough armies to keep "
<<"your territories! They are"
<<endl<<"reduced to
"<<Armies<<"."
<<endl;
Territory = Armies;
}
}
public:
int
ShowArmies()
{
return
(Armies);
}
// This
writes the name of the given player
///////////////////////////////////////////
void WriteName()
{
cout<<Name;
}
// This will
sell land to the player
////////////////////////////////////
void Grow()
{
char YorN
= 'y'; // For Yes or No answers
// Loop
as long as they want to grow
for(int
Count=0; tolower(YorN) == 'y'; Count++)
{
//
They must have at least one army for every territory
if
(Armies <= Territory)
{
cout<<"You need more armies for more
land."<<endl;
break;
}
//
Sell the land and tell the players
else
if(Money > 0)
{
Money -= 1;
Territory += 1;
cout<<"You now have "<<Territory
<<" States and "<<Money<<"
money."<<endl;
// Don't stay here if they are out of land or money
if((Armies <= Territory) || (Money <= 0))
{
break;
}
cout<<"Would you like to buy another ((Y)es or (N)o)?
";
cin>>YorN;
}
//
If they are out of money tell them and exit
else
{
cout<<"You are out of money!"<<endl;
break;
}
}
}
// This will
sell upgrades
//////////////////////////
void Upgrade()
{
char YorN
= 'y';
// Loop
till the user is out of money or is bored
while(tolower(YorN) == 'y')
{
//
Take their money if they have any
if(Money >= UpgradeCost)
{
Money -= UpgradeCost;
UpgradeCost *= 2;
Technology += 1;
cout<<"You now have "<<Technology
<<" Upgrades and "<<Money<<"
money."<<endl;
cout<<"Upgrades now cost you :
"<<UpgradeCost<<"."<<endl;
}
else
// Scold them for having no money
{
cout<<"You don't have enough money. You need
"<<UpgradeCost<<"."
<<endl;
}
//
break if they run out of money
if(Money <= UpgradeCost)
{
break;
}
// ask if they want more
cout<<"Would you like to upgrade again ((Y)es or
(N)o)? ";
cin>>YorN;
}
}
// This will
sell land back
///////////////////////////
void Shrink()
{
char YorN
= 'y';
while(tolower(YorN) == 'y')
{
if(Territory > 0)
{
Money += 1;
Territory -= 1;
cout<<"You now have "<<Territory
<<" States and "<<Money<<"
money."<<endl;
cout<<"Would you like to sell another ((Y)es or (N)o)?
";
cin>>YorN;
}
else
{
cout<<"You are out of money!"<<endl;
break;
}
}
}
// This adds
stuff for the player
/////////////////////////////////
void AddStuff()
{
Armies +=
Territory;
Money +=
Territory;
cout<<Name<<" now has "<<Armies
<<" armies and "<<Money<<" money
from ";
cout<<Name<<"'s
states."<<endl<<endl;
}
};
private:
CPlayer *Rear;
public:
CPlayer *Front;
//Set the
Front and rear to point to 0
//////////////////////////////////////
CCountries()
{
Front = 0;
//make front null
Rear = 0; //make
rear null
char Exit = 'N'; // Y or N value
AddPlayer(Total);
while(toupper(Exit) != 'Y')
{
Total++;
AddPlayer(Total);
cout<<"Are you the last player ((Y)es or (N)o)?
";
cin>>Exit;
}
}
//The
destructor that deletes any nodes on the queue
////////////////////////////////////////////////////
~CCountries()
{
CPlayer
*Current; //The current node to delete
CPlayer
*Next; //The next node to delete
Current = Front; //start Current in the front
//Until the
list is empty delete the nodes
while(Current!=0)
{
Next =
Current->Link; //setup the next node
delete
Current; //delete the current node
Current =
Next; //prepare for the next loop run
}
}
// This will
let the player attack
//////////////////////////////////
bool Attack()
{
char Exit = 'Y';
CPlayer *Victim;
char
VictimName[15]; // This will hold the victim's name
bool
Flag; // True or flase value
bool DeathFlag =
FALSE; // True or false value
bool Win =
FALSE;
while
(toupper(Exit) == 'Y')
{
Victim =
Front;
Flag =
FALSE;
cout<<"What Player do you want to attack? ";
cin>>VictimName;
if (
strcmp(VictimName,Front->Name) == 0)
{
cout<<"If you want to fight your self be my
guest!"<<endl;
}
Flag = FALSE; // Start Flag at FALSE
for(int
Player = 0; Player < Total - 1; Player ++)
{
//
Don't open a link = to 0.
if
(Victim->Link != 0) Victim = Victim->Link;
else
{
Flag = FALSE;
break;
}
if (strcmp(VictimName,Victim->Name) == 0)
{
Flag = TRUE;
break;
}
}
if
(Flag == FALSE)
{
cout<<"No such player, Try again."<<endl;
continue; // This will go skip the rest of the loop and start
again at the top
}
int
AttackerArmies; //This will hold the attacker's # of armies
int
VictimArmies; //This will hold the Victim's # of armies
char YorN
= 'Y';
while(toupper(YorN) == 'Y')
{
AttackerArmies = Front->Armies - 1 - (Victim->Armies *
Victim->Technology / 5);
VictimArmies = Victim->Armies - 1 - (Front->Armies *
Front->Technology / 5);
Front->Armies = AttackerArmies;
Victim->Armies = VictimArmies;
if(Victim->Armies < 1)
{
Win = Dead(Victim);
End();
DeathFlag = TRUE;
if(Front->Armies > 1)
{
Front->Armies += AddArmies;
AddArmies *= 2;
cout<<Front->Name<<" you now have
"<<Front->Armies<<" armies for your
kill."
<<endl;
return (TRUE);
}
else
{
Win = Dead(Front);
End();
if(DeathFlag == FALSE)
{
Victim->Armies += AddArmies;
AddArmies *= 2;
cout<<Victim->Name<<" you now have
"<<Victim->Armies<<" armies for your
kill."
<<endl;
}
DeathFlag = TRUE;
return (FALSE);
}
DeathFlag = TRUE;
if (Win == TRUE) return (FALSE);
}
else
if(Front->Armies < 1)
{
Win = Dead(Front);
End();
if(DeathFlag == FALSE)
{
Victim->Armies += AddArmies;
AddArmies *= 2;
cout<<Victim->Name<<" you now have
"<<Victim->Armies<<" armies for your
kill."
<<endl;
}
DeathFlag = TRUE;
return (FALSE);
}
if(End() == TRUE) break;
else
if(DeathFlag == FALSE)
{
Victim->CountryLoss();
Front->CountryLoss();
cout<<Front->Name<<" now has
"<<Front->Armies<<"
armies."<<endl;
cout<<Victim->Name<<" now has
"<<Victim->Armies<<"
armies."<<endl;
cout<<Front->Name<<", Would you like to
attack again ((Y)es or (N)o)? ";
cin>>YorN;
}
}
if(End() == FALSE)
{
cout<<"Would you like to attack another Player ((Y)es
or (N)o)? ";
cin>>Exit;
}
else
break;
}
return
(TRUE); // Flag success
}
//Inserts a
Player at the rear
/////////////////////////////
void
Enqueue(CPlayer Player)
{
CPlayer
*Ptr; //Make a Ptr for the new node
Ptr = new
CPlayer; // create a queuenode and point to it with Ptr
*Ptr =
Player;
Ptr->Link =
0; //start the pointer at 0
Ptr->Previous
= Rear; //point to the previous player
//If the node
isn't empty set the last link to point to the new node
if (Front != 0)
Rear->Link = Ptr;
//else make the
Front point to it.
else
Front =
Ptr;
//Make the rear
point to the new node
Rear = Ptr;
}
//Delete the
first player and return the data
/////////////////////////////////////////////
CPlayer Dequeue()
{
CPlayer Player;
//the return value
CPlayer *Ptr;
//Will point to the first node
//If there
are nodes delete the first one
if (Front != 0)
{
Ptr =
Front; //Have the Pointer aim at the Front
Player =
*Ptr; //Copy the player
Front =
Ptr->Link; //Make Front point to the next node in line
delete
Ptr; //delete the node
Front->Previous = 0; // set to 0
if
(Front==0) //If the list is empty make the rear point to 0
Rear
= 0;
}
//else print an
error
else
cout<<"Hey man, there aren't any Players
here!"<<endl;
return
(Player); //Return the deleted Player
}
//return TRUE
if the game is over
///////////////////////////////////
bool End()
{
if(Rear ==
Front)
{
cout<<endl
<<"\t"<<Front->Name<<" You
are the Winner!!!!!"
<<endl<<endl
<<"Thanks for Playing and Good Bye."<<endl;
exit(EXIT_SUCCESS); // end program
}
else return
(FALSE);
}
private:
// This will
Delete a Player
////////////////////////////
bool Dead(CPlayer
*Victim)
{
CPlayer
*Current;
cout<<Victim->Name<<" you are dead!"<<endl;
// Check to
make sure is is not the first entry
if(Victim->Previous != 0)
{
Current =
Victim->Previous;
Current->Link = Victim->Link;
}
else // else
change the rear
{
Front =
Victim->Link;
}
// Check to
make sure it is not the last entry
if(Victim->Link != 0)
{
Current =
Victim->Link;
Current->Previous = Victim->Previous;
}
else // else
change the Rear
{
Rear =
Victim->Previous;
}
delete Victim;
return
(FALSE);
}
// This adds a
player
void
AddPlayer(int Total)
{
CPlayer
*Ptr; //Make a Ptr for the new node
Ptr = new
CPlayer; // create a queuenode and point to it with Ptr
Ptr->Armies =
5;
Ptr->Money =
5;
Ptr->Territory = 5;
Ptr->Technology = 1;
Ptr->UpgradeCost = 5;
cout<<"Player "<<Total<<" What
is your name? ";
cin>>Ptr->Name;
Ptr->Link = 0; //start the pointer at 0
//If the node
isn't empty, set the last link to point to the new node
if (Front != 0)
{
Ptr->Previous = Rear;
Rear->Link = Ptr;
}
//else make the
Front point to it.
else
{
Ptr->Previous = 0;
Front =
Ptr;
}
//Make the rear
point to the new node
Rear = Ptr;
}
};
#endif
// main6a.cpp
// This holds the main
function for the second Conquest game of
// the C++ Game
Programmer's Tutorial.
// Copyright (c) Forest
J. Handford 1998
//////////////////////////////////////////////////////////////
#include
<iostream.h>
#include
<ctype.h>
#include
"Country6a.h"
void main()
{
char Answer;
bool EndGame =
FALSE;
cout<<endl<<"\tWelcome
to multi-player Conquest"<<endl
<<"\t\tBy Forest J. Handford"<<endl
<<"\t\tCopyright (c)
1998"<<endl<<endl;
CCountries Countries;
for(bool
Continue = TRUE; ; Countries.Enqueue( Countries.Dequeue()))
{
Answer = 'l';
Continue = TRUE;
Countries.Front->WriteName();
cout<<" take your seat."<<endl;
cout<<"You have
"<<Countries.Front->ShowArmies()<<"
armies."<<endl;
while
(tolower(Answer) != 'p' && Continue == TRUE)
{
cout<<"Would you like to (U)pgrade, (G)row, (S)hrink,
(A)ttack or (P)ass? ";
cin>>Answer;
if (
tolower(Answer) == 'a')
{
Continue = Countries.Attack();
if(Countries.End() == TRUE)
{
EndGame = TRUE;
break;
}
if(Continue == TRUE)
{
cout<<"Would you like to (U)pgrade, (G)row, (S)hrink
or (P)ass? ";
cin>>Answer;
if (tolower(Answer) == 'u')
{
Countries.Front->Upgrade();
cout<<"Would you like to (G)row, (S)hrink or (P)ass?
";
cin>>Answer;
if(tolower(Answer) == 'g')
{
Countries.Front->Grow();
}
else if(tolower(Answer) == 's')
{
Countries.Front->Shrink();
}
}
else if(tolower(Answer) == 'g')
{
Countries.Front->Grow();
cout<<"Would you like to (U)pgrade or (P)ass? ";
cin>>Answer;
if(tolower(Answer) == 'u')
{
Countries.Front->Upgrade();
}
}
else if(tolower(Answer) == 's')
{
Countries.Front->Shrink();
cout<<"Would you like to (U)pgrade or (P)ass? ";
cin>>Answer;
if(tolower(Answer) == 'u')
{
Countries.Front->Upgrade();
}
}
Answer = 'p';
}
}
else
if(tolower(Answer) == 'u')
{
Countries.Front->Upgrade();
cout<<"Would you like to (G)row, (S)hrink or (P)ass?
";
cin>>Answer;
if(tolower(Answer) == 'g')
{
Countries.Front->Grow();
}
else
if(tolower(Answer) == 's')
{
Countries.Front->Shrink();
}
Answer = 'p';
}
else
if(tolower(Answer) == 'g')
{
Countries.Front->Grow();
cout<<"Would you like to (U)pgrade or (P)ass? ";
cin>>Answer;
if(tolower(Answer) == 'u')
{
Countries.Front->Upgrade();
}
Answer = 'p';
}
else
if(tolower(Answer) == 's')
{
Countries.Front->Shrink();
cout<<"Would you like to (U)pgrade or (P)ass? ";
cin>>Answer;
if(tolower(Answer) == 'u')
{
Countries.Front->Upgrade();
}
Answer = 'p';
}
else
if(tolower(Answer) == 'p')
{
Answer = 'p';
}
else
{
cout<<"Sorry, bad input . . . try
again"<<endl;
}
}
if
(Countries.End() == TRUE) break;
else
Countries.Front->AddStuff();
}
}
For the next chapter we
are going to have a rubicks cube text game created by Joseph
Russell. That will be the last text game the tutorial
covers. After that we will dive head first into Windows and
DirectX programming. This will be a very slow process so
don't be surprised is it takes awhile for other chapters to be
posted.