Browse Source

PCG Random Numbers

This creates an object called simply RandomNumberGenerator that uses PCG random numbers.
Peter Robinson 3 years ago
parent
commit
602694024a

+ 5 - 0
engine/compilers/VisualStudio 2019/Torque 2D.vcxproj

@@ -516,6 +516,7 @@
     <ClCompile Include="..\..\source\2d\scene\WorldQuery.cc" />
     <ClCompile Include="..\..\source\algorithm\crc.cc" />
     <ClCompile Include="..\..\source\algorithm\hashFunction.cc" />
+    <ClCompile Include="..\..\source\algorithm\pcg_basic.c" />
     <ClCompile Include="..\..\source\algorithm\Perlin.cc" />
     <ClCompile Include="..\..\source\assets\assetBase.cc" />
     <ClCompile Include="..\..\source\assets\assetFieldTypes.cc" />
@@ -677,6 +678,7 @@
     <ClCompile Include="..\..\source\math\mFluid.cpp" />
     <ClCompile Include="..\..\source\math\mPoint.cpp" />
     <ClCompile Include="..\..\source\math\noise\NoiseGenerator.cc" />
+    <ClCompile Include="..\..\source\math\noise\RandomNumberGenerator.cc" />
     <ClCompile Include="..\..\source\math\rectClipper.cpp" />
     <ClCompile Include="..\..\source\memory\dataChunker.cc" />
     <ClCompile Include="..\..\source\memory\frameAllocator_ScriptBinding.cc" />
@@ -940,6 +942,7 @@
     <ClInclude Include="..\..\source\algorithm\crctab.h" />
     <ClInclude Include="..\..\source\algorithm\hashFunction.h" />
     <ClInclude Include="..\..\source\algorithm\md5.h" />
+    <ClInclude Include="..\..\source\algorithm\pcg_basic.h" />
     <ClInclude Include="..\..\source\algorithm\Perlin.h" />
     <ClInclude Include="..\..\source\assets\assetBase.h" />
     <ClInclude Include="..\..\source\assets\assetBase_ScriptBinding.h" />
@@ -1182,6 +1185,8 @@
     <ClInclude Include="..\..\source\math\mNormalDistribution.h" />
     <ClInclude Include="..\..\source\math\noise\NoiseGenerator.h" />
     <ClInclude Include="..\..\source\math\noise\NoiseGenerator_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\math\noise\RandomNumberGenerator.h" />
+    <ClInclude Include="..\..\source\math\noise\RandomNumberGenerator_ScriptBinding.h" />
     <ClInclude Include="..\..\source\math\random_ScriptBinding.h" />
     <ClInclude Include="..\..\source\math\rectClipper.h" />
     <ClInclude Include="..\..\source\math\vector_ScriptBinding.h" />

+ 15 - 0
engine/compilers/VisualStudio 2019/Torque 2D.vcxproj.filters

@@ -1336,6 +1336,12 @@
     <ClCompile Include="..\..\source\math\noise\NoiseGenerator.cc">
       <Filter>math\noise</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\source\algorithm\pcg_basic.c">
+      <Filter>algorithm</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\math\noise\RandomNumberGenerator.cc">
+      <Filter>math\noise</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -3058,6 +3064,15 @@
     <ClInclude Include="..\..\source\math\noise\NoiseGenerator_ScriptBinding.h">
       <Filter>math\noise</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\algorithm\pcg_basic.h">
+      <Filter>algorithm</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\math\noise\RandomNumberGenerator.h">
+      <Filter>math\noise</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\math\noise\RandomNumberGenerator_ScriptBinding.h">
+      <Filter>math\noise</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Torque 2D.rc" />

+ 116 - 0
engine/source/algorithm/pcg_basic.c

