scummvm/common/random.cpp
TehGelly 0992fa9c3b
COMMON: Change PRNG Function to Xorshift* (#3341)
The old RNG method had non-standard periods, ranging from some seeds looping on themselves (seed = 1184201285) to some seeds having periods as low as 11 or 48, as listed in https://github.com/scummvm/scummvm/pull/3340. This is a problem even for games that run the RNG once a frame, as the possibilities for random events is greatly reduced should the initial seed be in one of these sets of small periods.

Xorshift* is a standard, fast, non-cryptographic PRNG with academic backing that has period 2^32-1 (all seeds lead to another seed except 0, which is excluded from the initial seeds).  Many different flavors are possible, as listed in the paper, but the choice implemented in this pull request uses only a single 32-bit integer as a state, like the old PRNG.

Co-authored-by: Thierry Crozat <criezy@scummvm.org>
Co-authored-by: Filippos Karapetis <bluegr@gmail.com>
2021-09-09 21:46:08 +03:00

85 lines
2.4 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include <limits.h>
#include "common/random.h"
#include "common/system.h"
#include "gui/EventRecorder.h"
namespace Common {
RandomSource::RandomSource(const String &name) {
// Use system time as RNG seed. Normally not a good idea, if you are using
// a RNG for security purposes, but good enough for our purposes.
assert(g_system);
#ifdef ENABLE_EVENTRECORDER
setSeed(g_eventRec.getRandomSeed(name));
#else
TimeDate time;
g_system->getTimeAndDate(time);
uint32 newSeed = time.tm_sec + time.tm_min * 60 + time.tm_hour * 3600;
newSeed += time.tm_mday * 86400 + time.tm_mon * 86400 * 31;
newSeed += time.tm_year * 86400 * 366;
newSeed = newSeed * 1000 + g_system->getMillis();
setSeed(newSeed);
#endif
}
void RandomSource::setSeed(uint32 seed) {
if (seed == 0)
seed++;
_randSeed = seed;
}
uint RandomSource::getRandomNumber(uint max) {
scrambleSeed();
if (max == UINT_MAX)
return (_randSeed * 0xDEADBF03);
return (_randSeed * 0xDEADBF03) % (max + 1);
}
uint RandomSource::getRandomBit() {
scrambleSeed();
return _randSeed & 1;
}
uint RandomSource::getRandomNumberRng(uint min, uint max) {
return getRandomNumber(max - min) + min;
}
int RandomSource::getRandomNumberRngSigned(int min, int max) {
return getRandomNumber(max - min) + min;
}
inline void RandomSource::scrambleSeed() {
//marsaglia's paper says that any of 81 triplets are feasible
//(11,21,13) was chosen, with (cba) and (>>,<<,>>)
_randSeed ^= _randSeed >> 13;
_randSeed ^= _randSeed << 21;
_randSeed ^= _randSeed >> 11;
}
} // End of namespace Common