feat(Core/Random): port random system from TrinityCore (#5454)

* feat(Core/Random): port random system from TrinityCore

* lic

* logic correct

* MultimapErasePair move

* whitespace

17:13:34 1. 'Containers.h'. Replace (1)
17:13:40 2. 'LootMgr.h'. Replace (1)
17:13:44 3. 'World.cpp'. Replace (1)
17:13:47 4. 'instance_scholomance.cpp'. Replace (1)

* correct debug build
This commit is contained in:
Kargatum
2021-05-17 02:53:21 +07:00
committed by GitHub
parent 0d699222de
commit 13f71c9c4d
33 changed files with 2933 additions and 651 deletions

View File

@@ -0,0 +1,229 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#ifndef ACORE_CONTAINERS_H
#define ACORE_CONTAINERS_H
#include "Define.h"
#include "Random.h"
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace acore
{
template<class T>
constexpr inline T* AddressOrSelf(T* ptr)
{
return ptr;
}
template<class T>
constexpr inline T* AddressOrSelf(T& not_ptr)
{
return std::addressof(not_ptr);
}
template <class T>
class CheckedBufferOutputIterator
{
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using pointer = T*;
using reference = T&;
using difference_type = std::ptrdiff_t;
CheckedBufferOutputIterator(T* buf, size_t n) : _buf(buf), _end(buf + n) {}
T& operator*() const { check(); return *_buf; }
CheckedBufferOutputIterator& operator++() { check(); ++_buf; return *this; }
CheckedBufferOutputIterator operator++(int) { CheckedBufferOutputIterator v = *this; operator++(); return v; }
[[nodiscard]] size_t remaining() const { return (_end - _buf); }
private:
T* _buf;
T* _end;
void check() const
{
if (!(_buf < _end))
throw std::out_of_range("index");
}
};
}
namespace acore::Containers
{
// resizes <container> to have at most <requestedSize> elements
// if it has more than <requestedSize> elements, the elements to keep are selected randomly
template<class C>
void RandomResize(C& container, std::size_t requestedSize)
{
static_assert(std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<typename C::iterator>::iterator_category>::value, "Invalid container passed to acore::Containers::RandomResize");
if (std::size(container) <= requestedSize)
return;
auto keepIt = std::begin(container), curIt = std::begin(container);
uint32 elementsToKeep = requestedSize, elementsToProcess = std::size(container);
while (elementsToProcess)
{
// this element has chance (elementsToKeep / elementsToProcess) of being kept
if (urand(1, elementsToProcess) <= elementsToKeep)
{
if (keepIt != curIt)
*keepIt = std::move(*curIt);
++keepIt;
--elementsToKeep;
}
++curIt;
--elementsToProcess;
}
container.erase(keepIt, std::end(container));
}
template<class C, class Predicate>
void RandomResize(C& container, Predicate&& predicate, std::size_t requestedSize)
{
//! First use predicate filter
C containerCopy;
std::copy_if(std::begin(container), std::end(container), std::inserter(containerCopy, std::end(containerCopy)), predicate);
if (requestedSize)
RandomResize(containerCopy, requestedSize);
container = std::move(containerCopy);
}
/*
* Select a random element from a container.
*
* Note: container cannot be empty
*/
template<class C>
inline auto SelectRandomContainerElement(C const& container) -> typename std::add_const<decltype(*std::begin(container))>::type&
{
auto it = std::begin(container);
std::advance(it, urand(0, uint32(std::size(container)) - 1));
return *it;
}
/*
* Select a random element from a container where each element has a different chance to be selected.
*
* @param container Container to select an element from
* @param weights Chances of each element to be selected, must be in the same order as elements in container.
* Caller is responsible for checking that sum of all weights is greater than 0.
*
* Note: container cannot be empty
*/
template<class C>
inline auto SelectRandomWeightedContainerElement(C const& container, std::vector<double> weights) -> decltype(std::begin(container))
{
auto it = std::begin(container);
std::advance(it, urandweighted(weights.size(), weights.data()));
return it;
}
/*
* Select a random element from a container where each element has a different chance to be selected.
*
* @param container Container to select an element from
* @param weightExtractor Function retrieving chance of each element in container, expected to take an element of the container and returning a double
*
* Note: container cannot be empty
*/
template<class C, class Fn>
auto SelectRandomWeightedContainerElement(C const& container, Fn weightExtractor) -> decltype(std::begin(container))
{
std::vector<double> weights;
weights.reserve(std::size(container));
double weightSum = 0.0;
for (auto& val : container)
{
double weight = weightExtractor(val);
weights.push_back(weight);
weightSum += weight;
}
if (weightSum <= 0.0)
weights.assign(std::size(container), 1.0);
return SelectRandomWeightedContainerElement(container, weights);
}
/*
* @fn void acore::Containers::RandomShuffle(C& container)
*
* @brief Reorder the elements of the container randomly.
*
* @param container Container to reorder
*/
template<class C>
inline void RandomShuffle(C& container)
{
std::shuffle(std::begin(container), std::end(container), RandomEngine::Instance());
}
template<class K, class V, template<class, class, class...> class M, class... Rest>
void MultimapErasePair(M<K, V, Rest...>& multimap, K const& key, V const& value)
{
auto range = multimap.equal_range(key);
for (auto itr = range.first; itr != range.second;)
{
if (itr->second == value)
itr = multimap.erase(itr);
else
++itr;
}
}
template <typename Container, typename Predicate>
std::enable_if_t<std::is_move_assignable_v<decltype(*std::declval<Container>().begin())>, void> EraseIf(Container& c, Predicate p)
{
auto wpos = c.begin();
for (auto rpos = c.begin(), end = c.end(); rpos != end; ++rpos)
{
if (!p(*rpos))
{
if (rpos != wpos)
{
std::swap(*rpos, *wpos);
}
++wpos;
}
}
c.erase(wpos, c.end());
}
template <typename Container, typename Predicate>
std::enable_if_t<!std::is_move_assignable_v<decltype(*std::declval<Container>().begin())>, void> EraseIf(Container& c, Predicate p)
{
for (auto it = c.begin(); it != c.end();)
{
if (p(*it))
{
it = c.erase(it);
}
else
{
++it;
}
}
}
}
#endif //! #ifdef ACORE_CONTAINERS_H

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
*/
#include "Random.h"
#include "Errors.h"
#include "SFMTRand.h"
#include <memory>
#include <random>
static thread_local std::unique_ptr<SFMTRand> sfmtRand;
static RandomEngine engine;
static SFMTRand* GetRng()
{
if (!sfmtRand)
sfmtRand = std::make_unique<SFMTRand>();
return sfmtRand.get();
}
int32 irand(int32 min, int32 max)
{
ASSERT(max >= min);
std::uniform_int_distribution<int32> uid(min, max);
return uid(engine);
}
uint32 urand(uint32 min, uint32 max)
{
ASSERT(max >= min);
std::uniform_int_distribution<uint32> uid(min, max);
return uid(engine);
}
uint32 urandms(uint32 min, uint32 max)
{
ASSERT(std::numeric_limits<uint32>::max() / Milliseconds::period::den >= max);
return urand(min * Milliseconds::period::den, max * Milliseconds::period::den);
}
float frand(float min, float max)
{
ASSERT(max >= min);
std::uniform_real_distribution<float> urd(min, max);
return urd(engine);
}
Milliseconds randtime(Milliseconds min, Milliseconds max)
{
long long diff = max.count() - min.count();
ASSERT(diff >= 0);
ASSERT(diff <= (uint32)-1);
return min + Milliseconds(urand(0, diff));
}
uint32 rand32()
{
return GetRng()->RandomUInt32();
}
double rand_norm()
{
std::uniform_real_distribution<double> urd;
return urd(engine);
}
double rand_chance()
{
std::uniform_real_distribution<double> urd(0.0, 100.0);
return urd(engine);
}
uint32 urandweighted(size_t count, double const* chances)
{
std::discrete_distribution<uint32> dd(chances, chances + count);
return dd(engine);
}
RandomEngine& RandomEngine::Instance()
{
return engine;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
*/
#ifndef Random_h__
#define Random_h__
#include "Define.h"
#include "Duration.h"
#include <limits>
/* Return a random number in the range min..max. */
AC_COMMON_API int32 irand(int32 min, int32 max);
/* Return a random number in the range min..max (inclusive). */
AC_COMMON_API uint32 urand(uint32 min, uint32 max);
/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
AC_COMMON_API uint32 urandms(uint32 min, uint32 max);
/* Return a random number in the range 0 .. UINT32_MAX. */
AC_COMMON_API uint32 rand32();
/* Return a random time in the range min..max (up to millisecond precision). Only works for values where millisecond difference is a valid uint32. */
AC_COMMON_API Milliseconds randtime(Milliseconds min, Milliseconds max);
/* Return a random number in the range min..max */
AC_COMMON_API float frand(float min, float max);
/* Return a random double from 0.0 to 1.0 (exclusive). */
AC_COMMON_API double rand_norm();
/* Return a random double from 0.0 to 100.0 (exclusive). */
AC_COMMON_API double rand_chance();
/* Return a random number in the range 0..count (exclusive) with each value having a different chance of happening */
AC_COMMON_API uint32 urandweighted(size_t count, double const* chances);
/* Return true if a random roll fits in the specified chance (range 0-100). */
inline bool roll_chance_f(float chance)
{
return chance > rand_chance();
}
/* Return true if a random roll fits in the specified chance (range 0-100). */
inline bool roll_chance_i(int chance)
{
return chance > irand(0, 99);
}
/*
* Wrapper satisfying UniformRandomNumberGenerator concept for use in <random> algorithms
*/
class AC_COMMON_API RandomEngine
{
public:
typedef uint32 result_type;
static constexpr result_type min() { return std::numeric_limits<result_type>::min(); }
static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
result_type operator()() const { return rand32(); }
static RandomEngine& Instance();
};
#endif // Random_h__

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
*/
#include "SFMTRand.h"
#include <algorithm>
#include <array>
#include <functional>
#include <random>
#include <ctime>
#if defined(__aarch64__)
#if defined(__clang__)
#include <mm_malloc.h>
#elif defined(__GNUC__)
static __inline__ void *__attribute__((__always_inline__, __nodebug__, __malloc__))
_mm_malloc(size_t __size, size_t __align)
{
if (__align == 1)
{
return malloc(__size);
}
if (!(__align & (__align - 1)) && __align < sizeof(void *))
__align = sizeof(void *);
void *__mallocedMemory;
if (posix_memalign(&__mallocedMemory, __align, __size))
return NULL;
return __mallocedMemory;
}
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_free(void *__p)
{
free(__p);
}
#else
#error aarch64 only on clang and gcc
#endif
#else
#include <emmintrin.h>
#endif
SFMTRand::SFMTRand()
{
std::random_device dev;
if (dev.entropy() > 0)
{
std::array<uint32, SFMT_N32> seed;
std::generate(seed.begin(), seed.end(), std::ref(dev));
sfmt_init_by_array(&_state, seed.data(), seed.size());
}
else
{
sfmt_init_gen_rand(&_state, uint32(time(nullptr)));
}
}
uint32 SFMTRand::RandomUInt32() // Output random bits
{
return sfmt_genrand_uint32(&_state);
}
void* SFMTRand::operator new(size_t size, std::nothrow_t const&)
{
return _mm_malloc(size, 16);
}
void SFMTRand::operator delete(void* ptr, std::nothrow_t const&)
{
_mm_free(ptr);
}
void* SFMTRand::operator new(size_t size)
{
return _mm_malloc(size, 16);
}
void SFMTRand::operator delete(void* ptr)
{
_mm_free(ptr);
}
void* SFMTRand::operator new[](size_t size, std::nothrow_t const&)
{
return _mm_malloc(size, 16);
}
void SFMTRand::operator delete[](void* ptr, std::nothrow_t const&)
{
_mm_free(ptr);
}
void* SFMTRand::operator new[](size_t size)
{
return _mm_malloc(size, 16);
}
void SFMTRand::operator delete[](void* ptr)
{
_mm_free(ptr);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
*/
#ifndef SFMTRand_h__
#define SFMTRand_h__
#include "Define.h"
#include <SFMT.h>
#include <new>
/*
* C++ Wrapper for SFMT
*/
class SFMTRand
{
public:
SFMTRand();
uint32 RandomUInt32(); // Output random bits
void* operator new(size_t size, std::nothrow_t const&);
void operator delete(void* ptr, std::nothrow_t const&);
void* operator new(size_t size);
void operator delete(void* ptr);
void* operator new[](size_t size, std::nothrow_t const&);
void operator delete[](void* ptr, std::nothrow_t const&);
void* operator new[](size_t size);
void operator delete[](void* ptr);
private:
sfmt_t _state;
};
#endif // SFMTRand_h__

View File

@@ -10,62 +10,11 @@
#include "Log.h"
#include "Errors.h"
#include "TypeList.h"
#include <ace/Task.h>
#include "SFMT.h"
#include "Errors.h" // for ASSERT
#include <ace/TSS_T.h>
#include <array>
#include <cwchar>
#include <string>
#include <random>
typedef ACE_TSS<SFMTRand> SFMTRandTSS;
static SFMTRandTSS sfmtRand;
static SFMTEngine engine;
int32 irand(int32 min, int32 max)
{
ASSERT(max >= min);
return int32(sfmtRand->IRandom(min, max));
}
uint32 urand(uint32 min, uint32 max)
{
ASSERT(max >= min);
return sfmtRand->URandom(min, max);
}
float frand(float min, float max)
{
ASSERT(max >= min);
return float(sfmtRand->Random() * (max - min) + min);
}
uint32 rand32()
{
return int32(sfmtRand->BRandom());
}
double rand_norm()
{
return sfmtRand->Random();
}
double rand_chance()
{
return sfmtRand->Random() * 100.0;
}
uint32 urandweighted(size_t count, double const* chances)
{
std::discrete_distribution<uint32> dd(chances, chances + count);
return dd(SFMTEngine::Instance());
}
SFMTEngine& SFMTEngine::Instance()
{
return engine;
}
#include <ace/Default_Constants.h>
Tokenizer::Tokenizer(const std::string& src, const char sep, uint32 vectorReserve)
{

View File

@@ -71,38 +71,6 @@ uint32 TimeStringToSecs(const std::string& timestring);
std::string TimeToTimestampStr(time_t t);
std::string TimeToHumanReadable(time_t t);
/* Return a random number in the range min..max. */
int32 irand(int32 min, int32 max);
/* Return a random number in the range min..max (inclusive). */
uint32 urand(uint32 min, uint32 max);
/* Return a random number in the range 0 .. UINT32_MAX. */
uint32 rand32();
/* Return a random number in the range min..max */
float frand(float min, float max);
/* Return a random double from 0.0 to 1.0 (exclusive). */
double rand_norm();
/* Return a random double from 0.0 to 100.0 (exclusive). */
double rand_chance();
uint32 urandweighted(size_t count, double const* chances);
/* Return true if a random roll fits in the specified chance (range 0-100). */
inline bool roll_chance_f(float chance)
{
return chance > rand_chance();
}
/* Return true if a random roll fits in the specified chance (range 0-100). */
inline bool roll_chance_i(int32 chance)
{
return chance > irand(0, 99);
}
inline void ApplyPercentModFloatVar(float& var, float val, bool apply)
{
if (val == -100.0f) // prevent set var to zero
@@ -582,21 +550,6 @@ bool CompareValues(ComparisionType type, T val1, T val2)
}
}
/*
* SFMT wrapper satisfying UniformRandomNumberGenerator concept for use in <random> algorithms
*/
class SFMTEngine
{
public:
typedef uint32 result_type;
static constexpr result_type min() { return std::numeric_limits<result_type>::min(); }
static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
result_type operator()() const { return rand32(); }
static SFMTEngine& Instance();
};
class EventMap
{
typedef std::multimap<uint32, uint32> EventStore;