Reputation: 13
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
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:
std::random_device
is advised)std::default_random_engine
) and initialize it with the seedstd::uniform_distribution_int<int>
) and initialize its parameters (1, 6)
for your player, for example.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
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
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
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
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