Code:
#include <iostream>
#include<fstream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include<chrono>
#include<random>
#include<iomanip>
#include<exception>
#define _VCP_BEGIN namespace vcp{ namespace{
#define _VCP_END }}
_VCP_BEGIN
using namespace std::chrono_literals;
//WOW system constants:
constexpr auto movement_speed(7.05); //players max movement speed : Unit(yards/sec)
constexpr auto life_time(30s); //spheres existed duration : Unit(sec)
constexpr double expired_radius(12);
constexpr double sphere_radius(8);
constexpr double touch_radius(3); //I find this radius affects GotS effective greatly.
//Here are tested constants:
constexpr std::size_t test_times(200); //tested for 200 times
constexpr std::size_t mans(20); //suppose a 20-man raid
constexpr double player_range_x(30),player_range_y(30); //Suppose player init positions ranges
constexpr double range_x(100),range_y(100); //Suppose map ranges
constexpr auto combat_duration(400s); //Suppose combat duration is 400 sec
constexpr std::size_t sphere_proc_rate(4); //max spheres proc per second
static_assert(combat_duration!=0s,"combat_duration should not be 0s.");
static_assert(mans,"mans should not be 0.");
static_assert(player_range_x<=range_x,"player_range_x should <= range_x");
static_assert(player_range_y<=range_y,"player_range_y should <= range_y");
std::random_device rd;
std::mt19937_64 eng(rd());
using point = boost::geometry::model::d2::point_xy<double>;
using segment = boost::geometry::model::segment<point>;
using boost::geometry::distance;
std::uniform_int_distribution<std::size_t> ds_sp_pr(0,sphere_proc_rate);
std::uniform_real_distribution<double> ds_sp(-sphere_radius,sphere_radius),
ds_range_x(-player_range_x,player_range_x),
ds_range_y(-player_range_y,player_range_y),
ds_mv(-movement_speed,movement_speed);
class healing_sphere
{
point sphere_location;
std::chrono::seconds lftm = 0s;
public:
healing_sphere(const point &p=point()) : sphere_location(p){}
const point & location() const
{
return sphere_location;
}
point& location()
{
return sphere_location;
}
bool expired()
{
return life_time<=++lftm;
}
template<typename Iter>
bool expired_ef(Iter beg,Iter end)
{
for(;beg!=end;++beg)
if(distance(beg->location(),sphere_location)<=expired_radius)
return true;
return false;
}
};
class player
{
point player_location;
public:
const point & location() const
{
return player_location;
}
point& location()
{
return player_location;
}
player():player_location(ds_range_x(eng),ds_range_y(eng)){}
std::size_t wander(std::vector<healing_sphere> &healing_sphere_vec)
{
auto dx(ds_mv(eng)),dy(sqrt(movement_speed*movement_speed-dx * dx));
std::uniform_real_distribution<double> ud(-dy,dy);
point wandered_position(player_location.x()+ dx,player_location.y() + ud(eng));
if(range_x<wandered_position.x())
wandered_position.x(range_x);
if(range_y<wandered_position.y())
wandered_position.y(range_y);
segment seg(player_location,wandered_position);
player_location = wandered_position;
std::vector<healing_sphere> nv;
nv.reserve(healing_sphere_vec.size());
std::size_t pick(0);
for(const auto &ele : healing_sphere_vec)
if(distance(seg,ele.location())<=touch_radius)
++pick;
else
nv.push_back(ele);
healing_sphere_vec = std::move(nv);
return pick;
}
healing_sphere summon_sphere()
{
auto dx(ds_sp(eng)),dy(sqrt(sphere_radius*sphere_radius-dx * dx));
std::uniform_real_distribution<double> ud(-dy,dy);
point p(player_location.x() + dx,player_location.y() + ud(eng));
if(range_x<fabs(p.x()))
p.x(range_x);
if(range_y<fabs(p.y()))
p.y(range_y);
return p;
}
};
void process()
{
std::uniform_int_distribution<std::size_t> ud(0,mans-1);
std::size_t expired_orb(0),pickup(0),wasted(0),total(0);
for (std::size_t t(0);t!=test_times;++t)
{
std::vector<player> vplayers(mans);
std::vector<healing_sphere> vhsp;
for(std::chrono::seconds i(0);i!=combat_duration;++i)
{
for(auto &ele : vplayers)
pickup+=ele.wander(vhsp);
std::size_t tt(ds_sp_pr(eng));
total += tt;
for(std::size_t i(0);i!=tt;++i)
vhsp.push_back(vplayers[ud(eng)].summon_sphere());
std::vector<healing_sphere> v;
v.reserve(vhsp.size());
for(auto &ele : vhsp)
if(ele.expired())
{
if(ele.expired_ef(vplayers.cbegin(),vplayers.cend()))
++expired_orb;
else
++wasted;
}
else
v.push_back(ele);
vhsp = std::move(v);
}
}
auto effective(pickup+expired_orb);
std::ofstream fout("result.txt");
auto &rdbuf(*fout.rdbuf());
fout<<mans;
rdbuf.sputn("-man Raid\nPick Up: ",19);
fout<<pickup;
rdbuf.sputn("\nExpired: ",10);
fout<<expired_orb;
rdbuf.sputn("\nWasted: ",9);
fout<<wasted;
rdbuf.sputn("\nEffective: ",12);
fout<<effective;
rdbuf.sputn("\nExisted: ",10);
fout<<total-effective-wasted;
rdbuf.sputn("\nTotal: ",8);
fout<<total;
rdbuf.sputn("\nEffective Rate: ",17);
fout<<std::fixed<<std::setprecision(2);
fout<<static_cast<double>(expired_orb+pickup)/total * 100;
rdbuf.sputn("%\n",2);
}
_VCP_END
int main()
{
try
{
vcp::process();
}
catch(std::exception &ex)
{
std::cerr<<ex.what()<<std::endl;
}
catch(...)
{
std::cerr<<"Unknown Error"<<std::endl;
}
return 0;
}
- - - Updated - - -
These following testcases(1-4) suppose everyone moves at every second.
Testcase 1 (for 2v2 Arena):
2-man Raid
Pick Up: 89659
Expired: 6419
Wasted: 57371
Effective: 96078
Existed: 6151
Total: 159600
Effective Rate: 60.20%
Testcase 2 (for mythic raid):
20-man Raid
Pick Up: 112708
Expired: 9799
Wasted: 32570
Effective: 122507
Existed: 5331
Total: 160408
Effective Rate: 76.37%
Testcase 3 (for 30-man heroic Raid):
30-man Raid
Pick Up: 120314
Expired: 9975
Wasted: 24409
Effective: 130289
Existed: 4701
Total: 159399
Effective Rate: 81.74%
Testcase 4 (for World PVP):
40-man Raid
Pick Up: 126353
Expired: 9366
Wasted: 19159
Effective: 135719
Existed: 4347
Total: 159225
Effective Rate: 85.24%
In most cases, we won't move at every second. Our "Real" movement speed might be only 1/3 of the movement speed.
We change line 16:
Code:
constexpr auto movement_speed(7.05); //players max movement speed : Unit(yards/sec)
To
Code:
constexpr auto movement_speed(7.05/3); //players max movement speed : Unit(yards/sec)
Then test again. Now we get Testcase 5-8:
Testcase 5 (for 2v2 Arena):
2-man Raid
Pick Up: 77028
Expired: 41052
Wasted: 34690
Effective: 118080
Existed: 7246
Total: 160016
Effective Rate: 73.79%
Testcase 6 (for mythic raid):
20-man Raid
Pick Up: 99629
Expired: 39626
Wasted: 13442
Effective: 139255
Existed: 6413
Total: 159110
Effective Rate: 87.52%
Testcase 7 (for 30-man heroic Raid):
30-man Raid
Pick Up: 108900
Expired: 35221
Wasted: 9394
Effective: 144121
Existed: 6129
Total: 159644
Effective Rate: 90.28%
Testcase 8 (for World PVP):
40-man Raid
Pick Up: 116038
Expired: 31252
Wasted: 6793
Effective: 147290
Existed: 5696
Total: 159779
Effective Rate: 92.18%
- - - Updated - - -
What if no expired healing spheres?
Code:
#include <iostream>
#include<fstream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include<chrono>
#include<random>
#include<iomanip>
#include<exception>
#define _VCP_BEGIN namespace vcp{ namespace{
#define _VCP_END }}
_VCP_BEGIN
using namespace std::chrono_literals;
//WOW system constants:
constexpr auto movement_speed(7.05); //players max movement speed : Unit(yards/sec)
constexpr auto life_time(30s); //spheres existed duration : Unit(sec)
constexpr double expired_radius(12);
constexpr double sphere_radius(8);
constexpr double touch_radius(3); //I find this radius affects GotS effective greatly.
//Here are tested constants:
constexpr std::size_t test_times(200); //tested for 200 times
constexpr std::size_t mans(20); //suppose a 20-man raid
constexpr double player_range_x(30),player_range_y(30); //Suppose player init positions ranges
constexpr double range_x(100),range_y(100); //Suppose map ranges
constexpr auto combat_duration(400s); //Suppose combat duration is 400 sec
constexpr std::size_t sphere_proc_rate(4); //max spheres proc per second
static_assert(combat_duration!=0s,"combat_duration should not be 0s.");
static_assert(mans,"mans should not be 0.");
static_assert(player_range_x<=range_x,"player_range_x should <= range_x");
static_assert(player_range_y<=range_y,"player_range_y should <= range_y");
std::random_device rd;
std::mt19937_64 eng(rd());
using point = boost::geometry::model::d2::point_xy<double>;
using segment = boost::geometry::model::segment<point>;
using boost::geometry::distance;
std::uniform_int_distribution<std::size_t> ds_sp_pr(0,sphere_proc_rate);
std::uniform_real_distribution<double> ds_sp(-sphere_radius,sphere_radius),
ds_range_x(-player_range_x,player_range_x),
ds_range_y(-player_range_y,player_range_y),
ds_mv(-movement_speed,movement_speed);
class healing_sphere
{
point sphere_location;
std::chrono::seconds lftm = 0s;
public:
healing_sphere(const point &p=point()) : sphere_location(p){}
const point & location() const
{
return sphere_location;
}
point& location()
{
return sphere_location;
}
bool expired()
{
return life_time<=++lftm;
}
template<typename Iter>
bool expired_ef(Iter beg,Iter end)
{
/* for(;beg!=end;++beg)
if(distance(beg->location(),sphere_location)<=expired_radius)
return true;*/
return false;
}
};
class player
{
point player_location;
public:
const point & location() const
{
return player_location;
}
point& location()
{
return player_location;
}
player():player_location(ds_range_x(eng),ds_range_y(eng)){}
std::size_t wander(std::vector<healing_sphere> &healing_sphere_vec)
{
auto dx(ds_mv(eng)),dy(sqrt(movement_speed*movement_speed-dx * dx));
std::uniform_real_distribution<double> ud(-dy,dy);
point wandered_position(player_location.x()+ dx,player_location.y() + ud(eng));
if(range_x<fabs(wandered_position.x()))
wandered_position.x(range_x);
if(range_y<fabs(wandered_position.y()))
wandered_position.y(range_y);
segment seg(player_location,wandered_position);
player_location = wandered_position;
std::vector<healing_sphere> nv;
nv.reserve(healing_sphere_vec.size());
std::size_t pick(0);
for(const auto &ele : healing_sphere_vec)
if(distance(seg,ele.location())<=touch_radius)
++pick;
else
nv.push_back(ele);
healing_sphere_vec = std::move(nv);
return pick;
}
healing_sphere summon_sphere()
{
auto dx(ds_sp(eng)),dy(sqrt(sphere_radius*sphere_radius-dx * dx));
std::uniform_real_distribution<double> ud(-dy,dy);
point p(player_location.x() + dx,player_location.y() + ud(eng));
if(range_x<fabs(p.x()))
p.x(range_x);
if(range_y<fabs(p.y()))
p.y(range_y);
return p;
}
};
void process()
{
std::uniform_int_distribution<std::size_t> ud(0,mans-1);
std::size_t expired_orb(0),pickup(0),wasted(0),total(0);
for (std::size_t t(0);t!=test_times;++t)
{
std::vector<player> vplayers(mans);
std::vector<healing_sphere> vhsp;
for(std::chrono::seconds i(0);i!=combat_duration;++i)
{
for(auto &ele : vplayers)
pickup+=ele.wander(vhsp);
std::size_t tt(ds_sp_pr(eng));
total += tt;
for(std::size_t i(0);i!=tt;++i)
vhsp.push_back(vplayers[ud(eng)].summon_sphere());
std::vector<healing_sphere> v;
v.reserve(vhsp.size());
for(auto &ele : vhsp)
if(ele.expired())
{
if(ele.expired_ef(vplayers.cbegin(),vplayers.cend()))
++expired_orb;
else
++wasted;
}
else
v.push_back(ele);
vhsp = std::move(v);
}
}
auto effective(pickup+expired_orb);
std::ofstream fout("result.txt");
auto &rdbuf(*fout.rdbuf());
fout<<mans;
rdbuf.sputn("-man Raid\nPick Up: ",19);
fout<<pickup;
rdbuf.sputn("\nExpired: ",10);
fout<<expired_orb;
rdbuf.sputn("\nWasted: ",9);
fout<<wasted;
rdbuf.sputn("\nEffective: ",12);
fout<<effective;
rdbuf.sputn("\nExisted: ",10);
fout<<total-effective-wasted;
rdbuf.sputn("\nTotal: ",8);
fout<<total;
rdbuf.sputn("\nEffective Rate: ",17);
fout<<std::fixed<<std::setprecision(2);
fout<<static_cast<double>(expired_orb+pickup)/total * 100;
rdbuf.sputn("%\n",2);
}
_VCP_END
int main()
{
try
{
vcp::process();
}
catch(std::exception &ex)
{
std::cerr<<ex.what()<<std::endl;
}
catch(...)
{
std::cerr<<"Unknown Error"<<std::endl;
}
return 0;
}
20-man Raid
Pick Up: 112403
Expired: 0
Wasted: 41995
Effective: 112403
Existed: 5211
Total: 159609
Effective Rate: 70.42%
Code:
#include <iostream>
#include<fstream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include<chrono>
#include<random>
#include<iomanip>
#include<exception>
#define _VCP_BEGIN namespace vcp{ namespace{
#define _VCP_END }}
_VCP_BEGIN
using namespace std::chrono_literals;
//WOW system constants:
constexpr auto movement_speed(7.05/3); //players max movement speed : Unit(yards/sec)
constexpr auto life_time(30s); //spheres existed duration : Unit(sec)
constexpr double expired_radius(12);
constexpr double sphere_radius(8);
constexpr double touch_radius(3); //I find this radius affects GotS effective greatly.
//Here are tested constants:
constexpr std::size_t test_times(200); //tested for 200 times
constexpr std::size_t mans(20); //suppose a 20-man raid
constexpr double player_range_x(30),player_range_y(30); //Suppose player init positions ranges
constexpr double range_x(100),range_y(100); //Suppose map ranges
constexpr auto combat_duration(400s); //Suppose combat duration is 400 sec
constexpr std::size_t sphere_proc_rate(4); //max spheres proc per second
static_assert(combat_duration!=0s,"combat_duration should not be 0s.");
static_assert(mans,"mans should not be 0.");
static_assert(player_range_x<=range_x,"player_range_x should <= range_x");
static_assert(player_range_y<=range_y,"player_range_y should <= range_y");
std::random_device rd;
std::mt19937_64 eng(rd());
using point = boost::geometry::model::d2::point_xy<double>;
using segment = boost::geometry::model::segment<point>;
using boost::geometry::distance;
std::uniform_int_distribution<std::size_t> ds_sp_pr(0,sphere_proc_rate);
std::uniform_real_distribution<double> ds_sp(-sphere_radius,sphere_radius),
ds_range_x(-player_range_x,player_range_x),
ds_range_y(-player_range_y,player_range_y),
ds_mv(-movement_speed,movement_speed);
class healing_sphere
{
point sphere_location;
std::chrono::seconds lftm = 0s;
public:
healing_sphere(const point &p=point()) : sphere_location(p){}
const point & location() const
{
return sphere_location;
}
point& location()
{
return sphere_location;
}
bool expired()
{
return life_time<=++lftm;
}
template<typename Iter>
bool expired_ef(Iter beg,Iter end)
{
/* for(;beg!=end;++beg)
if(distance(beg->location(),sphere_location)<=expired_radius)
return true;*/
return false;
}
};
class player
{
point player_location;
public:
const point & location() const
{
return player_location;
}
point& location()
{
return player_location;
}
player():player_location(ds_range_x(eng),ds_range_y(eng)){}
std::size_t wander(std::vector<healing_sphere> &healing_sphere_vec)
{
auto dx(ds_mv(eng)),dy(sqrt(movement_speed*movement_speed-dx * dx));
std::uniform_real_distribution<double> ud(-dy,dy);
point wandered_position(player_location.x()+ dx,player_location.y() + ud(eng));
if(range_x<fabs(wandered_position.x()))
wandered_position.x(range_x);
if(range_y<fabs(wandered_position.y()))
wandered_position.y(range_y);
segment seg(player_location,wandered_position);
player_location = wandered_position;
std::vector<healing_sphere> nv;
nv.reserve(healing_sphere_vec.size());
std::size_t pick(0);
for(const auto &ele : healing_sphere_vec)
if(distance(seg,ele.location())<=touch_radius)
++pick;
else
nv.push_back(ele);
healing_sphere_vec = std::move(nv);
return pick;
}
healing_sphere summon_sphere()
{
auto dx(ds_sp(eng)),dy(sqrt(sphere_radius*sphere_radius-dx * dx));
std::uniform_real_distribution<double> ud(-dy,dy);
point p(player_location.x() + dx,player_location.y() + ud(eng));
if(range_x<fabs(p.x()))
p.x(range_x);
if(range_y<fabs(p.y()))
p.y(range_y);
return p;
}
};
void process()
{
std::uniform_int_distribution<std::size_t> ud(0,mans-1);
std::size_t expired_orb(0),pickup(0),wasted(0),total(0);
for (std::size_t t(0);t!=test_times;++t)
{
std::vector<player> vplayers(mans);
std::vector<healing_sphere> vhsp;
for(std::chrono::seconds i(0);i!=combat_duration;++i)
{
for(auto &ele : vplayers)
pickup+=ele.wander(vhsp);
std::size_t tt(ds_sp_pr(eng));
total += tt;
for(std::size_t i(0);i!=tt;++i)
vhsp.push_back(vplayers[ud(eng)].summon_sphere());
std::vector<healing_sphere> v;
v.reserve(vhsp.size());
for(auto &ele : vhsp)
if(ele.expired())
{
if(ele.expired_ef(vplayers.cbegin(),vplayers.cend()))
++expired_orb;
else
++wasted;
}
else
v.push_back(ele);
vhsp = std::move(v);
}
}
auto effective(pickup+expired_orb);
std::ofstream fout("result.txt");
auto &rdbuf(*fout.rdbuf());
fout<<mans;
rdbuf.sputn("-man Raid\nPick Up: ",19);
fout<<pickup;
rdbuf.sputn("\nExpired: ",10);
fout<<expired_orb;
rdbuf.sputn("\nWasted: ",9);
fout<<wasted;
rdbuf.sputn("\nEffective: ",12);
fout<<effective;
rdbuf.sputn("\nExisted: ",10);
fout<<total-effective-wasted;
rdbuf.sputn("\nTotal: ",8);
fout<<total;
rdbuf.sputn("\nEffective Rate: ",17);
fout<<std::fixed<<std::setprecision(2);
fout<<static_cast<double>(expired_orb+pickup)/total * 100;
rdbuf.sputn("%\n",2);
}
_VCP_END
int main()
{
try
{
vcp::process();
}
catch(std::exception &ex)
{
std::cerr<<ex.what()<<std::endl;
}
catch(...)
{
std::cerr<<"Unknown Error"<<std::endl;
}
return 0;
}
20-man Raid
Pick Up: 100468
Expired: 0
Wasted: 53837
Effective: 100468
Existed: 6566
Total: 160871
Effective Rate: 62.45%