0

I've been writing this game to just type in the amount of humans and skeletons with a random attack chance. If you enter in the same amount when running the program over and over it generates the same amount. Why isn't the hitChance/random attack amount changing everytime the while loop restarts? When I print the attackChance(randGen) in the while loop it changes. Why isn't the randGen changing the output of the winner of the battle?

   #include <iostream>
#include <string>
#include <random>
#include <ctime>

using namespace std;

int getNumSkeletons();
int getNumHumans();
void finishedBattleStats(int numHumans, int startHumans, int numSkeletons, int startSkeletons);
void simulatedBattle(int &numSkeletons, int &numHumans, int &startHumans, int &startSkeletons);

int main() {

    int startHumans;
    int startSkeletons;

    int numHumans;
    int numSkeletons;


    cout << "------------------------------------\nSkeletons V Humans\n\n";

    //Gets Number of Humans
    numHumans = getNumHumans();
    startHumans = numHumans;
    //Gets Number of Skeletons
    numSkeletons = getNumHumans();
    startSkeletons = numSkeletons;

    //Simulates Battle
    simulatedBattle(numSkeletons, numHumans, startHumans, startSkeletons);

    //End Battle Stats
    finishedBattleStats(numHumans, startHumans, numSkeletons, startSkeletons);
    cin.get();

    return 0;
}

int getNumHumans() {
    int numHumans;

    cout << "Enter number of Humans: \n";
    cin >> numHumans;
    return numHumans;
}

int getNumSkeletons() {
    int numSkeletons;

    cout << "Enter number of Skeletons: \n";
    cin >> numSkeletons;
    return numSkeletons;
}

void simulatedBattle(int &numSkeletons, int &numHumans, int &startHumans, int &startSkeletons) {

    static mt19937 randGen(time(NULL));
    uniform_real_distribution<float> attackChance(0.0f, 1.0f);

    //HumanProperties
    float humanDamage = 30.0f;
    float humanHitChance = 0.6f;
    float humanCritChance = 0.2f;
    float humanHealth = 50.0f;
    float startHumanHealth = humanHealth;
    float currentHuman = startHumanHealth;

    //Skeleton Properties
    float skeletonDamage = 20.0f;
    float skeletonHitChance = 0.7f;
    float skeletonCritChance = 0.2f;
    float skeletonHealth = 40.0f;
    float startSkeletonHealth = skeletonHealth;
    float currentSkeleton = startSkeletonHealth;

    char turn = 'H';

    float hitChance;

    while (numSkeletons > 0 && numHumans > 0) {
        hitChance = attackChance(randGen);
        if (turn == 'H') {
            if (hitChance > humanHitChance) {
                currentSkeleton -= humanDamage;
                turn = 'S';
                if (currentSkeleton < 0) {
                    numHumans--;
                    currentSkeleton = startSkeletonHealth;
                    turn = 'S';
                }
            }
        }
        else {
            if (hitChance > skeletonHitChance) {
                currentHuman -= skeletonDamage;
                turn = 'H';
            }
            if (currentHuman < 0) {
                numSkeletons--;
                currentHuman = startHumanHealth;
                turn = 'H';
            }
        }
    }

}

void finishedBattleStats(int numHumans, int startHumans, int numSkeletons, int startSkeletons) {
        if (numHumans == 0) {
            cout << "Skeletons won! \n\n";
            cout << "Skeletons left: " << numSkeletons << endl;
            cout << "Skeleton Casualties: " << startSkeletons - numSkeletons << endl;
            cout << "All " << startHumans << " humans are dead! \n\n";
            cout << "Game Over!";
            cin.get();
        }
        else {
            cout << "Humans Won! \n\n";
            cout << "Humans left: " << numHumans << endl;
            cout << "Human Casualties: " << startHumans - numHumans << endl;
            cout << "All " << startSkeletons << " skeletons are dead! \n\n";
            cout << "Game Over!";
            cin.get();
        }
    }