@@ -0,0 +1,116 @@
+/*
+ * PCG Random Number Generation for C.
+ *
+ * Copyright 2014 Melissa O'Neill <[email protected]>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * For additional information about the PCG random number generation scheme,
+ * including its license and other licensing options, visit
+ *
+ *       http://www.pcg-random.org
+ */
+
+/*
+ * This code is derived from the full C implementation, which is in turn
+ * derived from the canonical C++ PCG implementation. The C++ version
+ * has many additional features and is preferable if you can use C++ in
+ * your project.
+ */
+
+#include "pcg_basic.h"
+
+// state for global RNGs
+
+static pcg32_random_t pcg32_global = PCG32_INITIALIZER;
+
+// pcg32_srandom(initstate, initseq)
+// pcg32_srandom_r(rng, initstate, initseq):
+//     Seed the rng.  Specified in two parts, state initializer and a
+//     sequence selection constant (a.k.a. stream id)
+
+void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
+{
+    rng->state = 0U;
+    rng->inc = (initseq << 1u) | 1u;
+    pcg32_random_r(rng);
+    rng->state += initstate;
+    pcg32_random_r(rng);
+}
+
+void pcg32_srandom(uint64_t seed, uint64_t seq)
+{
+    pcg32_srandom_r(&pcg32_global, seed, seq);
+}
+
+// pcg32_random()
+// pcg32_random_r(rng)
+//     Generate a uniformly distributed 32-bit random number
+
+uint32_t pcg32_random_r(pcg32_random_t* rng)
+{
+    uint64_t oldstate = rng->state;
+    rng->state = oldstate * 6364136223846793005ULL + rng->inc;
+    uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
+    uint32_t rot = oldstate >> 59u;
+    return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
+}
+
+uint32_t pcg32_random()
+{
+    return pcg32_random_r(&pcg32_global);
+}
+
+
+// pcg32_boundedrand(bound):
+// pcg32_boundedrand_r(rng, bound):
+//     Generate a uniformly distributed number, r, where 0 <= r < bound
+
+uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound)
+{
+    // To avoid bias, we need to make the range of the RNG a multiple of
+    // bound, which we do by dropping output less than a threshold.
+    // A naive scheme to calculate the threshold would be to do
+    //
+    //     uint32_t threshold = 0x100000000ull % bound;
+    //
+    // but 64-bit div/mod is slower than 32-bit div/mod (especially on
+    // 32-bit platforms).  In essence, we do
+    //
+    //     uint32_t threshold = (0x100000000ull-bound) % bound;
+    //
+    // because this version will calculate the same modulus, but the LHS
+    // value is less than 2^32.
+
+    uint32_t threshold = -bound % bound;
+
+    // Uniformity guarantees that this loop will terminate.  In practice, it
+    // should usually terminate quickly; on average (assuming all bounds are
+    // equally likely), 82.25% of the time, we can expect it to require just
+    // one iteration.  In the worst case, someone passes a bound of 2^31 + 1
+    // (i.e., 2147483649), which invalidates almost 50% of the range.  In 
+    // practice, bounds are typically small and only a tiny amount of the range
+    // is eliminated.
+    for (;;) {
+        uint32_t r = pcg32_random_r(rng);
+        if (r >= threshold)
+            return r % bound;
+    }
+}
+
+
+uint32_t pcg32_boundedrand(uint32_t bound)
+{
+    return pcg32_boundedrand_r(&pcg32_global, bound);
+}
+

+ 78 - 0
engine/source/algorithm/pcg_basic.h

