#include <cstdlib>
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
static const float CONTROL_WR = 0.8f;
static const float AGGRO_WR = 0.65f;
static const unsigned STARS = 5 * 5 + 5 * 4 + 5 * 3; //From rank 20: 5 ranks with 3 stars, 5 ranks with 4, 5 with 5
static const unsigned REPEATS = 10000; //Higher is slower and more accurate estimate of expected number of games needed
static const unsigned STREAK_WINS = 3; //Number of consecutive wins that start awarding bonus stars
static const unsigned SWITCH_AFTER = STREAK_WINS - 1; //How many wins with the first deck before switching to the second
//Flip a coin that lands on "heads" with probability p
bool trial(float p)
{
float r = (float)((float)rand() / (float)RAND_MAX);
return r < p;
}
class RunningStat //See: http://w...content-available-to-author-only...k.com/blog/standard_deviation/
{
public:
RunningStat() : m_n(0) {}
void Clear()
{
m_n = 0;
}
void Push(float x)
{
m_n++;
// See Knuth TAOCP vol 2, 3rd edition, page 232
if (m_n == 1)
{
m_oldM = m_newM = x;
m_oldS = 0.0;
}
else
{
m_newM = m_oldM + (x - m_oldM)/m_n;
m_newS = m_oldS + (x - m_oldM)*(x - m_newM);
// set up for next iteration
m_oldM = m_newM;
m_oldS = m_newS;
}
}
int NumDataValues() const
{
return m_n;
}
float Mean() const
{
return (m_n > 0) ? m_newM : 0.0;
}
float Variance() const
{
return ( (m_n > 1) ? m_newS/(m_n - 1) : 0.0 );
}
float StandardDeviation() const
{
return sqrt( Variance() );
}
private:
int m_n;
float m_oldM, m_newM, m_oldS, m_newS;
};
bool floatCompare(float a, float b)
{
return max(a, b) - min(a, b) <= 0.01;
}
int main(int argc, char* argv[])
{
//Initialize a random seed.
//Can consider using something better than the standard built-in rand(). Google: Mersenne Twister
srand(time(NULL));
RunningStat game_stat;
RunningStat base_stat;
float base_wr = AGGRO_WR;
float streak_wr = CONTROL_WR;
for (unsigned iteration = 0; iteration < REPEATS; ++iteration)
{
unsigned cur_stars = 0; //This is unsigned and zero-based because we're simulating from rank 20. If you want to start at 10 or something, change to signed and take into account negatives
unsigned streak = 0; //Streak length
unsigned games = 0; //How many games played
unsigned base_games = 0; //How many games played with the initial deck
float wr = base_wr; //starting deck's win-rate
while (cur_stars < STARS)
{
if (streak >= SWITCH_AFTER)
{
//Yes, we'll be "switching" from a deck to itself a lot, it's not inefficient though
//I pulled this outside the "win" loop for the corner case where you only employ one deck
wr = streak_wr;
}
++games;
if (floatCompare(wr, base_wr))
{
++base_games;
}
if (trial(wr)) //if we won the game
{
++cur_stars;
++streak;
}
else
{
if (cur_stars > 0)
{
--cur_stars;
}
streak = 0;
wr = base_wr;
}
if (streak >= STREAK_WINS)
{
++cur_stars;
}
}
game_stat.Push((float)games);
base_stat.Push((float)base_games);
}
cout << "Based on " << REPEATS
<< " simulations, each playing a deck with a win-rate of " << base_wr
<< " until you reach " << SWITCH_AFTER
<< " then switching to a deck with " << streak_wr
<< " win-rate, it took an average of " << game_stat.Mean()
<< " games with a standard deviation of " << game_stat.StandardDeviation()
<< " of those " << base_stat.Mean()
<< " games were with the \"priming\" deck."
<< endl;
//std::cout << std::endl << "Hurray! It took " << games << " games at a win-rate of " << wr << " to reach Rank 5" << std::endl;
return 0;
}