2
0
Эх сурвалжийг харах

ADDED: Pseudo-random numbers generator!

Ray 1 жил өмнө
parent
commit
fc7dcff4a7
5 өөрчлөгдсөн 340 нэмэгдсэн , 13 устгасан
  1. 1 0
      CMakeOptions.txt
  2. 2 0
      src/config.h
  3. 306 0
      src/external/rprand.h
  4. 2 1
      src/raylib.h
  5. 29 12
      src/rcore.c

+ 1 - 0
CMakeOptions.txt

@@ -37,6 +37,7 @@ cmake_dependent_option(SUPPORT_MODULE_RAUDIO "Include module: raudio" ON CUSTOMI
 # rcore.c
 cmake_dependent_option(SUPPORT_CAMERA_SYSTEM "Provide camera module (rcamera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON CUSTOMIZE_BUILD ON)
 cmake_dependent_option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag" ON CUSTOMIZE_BUILD ON)
+cmake_dependent_option(SUPPORT_RPRAND_GENERATOR "Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64" ON CUSTOMIZE_BUILD ON)
 cmake_dependent_option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON CUSTOMIZE_BUILD ON)
 cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" ON CUSTOMIZE_BUILD ON)
 cmake_dependent_option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON CUSTOMIZE_BUILD ON)

+ 2 - 0
src/config.h

@@ -45,6 +45,8 @@
 #define SUPPORT_CAMERA_SYSTEM           1
 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
 #define SUPPORT_GESTURES_SYSTEM         1
+// Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64
+#define SUPPORT_RPRAND_GENERATOR        1
 // Mouse gestures are directly mapped like touches and processed by gestures system
 #define SUPPORT_MOUSE_GESTURES          1
 // Reconfigure standard input to receive key inputs, works with SSH connection.

+ 306 - 0
src/external/rprand.h

