Where do I place srand()? And other help needed

Started by
11 comments, last by Shakedown 16 years, 11 months ago
About half way down the code I call a vector and create some objects in it, and the default constructor gets called, initializing the stats of each object to a random number. The problem is I don't know where to put my srand() so each object doesn't have the same stats. ALSO, I don't know where to construct the object Player, so I just have it as a global constant/declaration. Becuase of this, I had to comment out the line in my default constructor that initializes a random name to the objects. What I want is for my vector to create some objects, each with a name picked randomly from the array. As of now, the objects created in the vector do not have names. So, where do I create my Player object so I can un-comment the line in my default constructor so my objects have default names? Thanks for your help!!! Ok, here's my entire code, all in one page:


// Second attempt at OOP
// Quite a bit more complex than CEnemy.  Intends to be a full program.

#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;

/////////////////////////////////////////////////////////////// Classes /////////////////////////////////////////////////////////////////////////////

class CCharacter
{
      private:
              int health;
              int power;
              int armor;
              string name;
      public:
             CCharacter();
      
             void setAll( int hp, int ap, int def );
             
             void setHealth( int hp );
             
             void setPower( int ap );
             
             void setArmor( int def );
             
             void setName( string nm );

             void displayName();

             void displayStats();
             
             //~CCharacter();
             
};

///////////////////////////////////////////////////////////// End of Classes ///////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////// Function Protoypes ///////////////////////////////////////////////////////////////////////

void SinglePlayer();

void GenEnemies();

void Fight( vector<CCharacter> &vGenEn );

int PlayerStats( int a, int b, int c );

void randomStats( int& x, int& y, int& z );

void Info();

void Exit();

//////////////////////////////////////////////////////////// End of Prototypes ///////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////// Global Constants ///////////////////////////////////////////////////////////////////////

CCharacter Player;

string enemyname[] = { "Snake", "Spider", "Oooze", "Slime", "Troll", "Ogre", "Gargoyle", "Chimaera", "Dragon", "Orc" };

///////////////////////////////////////////////////////// End of Global Constants ///////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////// MAIN ////////////////////////////////////////////////////////////////////////////////

int main(){
    
    srand( time ( NULL ) );
    
    int mainmenu;
    
    system("CLS");
    cout << "\tWelcome!" << endl
         << " What would you like to do? " << endl
         << "1. Single Player" << endl
         << "2. Generate Enemies" << endl
         << "3. Info" << endl
         << "4. Exit" << endl
         << " Selection : ";
    cin >> mainmenu;
    
    switch( mainmenu )
    {
            case 1:
                 SinglePlayer();
                 break;
            case 2:
                 GenEnemies();
                 break;
            case 3:
                 Info();
                 break;
            case 4:
                 Exit();
                 break;
            default:
                    cout << "Internal Error -> Invalid Input -> Exiting ... " << endl << endl;
                    break;
    }
            
    
return 0;
}

///////////////////////////////////////////////////////////// End of MAIN ////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////// Class Definitions ///////////////////////////////////////////////////////////////////////

/*
CCharacter::CCharacter( int hp, int ap, int def )
{
     health = hp;
     power = ap;
     armor = def;
     }
*/

CCharacter::CCharacter()
{
     //name = enemyname[ rand() % 10 ];
     health = rand() % 11 + 10;
     power = rand() % 11 + 10;
     armor = rand() % 11 + 10;
}
     
void CCharacter::setAll( int hp, int ap, int def )
{
     health = hp;
     power = ap;
     armor = def;
     }
     
void CCharacter::setHealth( int hp )
{
     health = hp;
     }
     
void CCharacter::setPower( int ap )
{
     power = ap;
     }
     
void CCharacter::setArmor( int def )
{
     armor = def;
     }
     
void CCharacter::setName( string nm )
{
     name = nm;
     }

void CCharacter::displayName()
{
     cout << name;
     }     

void CCharacter::displayStats()
{
     cout << "  Name = " << name << endl
          << "Health = " << health << endl
          << " Power = " << power << endl
          << " Armor = " << armor << endl << endl;
     }

