New RNG (Bob Jenkins' small non-crypto PRNG) that actually matches whatever I took...
authorMartin Read <martin@blackswordsonics.com>
Sat, 8 Feb 2014 17:11:31 +0000 (17:11 +0000)
committerMartin Read <martin@blackswordsonics.com>
Sat, 8 Feb 2014 17:11:31 +0000 (17:11 +0000)
rng.cc
rng.hh [new file with mode: 0644]

diff --git a/rng.cc b/rng.cc
index 1e9fe55..cf5994e 100644 (file)
--- a/rng.cc
+++ b/rng.cc
  */
 
 
-#include "victrix-abyssi.hh"
+#include "rng.hh"
 #include <time.h>
 #include <unistd.h>
 #include <stdint.h>
+#include <arpa/inet.h>
 
-uint32_t rng_state[5];
-uint32_t saved_state[5];
+Smallprng rng;
+void *saved_state_buffer;
+int saved_state_size;
 
-uint32_t rng(void)
+void Smallprng::extract_serialization(void *data, int size) const
 {
-    uint32_t tmp;
-    tmp = rng_state[0] ^ (rng_state[0] >> 7);
-    rng_state[0] = rng_state[1];
-    rng_state[1] = rng_state[2];
-    rng_state[2] = rng_state[3];
-    rng_state[3] = rng_state[4];
-    rng_state[4] = (rng_state[4] ^ (rng_state[4] << 6)) ^ (tmp ^ (tmp << 13));
-    return (rng_state[2] + rng_state[2] + 1) * rng_state[4];
+    if (size >= state_size())
+    {
+        uint32_t *foo = (uint32_t *) data;
+        foo[0] = htonl(a);
+        foo[1] = htonl(b);
+        foo[2] = htonl(c);
+        foo[3] = htonl(d);
+    }
+    else
+    {
+        // TODO throw a wobbly or at least an exception
+    }
+}
+
+void Smallprng::inject_serialization(void const *data, int size)
+{
+    if (size >= state_size())
+    {
+        uint32_t const *foo = (uint32_t const *) data;
+        a = ntohl(foo[0]);
+        b = ntohl(foo[1]);
+        c = ntohl(foo[2]);
+        d = ntohl(foo[3]);
+    }
+    else
+    {
+        // TODO throw a wobbly or at least an exception
+    }
 }
 
 void rng_init(void)
 {
-    int i;
-    /* To make manipulating the RNG by monitoring the system time
+    /* To make manipulating the Really Nasty God by monitoring the system time
      * marginally harder, we use PID and UID to perturb the return value of
-     * time() used to initialise the libc RNG.
+     * time() used to initialise it.
      *
-     * Yes, I am aware that the libc RNG on many platforms is a steaming
-     * pile of shite. However, I need *something* with which to populate
-     * my RNG's state array.
+     * (The currently selected PRNG has an initialization function which
+     * only takes one input value.)
      */
-    srand(time(NULL) ^ getpid() ^ (getuid() << 16));
-    rng_state[0] = rand();
-    rng_state[1] = rand();
-    rng_state[2] = rand();
-    rng_state[3] = rand();
-    rng_state[4] = rand();
-    /* Flush through the first 100 numbers just in case some bastard
-     * tries to run us with a 16-bit rand(). */
-    for (i = 0; i < 100; i++)
+    rng.init((uint32_t) (time(NULL) ^ getpid() ^ (getuid() << 16)));
+    saved_state_size = rng.state_size();
+    saved_state_buffer = malloc(saved_state_size);
+}
+
+int exclusive_flat(int lower, int upper)
+{
+    return lower + one_die(upper - lower - 1);
+}
+
+int inclusive_flat(int lower, int upper)
+{
+    return lower + zero_die(upper - lower + 1);
+}
+
+Coord inclusive_boxed(Coord topleft, Coord botright)
+{
+    Coord c = { inclusive_flat(topleft.y, botright.y), inclusive_flat(topleft.x, botright.x) };
+    return c;
+}
+
+Coord exclusive_boxed(Coord topleft, Coord botright)
+{
+    Coord c = { exclusive_flat(topleft.y, botright.y), exclusive_flat(topleft.x, botright.x) };
+    return c;
+}
+
+int dice(int count, int sides)
+{
+    int total = 0;
+    for ( ; count > 0; count--)
+    {
+        total += one_die(sides);
+    }
+    return total;
+}
+
+int zero_die(int sides)
+{
+    int rval;
+    if (sides < 2)
+    {
+        return 0;
+    }
+    // This imposes a very slight bias. The more correct approach is
+    // clumsy to read.
+    rval = rng() / ((RNG_MAX / sides) + 1);
+    return rval;
+}
+
+int one_die(int sides)
+{
+    int rval;
+    if (sides < 2)
     {
-        rng();
+        return 1;
     }
+    rval = 1 + (rng() / ((RNG_MAX / sides) + 1));
+    return rval;
 }
 
-/* rng.c */
+/* rng.cc */
 // vim:cindent
diff --git a/rng.hh b/rng.hh
new file mode 100644 (file)
index 0000000..d46bd5c
--- /dev/null
+++ b/rng.hh
@@ -0,0 +1,98 @@
+/*! \file rng.hh
+ *  \brief RNG-related header for Victrix Abyssi
+ */
+
+/* Copyright 2014 Martin Read
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RNG_HH
+#define RNG_HH
+
+#include <stdint.h>
+
+#ifndef COORD_HH
+#include "coord.hh"
+#endif
+
+#define RNG_MAX 0xFFFFFFFFu
+extern void *saved_state_buffer;
+extern int saved_state_size;
+//extern uint32_t rng(void);
+extern void rng_init(void);
+extern int zero_die(int sides); /* 0..n-1 */
+extern int one_die(int sides);  /* 1..n */
+extern int dice(int count, int sides);
+extern int exclusive_flat(int lower, int upper); /* l+1 ... u-1 */
+extern int inclusive_flat(int lower, int upper); /* l ... u */
+extern Coord inclusive_boxed(Coord topleft, Coord botright);
+extern Coord exclusive_boxed(Coord topleft, Coord botright);
+
+/*! \brief An instance of Bob Jenkins' "small noncryptographic PRNG" 
+ *
+ * See http://burtleburtle.net/bob/rand/smallprng.html for an explanation
+ * of this generator.
+ */
+struct Smallprng
+{
+    uint32_t a;
+    uint32_t b;
+    uint32_t c;
+    uint32_t d;
+    enum { magic = 0xf1ea5eed };
+    void init(uint32_t seed)
+    {
+        int i;
+        a = magic; b = c = d = seed;
+        for (i = 0; i < 20; ++i)
+        {
+            (*this)();
+        }
+    }
+    static uint32_t rot(uint32_t val, int bits)
+    {
+        return (val << bits) | (val >> (32 - bits));
+    }
+    uint32_t operator()(void)
+    {
+        uint32_t e = a - rot(b, 27);
+        a = b ^ rot(c, 17);
+        b = c + d;
+        c = d + e;
+        d = e + a;
+        return d;
+    }
+    static int state_size()
+    {
+        return 16;
+    }
+    void extract_serialization(void * buf, int size) const;
+    void inject_serialization(void const * buf, int size);
+};
+
+extern Smallprng rng;
+
+#endif
+
+/* rng.hh */
+// vim:cindent:ts=8:sw=4:expandtab