Core/Random: Add random distribution that minimizes long good and bad luck streaks

This commit is contained in:
luis
2026-09-21 12:03:48 -03:00
parent 4f942bbfe2
commit 1a4902da04
2 changed files with 66 additions and 1 deletions
+53
View File
@@ -18,6 +18,7 @@
#include "Random.h" #include "Random.h"
#include "Errors.h" #include "Errors.h"
#include "SFMTRand.h" #include "SFMTRand.h"
#include <boost/math/tools/roots.hpp>
#include <memory> #include <memory>
#include <random> #include <random>
@@ -89,3 +90,55 @@ uint32 urandweighted(size_t count, double const* chances)
std::discrete_distribution<uint32> dd(chances, chances + count); std::discrete_distribution<uint32> dd(chances, chances + count);
return dd(engine); return dd(engine);
} }
namespace
{
struct PseudoRandomDistributionChanceTable : std::array<float, 10000>
{
PseudoRandomDistributionChanceTable()
{
(*this)[0] = 0.0f;
for (std::size_t i = 1; i < size(); ++i)
(*this)[i] = NormalizePseudoRandomDistributionChance(i * 0.0001f);
}
static float NormalizePseudoRandomDistributionChance(float chance)
{
std::uintptr_t iterationLimit = 10;
return boost::math::tools::newton_raphson_iterate([target = 1.0f / chance](float p) -> std::pair<float, float>
{
// (value, derivative) pairs
std::pair<float, float> chain = { 1.0f, 0.0f };
std::pair<float, float> result = { 1.0f - target, 0.0f };
uint32 cap = uint32(std::ceil(1.0f / p));
for (uint32 i = 1; i < cap; ++i)
{
float chanceToFail = 1 - p * i;
chain.second = chain.second * chanceToFail - chain.first * i;
chain.first = chain.first * chanceToFail;
result.first += chain.first;
result.second += chain.second;
}
return result;
}, chance * chance, 0.0f, chance, 6, iterationLimit);
}
} const ChanceCache;
}
bool roll_chance(float chance, PseudoRandomDistributionState& state)
{
std::ptrdiff_t chanceIndex = std::ptrdiff_t(std::round(chance * 100.0f));
if (chanceIndex < 0)
return false;
if (chanceIndex >= std::ssize(ChanceCache) || state.AccumulateChance(ChanceCache[chanceIndex]) > rand_norm())
{
state.Reset();
return true;
}
return false;
}
+13 -1
View File
@@ -84,4 +84,16 @@ public:
result_type operator()() const { return rand32(); } result_type operator()() const { return rand32(); }
}; };
#endif // Random_h__ struct PseudoRandomDistributionState
{
float AccumulateChance(float progress) { return _progress += progress; }
void Reset() { _progress = 0; }
private:
float _progress = 0.0f;
};
/* Return true if a random roll fits in the specified chance (range 0-100) using pseudo random distribution that minimizes long good/bad luck streaks. */
TC_COMMON_API bool roll_chance(float chance, PseudoRandomDistributionState& state);
#endif // TRINITYCORE_RANDOM_H