Core/Utils: Moved rng functions to separate header and added utility functions to select a random element from a container where each element can have different chance of being selected

This commit is contained in:
Shauren
2015-12-24 19:48:39 +01:00
parent 2796de1a77
commit ae20b2ab56
5 changed files with 200 additions and 95 deletions

View File

@@ -19,10 +19,10 @@
#define TRINITY_CONTAINERS_H
#include "Define.h"
#include "Random.h"
#include <functional>
#include <list>
//! Because circular includes are bad
extern uint32 urand(uint32 min, uint32 max);
#include <vector>
namespace Trinity
{
@@ -57,14 +57,53 @@ namespace Trinity
list = listCopy;
}
/* Select a random element from a container. Note: make sure you explicitly empty check the container */
template <class C> typename C::value_type const& SelectRandomContainerElement(C const& container)
/*
* Select a random element from a container.
*
* Note: container cannot be empty
*/
template <class C>
typename C::value_type const& SelectRandomContainerElement(C const& container)
{
typename C::const_iterator it = container.begin();
std::advance(it, urand(0, uint32(container.size()) - 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 weightExtractor Function retrieving chance of each element in container
*
* Note: container cannot be empty
*/
template <class C>
typename C::const_iterator SelectRandomWeightedContainerElement(C const& container, std::function<double(typename C::value_type const&)> weightExtractor)
{
std::vector<double> weights;
weights.reserve(container.size());
std::transform(container.begin(), container.end(), std::back_inserter(weights), weightExtractor);
return SelectRandomWeightedContainerElement(container, weights);
}
/*
* 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
*
* Note: container cannot be empty
*/
template <class C>
typename C::const_iterator SelectRandomWeightedContainerElement(C const& container, std::vector<double> weights)
{
std::discrete_distribution<uint32> dd(weights.begin(), weights.end());
typename C::const_iterator it = container.begin();
std::advance(it, dd(SFMTEngine::Instance()));
return it;
}
/**
* @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2)
*

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Random.h"
#include "Common.h"
#include "Errors.h"
#include "SFMT.h"
#include <boost/thread/tss.hpp>
static boost::thread_specific_ptr<SFMTRand> sfmtRand;
static SFMTRand* GetRng()
{
SFMTRand* rand = sfmtRand.get();
if (!rand)
{
rand = new SFMTRand();
sfmtRand.reset(rand);
}
return rand;
}
int32 irand(int32 min, int32 max)
{
ASSERT(max >= min);
return int32(GetRng()->IRandom(min, max));
}
uint32 urand(uint32 min, uint32 max)
{
ASSERT(max >= min);
return GetRng()->URandom(min, max);
}
uint32 urandms(uint32 min, uint32 max)
{
ASSERT(max >= min);
ASSERT(INT_MAX / IN_MILLISECONDS >= max);
return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS);
}
float frand(float min, float max)
{
ASSERT(max >= min);
return float(GetRng()->Random() * (max - min) + min);
}
uint32 rand32()
{
return GetRng()->BRandom();
}
double rand_norm()
{
return GetRng()->Random();
}
double rand_chance()
{
return GetRng()->Random() * 100.0;
}
SFMTEngine& SFMTEngine::Instance()
{
static SFMTEngine engine;
return engine;
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Random_h__
#define Random_h__
#include "Define.h"
#include <random>
/* 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 millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
uint32 urandms(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();
/* 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);
}
/*
* SFMT wrapper satisfying UniformRandomNumberGenerator concept for use in <random> algorithms
*/
class SFMTEngine
{
public:
typedef uint32 result_type;
result_type min() const { return std::numeric_limits<result_type>::min(); }
result_type max() const { return std::numeric_limits<result_type>::max(); }
result_type operator()() const { return rand32(); }
static SFMTEngine& Instance();
};
#endif // Random_h__

View File

@@ -20,10 +20,8 @@
#include "Common.h"
#include "CompilerDefs.h"
#include "utf8.h"
#include "SFMT.h"
#include "Errors.h" // for ASSERT
#include <stdarg.h>
#include <boost/thread/tss.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#if COMPILER == COMPILER_GNU
@@ -32,61 +30,6 @@
#include <arpa/inet.h>
#endif
static boost::thread_specific_ptr<SFMTRand> sfmtRand;
static SFMTRand* GetRng()
{
SFMTRand* rand = sfmtRand.get();
if (!rand)
{
rand = new SFMTRand();
sfmtRand.reset(rand);
}
return rand;
}
int32 irand(int32 min, int32 max)
{
ASSERT(max >= min);
return int32(GetRng()->IRandom(min, max));
}
uint32 urand(uint32 min, uint32 max)
{
ASSERT(max >= min);
return GetRng()->URandom(min, max);
}
uint32 urandms(uint32 min, uint32 max)
{
ASSERT(max >= min);
ASSERT(INT_MAX/IN_MILLISECONDS >= max);
return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS);
}
float frand(float min, float max)
{
ASSERT(max >= min);
return float(GetRng()->Random() * (max - min) + min);
}
uint32 rand32()
{
return GetRng()->BRandom();
}
double rand_norm()
{
return GetRng()->Random();
}
double rand_chance()
{
return GetRng()->Random() * 100.0;
}
Tokenizer::Tokenizer(const std::string &src, const char sep, uint32 vectorReserve)
{
m_str = new char[src.length() + 1];

View File

@@ -21,6 +21,7 @@
#include "Define.h"
#include "Errors.h"
#include "Random.h"
#include <algorithm>
#include <string>
@@ -78,39 +79,6 @@ std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hou
uint32 TimeStringToSecs(const std::string& timestring);
std::string TimeToTimestampStr(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 millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
uint32 urandms(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();
/* 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);
}
inline void ApplyPercentModFloatVar(float& var, float val, bool apply)
{
if (val == -100.0f) // prevent set var to zero