///////////////////////////////////////////////////////////////// End of Class Definitions ////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////// Function Definitions ///////////////////////////////////////////////////////////////

void SinglePlayer()
{     
     string nm;                                                    // Name
     
     int a;                                                        // temporary Health
     int b;                                                        // temporary Attack Power
     int c;                                                        // temporary Armor
     
     system("CLS");
     
     cout << "\n\tWelcome to Single Player!" << endl << endl
          << "Here you will create your character." << endl
          << "Every character, including enemies, has 3 attributes: " << endl
          << "\tHealth, Attack Power, and Armor." << endl << endl;
          
     //CCharacter Player;
     
     cout << "What would you like your characters name to be? ";
     cin >> nm;
     Player.setName( nm );
     
     randomStats( a, b, c );
     PlayerStats( a, b, c );
     
     system("PAUSE");
     
     main();
     
}

int PlayerStats( int a, int b, int c )
{
     char YorN;
     
     system("CLS");
     
     cout << "Here are your characters attributes: " << endl << endl
          << "  Name = ";
          Player.displayName();
     cout << endl;
     cout << "Health = " << a << endl
          << " Power = " << b << endl
          << " Armor = " << c << endl << endl
          << "Would you like to keep these (Y or N)? ";
     cin >> YorN;
     
     if ( tolower(YorN) == 'y' )
     {
          Player.setAll( a, b, c );
          system("CLS");
          cout << "You kept these attributes: " << endl;
          Player.displayStats();
          }
     
     else if ( tolower(YorN) == 'n' )
     {
          randomStats( a, b, c );
          PlayerStats( a, b, c );
          }

     else
     {
          cout << "Internal Error -> Invalid Input -> Exiting ... " << endl << endl;
          exit ( 0 );
          }
}

void GenEnemies()
{
     
     int GenEn = 0;
     int aa;                                 // Temp enemy health
     int bb;                                 // Temp enemy attack
     int cc;                                 // Temp enemy defense
     
     system("CLS");
     
     cout << "How many enemies would you like to generate? ";
     cin >> GenEn;
     
     srand( time( NULL ) );
     
     vector<CCharacter> vGenEn( GenEn );  // <B> Here is where my default constructors are called, but each object created has the same stats.  It doesn't see any srand(). </B>
     /*
     
     for ( int i = --GenEn; i >= 0; --i )
     {
         randomStats( aa, bb, cc );
         vGenEn.setAll( aa, bb, cc );
         vGenEn.setName( enemyname[ rand() % 10 ] );
         vGenEn.displayStats();
         cout << endl;
         }
     */    
     system("PAUSE");
     
     Fight( vGenEn );
         
}

void Fight( vector<CCharacter> &vGenEn )
{
     system("CLS");
     
     cout << "\tTime to FIGHT!" << endl;
     cout << "Here are your enemies: " << endl;
     
     for( vector<CCharacter>::iterator enemy = vGenEn.begin(); enemy != vGenEn.end(); ++enemy )
     {
          cout << "\t";
          enemy -> displayName();
          enemy -> displayStats();
          //enemy -> Combat();
          }
     
     cout << endl << endl;
          
     system("PAUSE");
     
     system("CLS");
     
     for ( vector<CCharacter>::iterator enemy = vGenEn.begin(); enemy != vGenEn.end(); ++enemy )
     {
     cout << "You are fighting a ";
     enemy -> displayName();
     cout << "\n";
           }
     
     system("PAUSE");
          
}
/*
void Combat()
{
     if ( 
*/     
void randomStats( int& x, int& y, int& z )
{
     x = rand() % 11 + 10;
     y = rand() % 11 + 10;
     z = rand() % 11 + 10;    
}

void Info()
{
     cout << endl << "\t INFO: \t Made by Nate Wolfe" << endl << endl;
     system("PAUSE");
     main();
     
}

void Exit()
{
     cout << endl << "\t Thanks for playing!" << endl << endl;
     system("PAUSE");
     exit( 0 );
     
}

