Endriu Golara
Endriu Golara

Reputation: 13

How to call one variable (which contains rand() function) and get always different number?

I'm writing a program (actually a game) about fighting with opponents on the arena. Getting down to business, I created the following header, which contains information about fighters:

#ifndef COMPETITOR_H_INCLUDED
#define COMPETITOR_H_INCLUDED

#include <cstdlib>

int ID = 0;

struct competitor
{
    std::string name;
    int health;
    int attack;
};

competitor player;

player.health = 25;
player.attack = (rand()%6)+1;


competitor opponent[2];

opponent[0] = {"Rat", 6, (rand()%4)+1};
opponent[1] = {"Drunkard", 10, (rand()%6)+1};

#endif // COMPETITOR_H_INCLUDED

And here I have my function I have problem with:

int fight()
{

    int number = 1;

    cout << "Your opponent is " << opponent[ID].name;
    cout << endl << "Your opponent's health: " << opponent[ID].health;
    cout << endl << "Your health: " << player.health << endl;

    while (opponent[ID].health > 0 || player.health > 0)
        {
            cout << endl << endl << "Round " << number << endl;

            cout << opponent[ID].name << " inflicts" << opponent[ID].attack << " damage, ";
            cout << "you have " << (player.health = player.health - opponent[ID].attack) << " health points" << endl;

            if (player.health <= 0) break;

            cout << player.name << " inflicts " << player.attack << " damage, ";
            cout << "your opponent has " << (opponent[ID].health = opponent[ID].health - player.attack) << " health points" << endl;

            if (opponent[ID].health <= 0) break;

            number++;
        }

    if (player.health > opponent[ID].health)
        {
            cout << endl << "Congratulations! You managed to defeat your opponent. Prepare for the next fight.";
            ID++;
        }

         else
        {
            cout << endl << "Unfortunately, you have been defeated. Start again.";
            ID = 0;
        }

        getch();

    }

I also have srand(time(NULL)); at the beginning of my main() function. Basically it works, on each program run attack values are different, but they are the same in every round. I have no idea, how to make them to be generated on every while loop. Every help is greatly appreciated.

Regards

Upvotes: 1

Views: 307

Answers (5)

Matthieu M.
Matthieu M.

Reputation: 299800

First of all, please do note that it is recommended NOT to use rand. Instead you should use the <random> header and its goodies. If you do not have C++11, there is a nearly one-to-one mapping with Boost.Random.

Then, to generate a random value:

  • Find yourself a seed (use of std::random_device is advised)
  • Create a pseudo-randomness engine (std::default_random_engine) and initialize it with the seed
  • Use an appropriate distribution (in your case, std::uniform_distribution_int<int>) and initialize its parameters (1, 6) for your player, for example.
  • Finally, each time you need a random number, ask your distribution to provide it by extracting randomness from the engine.

In code:

// Seed with a real random value, if available
std::random_device rd;

// Choose a random mean between 1 and 6
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(1, 6);

Then each call to uniform_dist(e1) will return a number between 1 and 6, with little to no bias. The important things are that the engine and distribution are long-lived => for a given random sequence you should always draw from the same engine and distribution. It is also perfectly fine to use the same engine with various distributions (there would be a single object otherwise).

Thus, adapted to your code:

using AttackDice = std::uniform_distribution_int<int>;

struct Competitor {
    std::string name;
    int health;
    AttackDice attackDice;
};

Competitor player = {"", 25, AttackDice{1, 6}};

opponent[0] = {"Rat", 6, AttackDice{1, 4}};
opponent[1] = {"Drunkard", 10, AttackDice{1, 6}};

And then in your main:

std::random_device rd;
std::default_random_engine myRandomEngine(rd());

// .. stuff

while (opponent[ID].health > 0 || player.health > 0)
    {
        cout << endl << endl << "Round " << number << endl;

        int playerAttack = player.attackDice(myRandomEngine);
        int opponentAttack = opponent[ID].attackDice(myRandomEngine);

        // .. resolve
    }

// .. stuff

Note: you should only cast the dice one per iteration of the loop, most probably ;)

Upvotes: 1

MSalters
MSalters

Reputation: 179799

You're using the old rand function. That's not the best design. C++ nowadays has a better concept: separate the random generator and the random distribution.

In your case, .attack really should be a std::uniform_int_distribution<>. Initialize it with min/max values {1, N} where N varies between the different opponents.

Set up a single random generator: std::mt19937 random; and get a random damage value like this: int damage = opponent[ID].attack(random);

Upvotes: 0

Xin
Xin

Reputation: 1330

Since you tagged your question with C++11, in C++11 please don't use rand, it's not a good random function. Use the newly added instead.

Here is some snippet from my code base:

#include <random>
using namespace std;

auto engine = mt19937{ random_device()() };
function<int(int)> rand = [=](int range) mutable {
  auto idist = uniform_int_distribution<int>(0, range);
  return idist(engine);
};

auto myRandomNumberIn100 = rand(100);

Hint: Store the rand object of type function<int(int> and pass it by reference. If you copy it, rand will always produce the same result.

Upvotes: 0

Holstebroe
Holstebroe

Reputation: 5133

You ask how to assign a new attack value inside each iteration in the while loop, well just do it:

while (opponent[ID].health > 0 || player.health > 0)
{
   opponent[ID].attack = (rand()%6)+1;
   player.attack = (rand()%6)+1;
   ...

Upvotes: 1

utnapistim
utnapistim

Reputation: 27365

Besides setting the attack when you initialize the player instance, you will need to reset it in the while loop.

Basically add this:

while (opponent[ID].health > 0 || player.health > 0)
{
    player.attack = (rand()%6)+1;  // <<< add this

    cout << endl << endl << "Round " << number << endl;

    // rest of code is unchanged
}

Upvotes: 0

Related Questions