mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-29 01:31:30 +00:00
Merge pull request #1460 from EQEmu/feature/random_enhancements
[Feature] Add some knobs to our RNG class
This commit is contained in:
commit
8696ba398b
@ -131,6 +131,47 @@ OPTION(EQEMU_BUILD_TESTS "Build utility tests." OFF)
|
|||||||
OPTION(EQEMU_BUILD_CLIENT_FILES "Build Client Import/Export Data Programs." ON)
|
OPTION(EQEMU_BUILD_CLIENT_FILES "Build Client Import/Export Data Programs." ON)
|
||||||
OPTION(EQEMU_PREFER_LUA "Build with normal Lua even if LuaJIT is found." OFF)
|
OPTION(EQEMU_PREFER_LUA "Build with normal Lua even if LuaJIT is found." OFF)
|
||||||
|
|
||||||
|
#PRNG options
|
||||||
|
OPTION(EQEMU_ADDITIVE_LFIB_PRNG "Use Additive LFib for PRNG." OFF)
|
||||||
|
MARK_AS_ADVANCED(EQEMU_ADDITIVE_LFIB_PRNG)
|
||||||
|
OPTION(EQEMU_BIASED_INT_DIST "Use biased int dist instead of uniform." OFF)
|
||||||
|
MARK_AS_ADVANCED(EQEMU_BIASED_INT_DIST)
|
||||||
|
SET(EQEMU_CUSTOM_PRNG_ENGINE "" CACHE STRING "Custom random engine. (ex. std::default_random_engine)")
|
||||||
|
MARK_AS_ADVANCED(EQEMU_CUSTOM_PRNG_ENGINE)
|
||||||
|
|
||||||
|
IF(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
OPTION(EQEMU_SFMT19937 "Use GCC's extention for SIMD Fast MT19937." OFF)
|
||||||
|
MARK_AS_ADVANCED(EQEMU_SFMT19937)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(EQEMU_ADDITIVE_LFIB_PRNG)
|
||||||
|
ADD_DEFINITIONS(-DUSE_ADDITIVE_LFIB_PRNG)
|
||||||
|
IF(EQEMU_SFMT19937)
|
||||||
|
MESSAGE(STATUS "SFMT19937 and ADDITITVE_LFIB_PRNG both set, SFMT19937 ignored.")
|
||||||
|
SET(EQEMU_SFMT19937 OFF)
|
||||||
|
ENDIF()
|
||||||
|
IF(NOT EQEMU_CUSTOM_PRNG_ENGINE STREQUAL "")
|
||||||
|
MESSAGE(STATUS "CUSTOM_PRNG_ENGINE and ADDITITVE_LFIB_PRNG both set, CUSTOM_PRNG_ENGINE ignored.")
|
||||||
|
SET(EQEMU_CUSTOM_PRNG_ENGINE "")
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(EQEMU_SFMT19937)
|
||||||
|
ADD_DEFINITIONS(-DUSE_SFMT19937)
|
||||||
|
IF(NOT EQEMU_CUSTOM_PRNG_ENGINE STREQUAL "")
|
||||||
|
MESSAGE(STATUS "CUSTOM_PRNG_ENGINE and SFMT19937 both set, CUSTOM_PRNG_ENGINE ignored.")
|
||||||
|
SET(EQEMU_CUSTOM_PRNG_ENGINE "")
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(NOT EQEMU_CUSTOM_PRNG_ENGINE STREQUAL "")
|
||||||
|
ADD_DEFINITIONS(-DUSE_CUSTOM_PRNG_ENGINE=${EQEMU_CUSTOM_PRNG_ENGINE})
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(EQEMU_BIASED_INT_DIST)
|
||||||
|
ADD_DEFINITIONS(-DBIASED_INT_DIST)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
IF(EQEMU_COMMANDS_LOGGING)
|
IF(EQEMU_COMMANDS_LOGGING)
|
||||||
ADD_DEFINITIONS(-DCOMMANDS_LOGGING)
|
ADD_DEFINITIONS(-DCOMMANDS_LOGGING)
|
||||||
ENDIF(EQEMU_COMMANDS_LOGGING)
|
ENDIF(EQEMU_COMMANDS_LOGGING)
|
||||||
|
|||||||
@ -451,6 +451,7 @@ SET(repositories
|
|||||||
)
|
)
|
||||||
|
|
||||||
SET(common_headers
|
SET(common_headers
|
||||||
|
additive_lagged_fibonacci_engine.h
|
||||||
any.h
|
any.h
|
||||||
base_packet.h
|
base_packet.h
|
||||||
base_data.h
|
base_data.h
|
||||||
|
|||||||
147
common/additive_lagged_fibonacci_engine.h
Normal file
147
common/additive_lagged_fibonacci_engine.h
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/* EQEMu: Everquest Server Emulator
|
||||||
|
Copyright (C) 2001-2021 EQEMu Development Team (http://eqemulator.net)
|
||||||
|
|
||||||
|
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; version 2 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||||
|
are required to give you total support for your newly bought product;
|
||||||
|
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, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an additive lagged fibonacci generator as seen in The Art of Computer Programming, Vol. 2
|
||||||
|
* This should roughly match the implementation that EQ's client uses and be compatible with our Random class
|
||||||
|
*
|
||||||
|
* EQ's rand looks like it was from an example implementation that as posted on pscode.com
|
||||||
|
*
|
||||||
|
* You might also want to consider defining BIASED_INT_DIST as well to more closely match EQ
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace EQ {
|
||||||
|
template<typename UIntType, size_t w, size_t j, size_t k>
|
||||||
|
class additive_lagged_fibonacci_engine {
|
||||||
|
static_assert(std::is_unsigned<UIntType>::value, "result_type must be an unsigned integral type");
|
||||||
|
static_assert(0u < j && j < k, "0 < j < k");
|
||||||
|
static_assert(0u < w && w <= std::numeric_limits<UIntType>::digits,
|
||||||
|
"template argument substituting w out of bounds");
|
||||||
|
|
||||||
|
public:
|
||||||
|
using result_type = UIntType;
|
||||||
|
static constexpr size_t word_size = w;
|
||||||
|
static constexpr size_t short_lag = j;
|
||||||
|
static constexpr size_t long_lag = k;
|
||||||
|
static constexpr result_type default_seed = 19780503u; // default for subtract_with_carry_engine
|
||||||
|
|
||||||
|
additive_lagged_fibonacci_engine() : additive_lagged_fibonacci_engine(default_seed) {}
|
||||||
|
|
||||||
|
explicit additive_lagged_fibonacci_engine(result_type sd) { seed(sd); }
|
||||||
|
|
||||||
|
void seed(result_type seed = default_seed)
|
||||||
|
{
|
||||||
|
state1 = long_lag - long_lag;
|
||||||
|
state2 = long_lag - short_lag;
|
||||||
|
state[0] = static_cast<int>(seed) & ((1u << word_size) - 1);
|
||||||
|
state[1] = 1;
|
||||||
|
for (int i = 2; i < long_lag; ++i)
|
||||||
|
state[i] = (state[i - 1] + state[i - 2]) & ((1u << word_size) - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: seed via seed_seq
|
||||||
|
|
||||||
|
static constexpr result_type min() { return 0; }
|
||||||
|
static constexpr result_type max() { return ((1u << word_size) - 1) >> 6; }
|
||||||
|
|
||||||
|
void discard(unsigned long long z) {
|
||||||
|
for (; z != 0ULL; --z)
|
||||||
|
(*this)();
|
||||||
|
}
|
||||||
|
|
||||||
|
result_type operator()() {
|
||||||
|
result_type rand = (state[state1] + state[state2]) & ((1u << word_size) - 1);
|
||||||
|
state[state1] = rand;
|
||||||
|
if (++state1 == long_lag)
|
||||||
|
state1 = 0;
|
||||||
|
if (++state2 == long_lag)
|
||||||
|
state2 = 0;
|
||||||
|
|
||||||
|
return rand >> 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
result_type state1;
|
||||||
|
result_type state2;
|
||||||
|
result_type state[long_lag];
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename UInt, size_t W, size_t J, size_t K>
|
||||||
|
friend bool operator==(const additive_lagged_fibonacci_engine<UInt, W, J, K> &x,
|
||||||
|
const additive_lagged_fibonacci_engine<UInt, W, J, K> &y)
|
||||||
|
{
|
||||||
|
return std::equal(x.state, x.state + long_lag, y.state) && x.state1 == y.state1 &&
|
||||||
|
x.state2 == y.state2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename UInt, size_t W, size_t J, size_t K>
|
||||||
|
friend bool operator!=(const additive_lagged_fibonacci_engine<UInt, W, J, K> &x,
|
||||||
|
const additive_lagged_fibonacci_engine<UInt, W, J, K> &y)
|
||||||
|
{ return !(x == y); }
|
||||||
|
|
||||||
|
template<typename UInt, size_t W, size_t J, size_t K, typename CharT, typename Traits>
|
||||||
|
friend std::basic_ostream<CharT, Traits> &
|
||||||
|
operator<<(std::basic_istream<CharT, Traits> &os, additive_lagged_fibonacci_engine<UInt, W, J, K> &x)
|
||||||
|
{
|
||||||
|
using ios_base = typename std::basic_istream<CharT, Traits>::ios_base;
|
||||||
|
|
||||||
|
const typename ios_base::fmtflags flags = os.flags();
|
||||||
|
const CharT fill = os.fill();
|
||||||
|
const CharT space = os.widen(' ');
|
||||||
|
os.flags(ios_base::dec | ios_base::fixed | ios_base::left);
|
||||||
|
os.fill(space);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < long_lag; ++i)
|
||||||
|
os << x.state[i] << space;
|
||||||
|
os << x.state1 << space << x.state2;
|
||||||
|
|
||||||
|
os.flags(flags);
|
||||||
|
os.fill(fill);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename UInt, size_t W, size_t J, size_t K, typename CharT, typename Traits>
|
||||||
|
friend std::basic_istream<CharT, Traits> &
|
||||||
|
operator>>(std::basic_istream<CharT, Traits> &is, additive_lagged_fibonacci_engine<UInt, W, J, K> &x)
|
||||||
|
{
|
||||||
|
using ios_base = typename std::basic_istream<CharT, Traits>::ios_base;
|
||||||
|
|
||||||
|
const typename ios_base::fmtflags flags = is.flags();
|
||||||
|
is.flags(ios_base::dec | ios_base::skipws);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < long_lag; ++i)
|
||||||
|
is >> x.state[i];
|
||||||
|
is >> x.state1;
|
||||||
|
is >> x.state2;
|
||||||
|
|
||||||
|
is.flags(flags);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using EQRand = additive_lagged_fibonacci_engine<uint32_t, 30, 24, 55>;
|
||||||
|
};
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/* EQEMu: Everquest Server Emulator
|
/* EQEMu: Everquest Server Emulator
|
||||||
Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net)
|
Copyright (C) 2001-2021 EQEMu Development Team (http://eqemulator.net)
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -25,10 +25,24 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
/* This uses mt19937 seeded with the std::random_device
|
#ifdef USE_SFMT19937
|
||||||
* The idea is to have this be included as a member of another class
|
// only GCC supports, so lets turn it off
|
||||||
* so mocking out for testing is easier
|
#ifndef __GNUC__
|
||||||
* If you need to reseed random.Reseed()
|
#undef USE_SFMT19937
|
||||||
|
#endif
|
||||||
|
#ifdef __clang__
|
||||||
|
#undef USE_SFMT19937
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ADDITIVE_LFIB_PRNG
|
||||||
|
#include "additive_lagged_fibonacci_engine.h"
|
||||||
|
#elif defined(USE_SFMT19937)
|
||||||
|
#include <ext/random>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This uses mt19937 (or other compatible engine) seeded with the std::random_device. The idea is to have this be
|
||||||
|
* included as a member of another class so mocking out for testing is easier If you need to reseed random.Reseed()
|
||||||
* Eventually this should be derived from an abstract base class
|
* Eventually this should be derived from an abstract base class
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -40,7 +54,12 @@ namespace EQ {
|
|||||||
{
|
{
|
||||||
if (low > high)
|
if (low > high)
|
||||||
std::swap(low, high);
|
std::swap(low, high);
|
||||||
|
// EQ uses biased int distribution, so I guess we can support it :P
|
||||||
|
#ifdef BIASED_INT_DIST
|
||||||
|
return low + m_gen() % (high - low + 1);
|
||||||
|
#else
|
||||||
return int_dist(m_gen, int_param_t(low, high)); // [low, high]
|
return int_dist(m_gen, int_param_t(low, high)); // [low, high]
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// AKA old MakeRandomFloat
|
// AKA old MakeRandomFloat
|
||||||
@ -98,11 +117,24 @@ namespace EQ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#ifndef BIASED_INT_DIST
|
||||||
typedef std::uniform_int_distribution<int>::param_type int_param_t;
|
typedef std::uniform_int_distribution<int>::param_type int_param_t;
|
||||||
typedef std::uniform_real_distribution<double>::param_type real_param_t;
|
|
||||||
std::mt19937 m_gen;
|
|
||||||
std::uniform_int_distribution<int> int_dist;
|
std::uniform_int_distribution<int> int_dist;
|
||||||
|
#endif
|
||||||
|
typedef std::uniform_real_distribution<double>::param_type real_param_t;
|
||||||
std::uniform_real_distribution<double> real_dist;
|
std::uniform_real_distribution<double> real_dist;
|
||||||
|
// define USE_CUSTOM_PRNG_ENGINE to any supported random engine like:
|
||||||
|
// #define USE_CUSTOM_PRNG_ENGINE std::default_random_engine
|
||||||
|
#ifdef USE_ADDITIVE_LFIB_PRNG
|
||||||
|
EQRand
|
||||||
|
#elif defined(USE_SFMT19937)
|
||||||
|
__gnu_cxx::sfmt19937
|
||||||
|
#elif defined(USE_CUSTOM_PRNG_ENGINE)
|
||||||
|
USE_CUSTOM_PRNG_ENGINE
|
||||||
|
#else
|
||||||
|
std::mt19937
|
||||||
|
#endif
|
||||||
|
m_gen;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user