//////////////////////////////////////////////////////// End of Function Definitions ////////////////////////////////////////////////////////////////


Advertisement
srand() can be used in the first script that gets run, usually main() or for more Windows-based programs, WinMain(). Some people call if in their forward declarations, but that is considered poor practice.

Since you have ctime included, you can use srand(unsigned int(time(0)));
The format you used is correct but not entirely efficient standard.

The place that you put is is perfectly fine. I don't see why you would have any problem with it there as long as main() ran first.

As for your other problem, I'm stumped.
This man made my day: http://www.gamedev.net/community/forums/topic.asp?topic_id=523021
I have it placed in several different places, and I've tried any place I could think of, even in the default constructor itself. Nothing seems to work.
Quote:Original post by Shakedown
I have it placed in several different places, and I've tried any place I could think of, even in the default constructor itself. Nothing seems to work.


Make sure you pass it a seed that's not the same everytime you run your program (an above example uses time, which is the most common way to do it).
Quote:Original post by Replicon
Quote:Original post by Shakedown
I have it placed in several different places, and I've tried any place I could think of, even in the default constructor itself. Nothing seems to work.


Make sure you pass it a seed that's not the same everytime you run your program (an above example uses time, which is the most common way to do it).


Uhh...I don't know what that means in terms of my code. I've always seeded time for srand and I've always had it work fine, just a matter of where to place it. Perhaps you could give me an example. Also, it seems that it wouldn't matter what you use to seed random, but rather where you place it.

So I still need help, hah thank you
It shouldn't matter where you put srand() really because in your code its using time() and that is just a function that gets the internal tick count of the clock on your computer which will never be the same for each instance you run the program. Typically though you don't want srand() to be called more than once in your application as calling it once is more than you will ever need.

If that however, still does not solve your problem stick srand() inside the constructor for CCharacter and see if that helps.

As for your Player creation problem I would suggest the use of pointers to eliminate the creation of your player at the start of the program.

//Global pointer to a CCharater
CCharacter * myCharacter = 0;

//Create the character when needed using the proper constructor
myCharacter = new CCharacter( *input params* ); // puts the object into RAM

//Delete the character when the program is about to exit;

void Exit()
{
.
.
.
delete myCharacter; // removes the character from RAM
myCharacter = 0;
}


This way you can create your player any time you want to. Just make sure to delete the object when you exit the program. Also note that you need to use the '->' arrow operator when dealing with pointers to access member functions in case you are new to them.
Quote:Original post by zorg19
It shouldn't matter where you put srand() really because in your code its using time() and that is just a function that gets the internal tick count of the clock on your computer which will never be the same for each instance you run the program. Typically though you don't want srand() to be called more than once in your application as calling it once is more than you will ever need.

If that however, still does not solve your problem stick srand() inside the constructor for CCharacter and see if that helps.

As for your Player creation problem I would suggest the use of pointers to eliminate the creation of your player at the start of the program.

//Global pointer to a CCharater
CCharacter * myCharacter = 0;

//Create the character when needed using the proper constructor
myCharacter = new CCharacter( *input params* ); // puts the object into RAM

//Delete the character when the program is about to exit;

void Exit()
{
.
.
.
delete myCharacter; // removes the character from RAM
myCharacter = 0;
}


This way you can create your player any time you want to. Just make sure to delete the object when you exit the program. Also note that you need to use the '->' arrow operator when dealing with pointers to access member functions in case you are new to them.


Hmm...well as for the srand, I did put it in my default constructor and still it would not work.

And for the pointer, the reason I have the "CCharacter Player" declared globally is so all my functions can access it. So by declaring my pointer globally, then creating the Player inside another function, I could access Player from other functions via the pointer. Correct?

But, would that solve the problem for my default constructor initializing names?



You only need to seed the random number generator once. From there, each call to rand() will be a random number. Typically, srand() is called from main() or some other similar function. In general, only call it once unless you really, deeply understand how deterministic pseudorandom number generators work, and you really truly know that you need to reseed. Calling srand() repeatedly, and especially in a place like a class constructor, will probably give you undesirable (read: not-random) results.