@@ -0,0 +1,306 @@
+/**********************************************************************************************
+*
+*   rprand v1.0 - A simple and easy-to-use pseudo-random numbers generator (PRNG)
+*
+*   FEATURES:
+*       - Pseudo-random values generation, 32 bits: [0..4294967295]
+*       - Sequence generation avoiding duplicate values
+*       - Using standard and proven prng algorithm (Xoshiro128**)
+*       - State initialized with a separate generator (SplitMix64)
+*
+*   LIMITATIONS:
+*       - No negative numbers, up to the user to manage them
+*
+*   POSSIBLE IMPROVEMENTS:
+*       - Support 64 bits generation
+*
+*   ADDITIONAL NOTES:
+*     This library implements two pseudo-random number generation algorithms: 
+*
+*         - Xoshiro128** : https://prng.di.unimi.it/xoshiro128starstar.c
+*         - SplitMix64   : https://prng.di.unimi.it/splitmix64.c
+*
+*     SplitMix64 is used to initialize the Xoshiro128** state, from a provided seed
+*
+*     It's suggested to use SplitMix64 to initialize the state of the generators starting from 
+*     a 64-bit seed, as research has shown that initialization must be performed with a generator 
+*     radically different in nature from the one initialized to avoid correlation on similar seeds.
+*
+*   CONFIGURATION:
+*       #define RPRAND_IMPLEMENTATION
+*           Generates the implementation of the library into the included file.
+*           If not defined, the library is in header only mode and can be included in other headers
+*           or source files without problems. But only ONE file should hold the implementation.
+* 
+*   DEPENDENCIES: none
+*
+*   VERSIONS HISTORY:
+*       1.0 (01-Jun-2023) First version
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2023 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RPRAND_H
+#define RPRAND_H
+
+#define RPRAND_VERSION    "1.0"
+
+// Function specifiers in case library is build/used as a shared library (Windows)
+// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
+#if defined(_WIN32)
+    #if defined(BUILD_LIBTYPE_SHARED)
+        #define RPRAND __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)
+    #elif defined(USE_LIBTYPE_SHARED)
+        #define RPRAND __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)
+    #endif
+#endif
+
+// Function specifiers definition
+#ifndef RPRANDAPI
+    #define RPRANDAPI       // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Allow custom memory allocators
+#ifndef RPRAND_CALLOC
+    #define RPRAND_CALLOC(ptr,sz)     calloc(ptr,sz)
+#endif
+#ifndef RPRAND_FREE
+    #define RPRAND_FREE(ptr)          free(ptr)
+#endif
+
+// Simple log system to avoid RPNG_LOG() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+#define RPRAND_SHOW_LOG_INFO
+#if defined(RPNG_SHOW_LOG_INFO)
+  #define RPRAND_LOG(...) printf(__VA_ARGS__)
+#else
+  #define RPRAND_LOG(...)
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+//...
+
+#ifdef __cplusplus
+extern "C" {                // Prevents name mangling of functions
+#endif
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+//...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+RPRANDAPI void rprand_set_seed(unsigned long long seed);        // Set rprand_state for Xoshiro128**, seed is 64bit
+RPRANDAPI unsigned int rprand_get_value(int min, int max);      // Get random value within a range, min and max included
+
+RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates
+RPRANDAPI void rprand_unload_sequence(unsigned int *sequence);  // Unload pseudo-random numbers sequence
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RPRAND_H
+
+/***********************************************************************************
+*
+*   RPRAND IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RPRAND_IMPLEMENTATION)
+
+#include <stdlib.h>     // Required for: calloc(), free()
+#include <stdint.h>     // Required for data types: uint32_t, uint64_t
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static uint64_t rprand_seed = 0;                // SplitMix64 actual seed
+static uint32_t rprand_state[4] = { 0 };        // Xoshiro128** state, nitialized by SplitMix64
+
+//----------------------------------------------------------------------------------
+// Module internal functions declaration
+//----------------------------------------------------------------------------------
+static uint32_t rprand_xoshiro(void);           // Xoshiro128** generator (uses global rprand_state)
+static uint64_t rprand_splitmix64(void);        // SplitMix64 generator (uses seed to generate rprand_state)
+
+//----------------------------------------------------------------------------------
+// Module functions definition
+//----------------------------------------------------------------------------------
+// Set rprand_state for Xoshiro128**
+// NOTE: We use a custom generation algorithm using SplitMix64
+void rprand_set_seed(unsigned long long seed)
+{
+    rprand_seed = (uint64_t)seed;    // Set SplitMix64 seed for further use
+
+    // To generate the Xoshiro128** state, we use SplitMix64 generator first
+    // We generate 4 pseudo-random 64bit numbers that we combine using their LSB|MSB
+    rprand_state[0] = (uint32_t)(rprand_splitmix64() & 0xffffffff);
+    rprand_state[1] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32);
+    rprand_state[2] = (uint32_t)(rprand_splitmix64() & 0xffffffff);
+    rprand_state[3] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32);
+}
+
+// Get random value within a range, min and max included
+unsigned int rprand_get_value(int min, int max)
+{
+    unsigned int value = rprand_xoshiro()%(max - min) + min;
+
+    return value;
+}
+
+// Load pseudo-random numbers sequence with no duplicates
+unsigned int *rprand_load_sequence(unsigned int count, int min, int max)
+{
+    unsigned int *sequence = NULL;
+    
+    if (count > (max - min)) 
+    {
+        RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n");
+        //count = (max - min);
+        return sequence;
+    }
+
+    sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int));
+
+    uint32_t value = 0;
+    int value_count = 0;
+    bool value_is_dup = false;
+
+    for (int i = 0; value_count < count; i++)
+    {
+        value = rprand_xoshiro()%(max - min) + min;
+        value_is_dup = false;
+
+        for (int j = 0; j < value_count; j++)
+        {
+            if (sequence[j] == value)
+            {
+                value_is_dup = true;
+                break;
+            }
+        }
+
+        if (!value_is_dup)
+        {
+            sequence[value_count] = value;
+            value_count++;
+        }
+    }
+
+    return sequence;
+}
+
+// Unload pseudo-random numbers sequence
+void rprand_unload_sequence(unsigned int *sequence)
+{
+    RPRAND_FREE(sequence);
+    sequence = NULL;
+}
+
+//----------------------------------------------------------------------------------
+// Module internal functions definition
+//----------------------------------------------------------------------------------
+static inline uint32_t rprand_rotate_left(const uint32_t x, int k)
+{
+    return (x << k) | (x >> (32 - k));
+}
+
+// Xoshiro128** generator info:
+//   
+//   Written in 2018 by David Blackman and Sebastiano Vigna ([email protected])
+//   
+//   To the extent possible under law, the author has dedicated all copyright
+//   and related and neighboring rights to this software to the public domain
+//   worldwide. This software is distributed without any warranty.
+//   
+//   See <http://creativecommons.org/publicdomain/zero/1.0/>.
+//   
+//   This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid
+//   generators. It has excellent speed, a state size (128 bits) that is
+//   large enough for mild parallelism, and it passes all tests we are aware
+//   of.
+// 
+//   Note that version 1.0 had mistakenly s[0] instead of s[1] as state
+//   word passed to the scrambler.
+// 
+//   For generating just single-precision (i.e., 32-bit) floating-point
+//   numbers, xoshiro128+ is even faster.
+// 
+//   The state must be seeded so that it is not everywhere zero.
+//
+uint32_t rprand_xoshiro(void)
+{
+    const uint32_t result = rprand_rotate_left(rprand_state[1]*5, 7)*9;
+    const uint32_t t = rprand_state[1] << 9;
+
+    rprand_state[2] ^= rprand_state[0];
+    rprand_state[3] ^= rprand_state[1];
+    rprand_state[1] ^= rprand_state[2];
+    rprand_state[0] ^= rprand_state[3];
+    
+    rprand_state[2] ^= t;
+    
+    rprand_state[3] = rprand_rotate_left(rprand_state[3], 11);
+
+    return result;
+}
+
+// SplitMix64 generator info:
+//   
+//   Written in 2015 by Sebastiano Vigna ([email protected])
+//   
+//   To the extent possible under law, the author has dedicated all copyright
+//   and related and neighboring rights to this software to the public domain
+//   worldwide. This software is distributed without any warranty.
+//   
+//   See <http://creativecommons.org/publicdomain/zero/1.0/>.
+//   
+//
+//   This is a fixed-increment version of Java 8's SplittableRandom generator
+//   See http://dx.doi.org/10.1145/2714064.2660195 and
+//   http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
+//   
+//   It is a very fast generator passing BigCrush, and it can be useful if
+//   for some reason you absolutely want 64 bits of state.
+uint64_t rprand_splitmix64()
+{
+    uint64_t z = (rprand_seed += 0x9e3779b97f4a7c15);
+    z = (z ^ (z >> 30))*0xbf58476d1ce4e5b9;
+    z = (z ^ (z >> 27))*0x94d049bb133111eb;
+    return z ^ (z >> 31);
+}
+
+#endif  // RPRAND_IMPLEMENTATION

+ 2 - 1
src/raylib.h

@@ -1068,8 +1068,9 @@ RLAPI void PollInputEvents(void);                                 // Register al
 RLAPI void WaitTime(double seconds);                              // Wait for some time (halt program execution)
 
 // Misc. functions
-RLAPI int GetRandomValue(int min, int max);                       // Get a random value between min and max (both included)
 RLAPI void SetRandomSeed(unsigned int seed);                      // Set the seed for the random number generator
+RLAPI int GetRandomValue(int min, int max);                       // Get a random value between min and max (both included)
+
 RLAPI void TakeScreenshot(const char *fileName);                  // Takes a screenshot of current screen (filename extension defines format)
 RLAPI void SetConfigFlags(unsigned int flags);                    // Setup init configuration flags (view FLAGS)
 RLAPI void OpenURL(const char *url);                              // Open URL with default system browser (if available)

+ 29 - 12
src/rcore.c

@@ -131,6 +131,11 @@
     #include "external/sdefl.h"     // Deflate (RFC 1951) compressor
 #endif
 
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    #define RPRAND_IMPLEMENTATION
+    #include "external/rprand.h"
+#endif
+
 #if defined(__linux__) && !defined(_GNU_SOURCE)
     #define _GNU_SOURCE
 #endif
@@ -1647,31 +1652,43 @@ void WaitTime(double seconds)
 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
 //void OpenURL(const char *url)
 
+
+// Set the seed for the random number generator
+void SetRandomSeed(unsigned int seed)
+{
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    rprand_set_seed(seed);
+#else
+    srand(seed);
+#endif
+}
+
 // Get a random value between min and max (both included)
-// WARNING: Ranges higher than RAND_MAX will return invalid results
-// More specifically, if (max - min) > INT_MAX there will be an overflow,
-// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
 int GetRandomValue(int min, int max)
 {
+    int value = 0;
+    
     if (min > max)
     {
         int tmp = max;
         max = min;
         min = tmp;
     }
-
+    
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    value = rprand_get_value(min, max);
+#else 
+    // WARNING: Ranges higher than RAND_MAX will return invalid results
+    // More specifically, if (max - min) > INT_MAX there will be an overflow,
+    // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
     if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
     {
         TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX);
     }
-
-    return (rand()%(abs(max - min) + 1) + min);
-}
-
-// Set the seed for the random number generator
-void SetRandomSeed(unsigned int seed)
-{
-    srand(seed);
+    
+    value = (rand()%(abs(max - min) + 1) + min);
+#endif
+    return value;
 }
 
 // Takes a screenshot of current screen (saved a .png)