Ethan Shapiro
  • 103
  • 1
  • 3
  • 11
  • Can't get `static mt19937 randGen(time(NULL));` to compile. No overload takes time_t. Might I recommend feeding it a [std::random_device](http://en.cppreference.com/w/cpp/numeric/random/random_device) instead? – user4581301 Nov 14 '15 at 01:16
  • What do you mean? does that mean add **rand_device randDevice();** or just random_device by itself. – Ethan Shapiro Nov 14 '15 at 01:21
  • Which compiler toolchain are you using? If GCC, read this: http://stackoverflow.com/questions/18880654/why-do-i-get-same-sequence-for-everyrun-with-stdrandom-device-with-mingw-gcc4 – user4581301 Nov 14 '15 at 01:30
  • You deleted too much in that last edit. – Sebastian Redl Nov 14 '15 at 01:46

2 Answers2

2

IMHO opinion you have a flaw in the battle calculations. That is, when a human's healths goes < 0 you subtract a skeleton and not a human and vice versa. Below is the correction. With a demo. I tried the demo a number of times and gives different results (ENJOY):

void simulatedBattle(int &numSkeletons, int &numHumans, int &startHumans, int &startSkeletons) {
    uniform_real_distribution<float> attackChance(0.0f, 1.0f);
    static mt19937 randGen(time(NULL));

    //HumanProperties
    float humanDamage = 25.0f;
    float humanHitChance = 0.2f;
    float humanCritChance = 0.2f;
    float humanHealth = 50.0f;
    float startHumanHealth = humanHealth;
    float currentHuman = startHumanHealth;

    //Skeleton Properties
    float skeletonDamage = 20.0f;
    float skeletonHitChance = 0.8f;
    float skeletonCritChance = 0.2f;
    float skeletonHealth = 40.0f;
    float startSkeletonHealth = skeletonHealth;
    float currentSkeleton = startSkeletonHealth;

    char turn = 'H';

    float hitChance;

    while (numSkeletons > 0 && numHumans > 0) {
        hitChance = attackChance(randGen);
        if (turn == 'H') {
            if (hitChance > humanHitChance) {
                currentSkeleton -= humanDamage;
                if (currentSkeleton < 0) {
                    --numSkeletons;
                    currentSkeleton = startSkeletonHealth;
                }
                turn = 'S';
            }
        }
        else {
            if (hitChance > skeletonHitChance) currentHuman -= skeletonDamage;
            if (currentHuman < 0) {
                --numHumans;
                currentHuman = startHumanHealth;
            }
            turn = 'H';
        }
    }
}

LIVE DEMO

101010
  • 41,839
  • 11
  • 94
  • 168
  • That was bad of me. I can't believe I didn't notice that. Why did you put **--numHumans** instead of **numHumans--** and does **if (hitChance > skeletonHitChance) currentHuman -= skeletonDamage;** mean the same thing as **if (hitChance > skeletonHitChance) { currentHuman -= skeletonDamage}** – Ethan Shapiro Nov 14 '15 at 02:24
  • Also btw thank you for clearing this up for me, I was getting so frustrated just running the code over and over and trying to change anything. – Ethan Shapiro Nov 14 '15 at 02:25
  • 1
    @EthanShapiro: Most C++ developers use prefix increment/decrement as a matter of habit unless they're relying on postfix behavior specifically; for primitives the difference is immaterial, but for user defined classes, postfix increment/decrement is much more expensive (_postfix_ has to copy the current state of the class, then increment the existing instance, then return the copy; _prefix_ increment just modifies in place and returns a reference to the existing class). Getting in the habit of using prefix inc/dec avoids accidentally wasting work. – ShadowRanger Nov 14 '15 at 02:41
  • 1
    @EthanShapiro: As for omitting braces, that's a style choice (one that some people would disagree with). Conditionals that control the execution of a single statement can omit the braces, but if there is more than one statement, the braces are necessary to make a block that the conditional can control as a single unit. – ShadowRanger Nov 14 '15 at 02:43
  • So basically prefix increment is a better practice when coding because it doesn't have to create a copy of the class. Would it be better to always use braces just in case you want to add more conditions after the program is completed? Sorry if I'm asking a lot of questions, I just want to understand the concept of these "errors" I made so I don't make them later. – Ethan Shapiro Nov 14 '15 at 03:21
0

You have to seed a mersenne twister before use, or you'll always have the same output. It is common practice to seed it with the internal clock:

// obtain a seed from the timer
myclock::duration d = myclock::now() - beginning;
unsigned seed2 = d.count();
generator.seed (seed2);

ref: std::mersenne_twister_engine::seed

blld
  • 1,030
  • 10
  • 19