@@ -0,0 +1,78 @@
+/*
+ * PCG Random Number Generation for C.
+ *
+ * Copyright 2014 Melissa O'Neill <[email protected]>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * For additional information about the PCG random number generation scheme,
+ * including its license and other licensing options, visit
+ *
+ *     http://www.pcg-random.org
+ */
+
+/*
+ * This code is derived from the full C implementation, which is in turn
+ * derived from the canonical C++ PCG implementation. The C++ version
+ * has many additional features and is preferable if you can use C++ in
+ * your project.
+ */
+
+#ifndef PCG_BASIC_H_INCLUDED
+#define PCG_BASIC_H_INCLUDED 1
+
+#include <inttypes.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+struct pcg_state_setseq_64 {    // Internals are *Private*.
+    uint64_t state;             // RNG state.  All values are possible.
+    uint64_t inc;               // Controls which RNG sequence (stream) is
+                                // selected. Must *always* be odd.
+};
+typedef struct pcg_state_setseq_64 pcg32_random_t;
+
+// If you *must* statically initialize it, here's one.
+
+#define PCG32_INITIALIZER   { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }
+
+// pcg32_srandom(initstate, initseq)
+// pcg32_srandom_r(rng, initstate, initseq):
+//     Seed the rng.  Specified in two parts, state initializer and a
+//     sequence selection constant (a.k.a. stream id)
+
+void pcg32_srandom(uint64_t initstate, uint64_t initseq);
+void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate,
+                     uint64_t initseq);
+
+// pcg32_random()
+// pcg32_random_r(rng)
+//     Generate a uniformly distributed 32-bit random number
+
+uint32_t pcg32_random(void);
+uint32_t pcg32_random_r(pcg32_random_t* rng);
+
+// pcg32_boundedrand(bound):
+// pcg32_boundedrand_r(rng, bound):
+//     Generate a uniformly distributed number, r, where 0 <= r < bound
+
+uint32_t pcg32_boundedrand(uint32_t bound);
+uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound);
+
+#if __cplusplus
+}
+#endif
+
+#endif // PCG_BASIC_H_INCLUDED

+ 23 - 0
engine/source/math/mRandom.cc

@@ -181,4 +181,27 @@ U32 RandomR250::randI( void )
     return new_rand >> 1;
 }
 
+RandomPCG::RandomPCG()
+{
+    pcg32_srandom_r(&rng, generateSeed(), generateSeed());
+}
+
+RandomPCG::RandomPCG(const S32 seed)
+{
+    pcg32_srandom_r(&rng, seed, generateSeed());
+}
+
+RandomPCG::RandomPCG(const S32 seed, const S32 stream)
+{
+    pcg32_srandom_r(&rng, seed, stream);
+}
+
+void RandomPCG::setSeed(const S32 seed)
+{
+    pcg32_srandom_r(&rng, seed, generateSeed());
+}
 
+void RandomPCG::setSeed(const S32 seed, const S32 stream)
+{
+    pcg32_srandom_r(&rng, seed, stream);
+}

+ 32 - 0
engine/source/math/mRandom.h

@@ -27,6 +27,8 @@
 #include "platform/platform.h"
 #endif
 
+#include "algorithm/pcg_basic.h"
+
 //-----------------------------------------------------------------------------
 
 class RandomGeneratorBase
@@ -99,6 +101,36 @@ public:
 
 //-----------------------------------------------------------------------------
 
+
+//-----------------------------------------------------------------------------
+/// PCG Random Number Generator
+/// 
+/// Fast and statistically excellent random numbers
+///
+/// Period = 2^64
+///
+/// Copyright 2014 Melissa O'Neill <[email protected]>
+///
+/// Licensed under the Apache License, Version 2.0
+//-----------------------------------------------------------------------------
+class RandomPCG : public RandomGeneratorBase
+{
+private:
+    pcg32_random_t rng;
+
+public:
+    RandomPCG();
+    RandomPCG(const S32 seed);
+    RandomPCG(const S32 seed, const S32 stream);
+
+    void setSeed(const S32 seed);
+    void setSeed(const S32 seed, const S32 stream);
+    U32 randI(void) { return static_cast<U32>(pcg32_random_r(&rng)); }
+    U32 randI(U32 bound) { return static_cast<U32>(pcg32_boundedrand_r(&rng, static_cast<uint32_t>(bound))); }
+};
+
+//-----------------------------------------------------------------------------
+
 extern RandomLCG gRandGen;
 
 #endif //_MRANDOM_H_

+ 1 - 1
engine/source/math/noise/NoiseGenerator_ScriptBinding.h

@@ -23,7 +23,7 @@
 ConsoleMethodGroupBeginWithDocs(NoiseGenerator, ScriptObject)
 
 /*! Sets the seed for the noise generater.
-@param seed An interger seed value.
+@param seed An integer seed value.
 @return No return value.
 */
 ConsoleMethodWithDocs(NoiseGenerator, setSeed, ConsoleVoid, 3, 3, (int seed))