As for the original question, just construct Player inside of main(), after the srand() call. Instead of having Player be a global variable, pass a reference to it as a parameter to your various functions. For example, SinglePlayer() would become void SinglePlayer(CCharacter& Player), and main() would contain something like this:

CCharacter player;SinglePlayer(player);


That will give you some decent control over when the player object is actually created. As a bonus, it doesn't require you to manually create the object with new, and you don't have to remember to get rid of it with delete. (Actually, as a general rule, you should almost never use raw pointers anyways. Always use a "smart pointer" class like std::auto_ptr or one of the Boost smart pointers. This will help you avoid problems with memory leaks and other nasty bugs.)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by ShinkaFudan
srand() can be used in the first script that gets run, usually main() or for more Windows-based programs, WinMain().


The correct term is function. Referring to C++ code as a "script" isn't technically accurate (aside from the fact that the term "script" is fairly poorly defined in the first place).

Quote:Original post by ShinkaFudan
Some people call if in their forward declarations, but that is considered poor practice.


This statement is gibberish. A forward declaration can technically be placed at many different scopes, and in well-written C++ code, forwards often are scattered around in various places, based on the principle of "declare nearest to the point of usage."

It's common for forward declarations (including function prototypes) to be made at global scope, e.g. near the top of a code file. However, it is illegal to include executable code statements at global scope in C++. There's no way you can call a function (such as srand()) in amongst the forward declares/prototypes of a program in C++.


Quote:Original post by ShinkaFudan
Since you have ctime included, you can use srand(unsigned int(time(0)));
The format you used is correct but not entirely efficient standard.


This is a poor suggestion. Technically, it reduces to the exact same code as the OP's (namely: srand(time(NULL));) because NULL in this context is being passed as a pointer value to time(), and by the rules of the C++ language, is equivalent to 0 in this context. However, it is more correct to use NULL over a literal 0, because technically a literal integer 0 is not the same thing at all as a null pointer.

Moreover, the added unsigned int cast is superfluous and actually potentially deadly. On a platform where time_t does not meaningfully cast to an unsigned int (which is thoroughly legal), you are actually preventing the compiler from notifying you. If the time_t to unsigned int conversion is bogus, the compiler will warn you when using the code srand(time(NULL));. However, using your suggestion, this warning will be suppressed due to the cast.

All that aside, using C-style casts in C++ is very poor form. Use static_cast, dynamic_cast, or reinterpret_cast.

Lastly, there is absolutely nothing "inefficient" or "nonstandard" about the srand(time(NULL)); syntax. In fact, it's a very common idiom from C programs, and has largely carried over into C++ as well since C++ does not provide an alternative standard random number generator.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by ApochPiQ
You only need to seed the random number generator once. From there, each call to rand() will be a random number. Typically, srand() is called from main() or some other similar function. In general, only call it once unless you really, deeply understand how deterministic pseudorandom number generators work, and you really truly know that you need to reseed. Calling srand() repeatedly, and especially in a place like a class constructor, will probably give you undesirable (read: not-random) results.

As for the original question, just construct Player inside of main(), after the srand() call. Instead of having Player be a global variable, pass a reference to it as a parameter to your various functions. For example, SinglePlayer() would become void SinglePlayer(CCharacter& Player), and main() would contain something like this:

CCharacter player;SinglePlayer(player);


That will give you some decent control over when the player object is actually created. As a bonus, it doesn't require you to manually create the object with new, and you don't have to remember to get rid of it with delete. (Actually, as a general rule, you should almost never use raw pointers anyways. Always use a "smart pointer" class like std::auto_ptr or one of the Boost smart pointers. This will help you avoid problems with memory leaks and other nasty bugs.)


So I'll go and take out all the srand()'s and leave only 1 in my main(), hopefully that will work. Ha so I guess it came back to passing through parameters.

I'll give it a go, but that still doesn't resolve my problem with the auto-naming in the default constructor does it? (I havn't had a chance to implement any of this yet, so perhaps I'm wrong)

This topic is closed to new replies.

Advertisement