+ 72 - 0
engine/source/math/noise/RandomNumberGenerator.cc

@@ -0,0 +1,72 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _RANDOM_NUMBER_GENERATOR_H_
+#include "RandomNumberGenerator.h"
+#endif
+
+// Script bindings.
+#include "RandomNumberGenerator_ScriptBinding.h"
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(RandomNumberGenerator);
+
+//------------------------------------------------------------------------------
+
+RandomNumberGenerator::RandomNumberGenerator()
+{
+    mRNG = RandomPCG();
+}
+
+//------------------------------------------------------------------------------
+
+RandomNumberGenerator::~RandomNumberGenerator()
+{
+}
+
+//------------------------------------------------------------------------------
+
+void RandomNumberGenerator::setSeed(const U32 seed)
+{
+    mRNG.setSeed(seed);
+}
+
+void RandomNumberGenerator::setSeed(const U32 seed, const U32 stream)
+{
+    mRNG.setSeed(seed, stream);
+}
+
+U32 RandomNumberGenerator::getSeed()
+{
+    return mRNG.getSeed();
+}
+
+U32 RandomNumberGenerator::getRandom()
+{
+    return mRNG.randI();
+}
+
+U32 RandomNumberGenerator::getRandom(const U32 bound)
+{
+    return mRNG.randI(bound);
+}

+ 65 - 0
engine/source/math/noise/RandomNumberGenerator.h

@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _RANDOM_NUMBER_GENERATOR_H_
+#define _RANDOM_NUMBER_GENERATOR_H_
+
+#ifndef _MRANDOM_H_
+#include "math/mRandom.h"
+#endif
+
+#ifndef _SCRIPT_OBJECT_H_
+#include "sim/scriptObject.h"
+#endif
+
+#ifndef _UTILITY_H_
+#include "2d/core/Utility.h"
+#endif
+
+//-----------------------------------------------------------------------------
+
+class RandomNumberGenerator : public ScriptObject
+{
+	typedef ScriptObject			Parent;
+
+private:
+	RandomPCG					mRNG;
+
+public:
+	RandomNumberGenerator();
+	virtual ~RandomNumberGenerator();
+
+	void setSeed(const U32 seed);
+	void setSeed(const U32 seed, const U32 stream);
+	U32 getSeed();
+	U32 getRandom();
+	U32 getRandom(const U32 bound);
+
+	/// Declare Console Object.
+	DECLARE_CONOBJECT(RandomNumberGenerator);
+
+private:
+
+protected:
+};
+
+#endif // _NOISE_GENERATOR_H_

+ 60 - 0
engine/source/math/noise/RandomNumberGenerator_ScriptBinding.h

@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethodGroupBeginWithDocs(RandomNumberGenerator, ScriptObject)
+
+/*! Sets the seed and stream for the random number generater. Random values will always be the same for a seed/stream combination.
+@param seed An integer seed value.
+@param stream An optional integer value for the stream. If no stream is set a random stream value will be picked.
+@return No return value.
+*/
+ConsoleMethodWithDocs(RandomNumberGenerator, setSeed, ConsoleVoid, 3, 4, (int seed, [int stream]))
+{
+	if (argc == 3)
+	{
+		object->setSeed(dAtoi(argv[2]));
+	}
+	else if (argc == 4)
+	{
+		object->setSeed(dAtoi(argv[2]), dAtoi(argv[3]));
+	}
+}
+
+//------------------------------------------------------------------------------
+
+/*! Returns a random number integer value between 0 and an optional upper bound.
+* @param bound The optional value that the random number will be less than.
+@return A 32 bit number or a value where 0 <= number < bound.
+*/
+ConsoleMethodWithDocs(RandomNumberGenerator, getRandom, ConsoleFloat, 2, 3, ([int bound]))
+{
+	if (argc == 3)
+	{
+		return object->getRandom(dAtoi(argv[2]));
+	}
+	else
+	{
+		return object->getRandom();
+	}
+}
+
+ConsoleMethodGroupEndWithDocs(RandomNumberGenerator)