summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBardur Arantsson <bardur@scientician.net>2016-09-17 09:58:14 +0200
committerBardur Arantsson <bardur@scientician.net>2016-09-17 09:58:14 +0200
commit6d11bb4a2d5bc8ab7c1491639f1083532b1b8fd1 (patch)
tree0a1ee125b7c5ef7ec37ff0781392989be4f75e26 /src
parent36969a9058806e71078efcb04a91cc263612d42e (diff)
Replace RNG with PCG random number generator
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/birth.cc4
-rw-r--r--src/dungeon.cc27
-rw-r--r--src/generate.cc3
-rw-r--r--src/loadsave.cc78
-rw-r--r--src/object1.cc9
-rw-r--r--src/seed.cc53
-rw-r--r--src/seed.hpp51
-rw-r--r--src/seed_fwd.hpp3
-rw-r--r--src/spells2.cc12
-rw-r--r--src/town_type.hpp3
-rw-r--r--src/variable.cc6
-rw-r--r--src/variable.hpp3
-rw-r--r--src/wild.cc17
-rw-r--r--src/wilderness_map.hpp3
-rw-r--r--src/z-rand.cc294
-rw-r--r--src/z-rand.hpp32
17 files changed, 315 insertions, 285 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0cce20be..7b4d025b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,6 +2,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../vendor/bandit)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../vendor/fmt)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../vendor/pcg-cpp/include)
# Add subdirectories
ADD_SUBDIRECTORY (squelch)
@@ -105,6 +106,7 @@ SET(SRCS_COMMON
variable.cc
wild.cc
wizard2.cc
+ seed.cc
xtra1.cc
xtra2.cc
z-form.c
diff --git a/src/birth.cc b/src/birth.cc
index c4d3718e..1fc5d515 100644
--- a/src/birth.cc
+++ b/src/birth.cc
@@ -3050,7 +3050,7 @@ void init_town(int t_idx)
t_ptr->flags &= ~(TOWN_KNOWN);
/* Generation seed for the town */
- t_ptr->seed = randint(0x10000000);
+ t_ptr->seed = seed_t::system();
}
/*
@@ -3189,7 +3189,7 @@ void player_birth(void)
{
for (j = 0; j < max_wild_y; j++)
{
- wild_map[j][i].seed = rand_int(0x10000000);
+ wild_map[j][i].seed = seed_t::system();
wild_map[j][i].entrance = 0;
wild_map[j][i].known = FALSE;
}
diff --git a/src/dungeon.cc b/src/dungeon.cc
index eeec226b..b02b2a87 100644
--- a/src/dungeon.cc
+++ b/src/dungeon.cc
@@ -5125,27 +5125,8 @@ void play_game()
process_player_name(FALSE);
}
- /* Init the RNG */
- if (Rand_quick)
- {
- u32b seed;
-
- /* Basic seed */
- seed = (time(NULL));
-
-#ifdef SET_UID
-
- /* Mutate the seed on Unix machines */
- seed = ((seed >> 3) * (getpid() << 1));
-
-#endif
-
- /* Use the complex RNG */
- Rand_quick = FALSE;
-
- /* Seed the "complex" RNG */
- Rand_state_init(seed);
- }
+ /* Force "complex" RNG */
+ set_complex_rng();
/* Roll new character */
if (new_game)
@@ -5156,8 +5137,8 @@ void play_game()
/* The dungeon is not ready */
character_dungeon = FALSE;
- /* Hack -- seed for flavors */
- seed_flavor = rand_int(0x10000000);
+ /* Set the seed for flavors */
+ seed_flavor() = seed_t::system();
/* Roll up a new character */
player_birth();
diff --git a/src/generate.cc b/src/generate.cc
index ec6d002d..573a58f6 100644
--- a/src/generate.cc
+++ b/src/generate.cc
@@ -8299,8 +8299,7 @@ void generate_cave(void)
/* Seed the RNG if appropriate */
if (town_level)
{
- Rand_quick = TRUE;
- Rand_value = town_info[town_level].seed;
+ set_quick_rng(town_info[town_level].seed);
}
process_hooks_new(HOOK_GEN_LEVEL_BEGIN, NULL, NULL);
diff --git a/src/loadsave.cc b/src/loadsave.cc
index 1843c414..8bea40f4 100644
--- a/src/loadsave.cc
+++ b/src/loadsave.cc
@@ -315,9 +315,37 @@ template<typename T, typename F> void do_vector(ls_flag_t flag, std::vector<T> &
}
}
+static void do_bytes(ls_flag_t flag, std::uint8_t *buf, std::size_t n)
+{
+ for (std::size_t i = 0; i < n; i++)
+ {
+ do_byte(&buf[i], flag);
+ }
+};
+
+static void do_seed(seed_t *seed, ls_flag_t flag)
+{
+ uint8_t buf[seed_t::n_bytes];
+
+ if (flag == ls_flag_t::SAVE)
+ {
+ seed->to_bytes(buf);
+ }
+
+ do_bytes(flag, buf, sizeof(buf));
+
+ if (flag == ls_flag_t::LOAD)
+ {
+ *seed = seed_t::from_bytes(buf);
+ }
+}
+
} // namespace (anonymous)
+/*
+ * Load/Save quick start data
+ */
static void do_quick_start(ls_flag_t flag)
{
do_s16b(&previous_char.race, flag);
@@ -878,8 +906,8 @@ static bool_ do_extra(ls_flag_t flag)
}
}
- /* Load random seeds */
- do_u32b(&seed_flavor, flag); /* For consistent object flavors. */
+ /* Random seed for object flavors. */
+ do_seed(&seed_flavor(), flag);
/* Special stuff */
do_u16b(&total_winner, flag);
@@ -1676,19 +1704,24 @@ static void do_store(store_type *str, ls_flag_t flag)
*/
static void do_randomizer(ls_flag_t flag)
{
- /* Place */
- do_u16b(&Rand_place, flag);
+ std::string state;
+
+ if (flag == ls_flag_t::SAVE)
+ {
+ state = get_complex_rng_state();
+ }
- /* State */
- for (std::size_t i = 0; i < RAND_DEG; i++)
+ do_std_string(state, flag);
+
+ if (flag == ls_flag_t::LOAD)
{
- do_u32b(&Rand_state[i], flag);
+ set_complex_rng_state(state);
}
/* Accept */
if (flag == ls_flag_t::LOAD)
{
- Rand_quick = FALSE;
+ set_complex_rng();
}
}
@@ -2129,25 +2162,6 @@ static bool do_object_lore(ls_flag_t flag)
}
-/*
- * Note that this function may not be needed at all.
- * It was taken out of load_player_aux(). Do we need it?
- */
-static void junkinit(void)
-{
- p_ptr->inside_quest = 0;
- p_ptr->town_num = 1;
- p_ptr->wilderness_x = 4;
- p_ptr->wilderness_y = 4;
- for (std::size_t i = 0; i < max_wild_x; i++)
- {
- for (std::size_t j = 0; j < max_wild_y; j++)
- {
- wild_map[j][i].seed = rand_int(0x10000000);
- }
- }
-}
-
static bool do_towns(ls_flag_t flag)
{
u16b max_towns_ldsv = max_towns;
@@ -2181,7 +2195,7 @@ static bool do_towns(ls_flag_t flag)
if (i >= TOWN_RANDOM)
{
- do_u32b(&town->seed, flag);
+ do_seed(&town->seed, flag);
do_byte(&town->flags, flag);
// Create stock if necessary
@@ -2289,7 +2303,7 @@ static bool do_wilderness(ls_flag_t flag)
for (std::size_t j = 0; j < wild_y_size; j++)
{
auto w = &wild_map[j][i];
- do_u32b(&w->seed, flag);
+ do_seed(&w->seed, flag);
do_u16b(&w->entrance, flag);
do_bool(&w->known, flag);
}
@@ -2517,12 +2531,6 @@ static bool_ do_savefile_aux(ls_flag_t flag)
return FALSE;
}
- // Initialize
- if (flag == ls_flag_t::LOAD)
- {
- junkinit();
- }
-
if (!do_towns(flag))
{
return FALSE;
diff --git a/src/object1.cc b/src/object1.cc
index c4945e6b..3f5d123f 100644
--- a/src/object1.cc
+++ b/src/object1.cc
@@ -507,12 +507,8 @@ static void shuffle_flavors(cptr adj[], byte col[])
*/
void flavor_init(void)
{
- /* Hack -- Use the "simple" RNG */
- Rand_quick = TRUE;
-
/* Hack -- Induce consistant flavors */
- Rand_value = seed_flavor;
-
+ set_quick_rng(seed_flavor());
/* Efficiency -- Rods/Wands share initial array */
for (std::size_t i = 0; i < MAX_METALS; i++)
@@ -599,9 +595,8 @@ void flavor_init(void)
scroll_col[i] = TERM_WHITE;
}
-
/* Hack -- Use the "complex" RNG */
- Rand_quick = FALSE;
+ set_complex_rng();
/* Analyze every object */
for (std::size_t i = 1; i < max_k_idx; i++)
diff --git a/src/seed.cc b/src/seed.cc
new file mode 100644
index 00000000..0550c769
--- /dev/null
+++ b/src/seed.cc
@@ -0,0 +1,53 @@
+#include "seed.hpp"
+
+#include <random>
+
+seed_t seed_t::system()
+{
+ seed_t seed;
+ // Use system's random device for seeding.
+ std::random_device random_device;
+ std::uniform_int_distribution<std::uint8_t> distribution;
+ // Extract the number of bytes we need.
+ for (std::size_t i = 0; i < n_bytes; i++)
+ {
+ seed.m_data[i] = distribution(random_device);
+ }
+ // Done
+ return seed;
+}
+
+seed_t seed_t::from_bytes(std::uint8_t bytes[n_bytes])
+{
+ seed_t seed;
+ // Copy
+ for (std::size_t i = 0; i < n_bytes; i++)
+ {
+ seed.m_data[i] = bytes[i];
+ }
+ // Done
+ return seed;
+}
+
+void seed_t::to_bytes(std::uint8_t bytes[n_bytes]) const
+{
+ // Copy
+ for (std::size_t i = 0; i < n_bytes; i++)
+ {
+ bytes[i] = m_data[i];
+ }
+}
+void seed_t::to_uint32(std::uint32_t seed_seq_data[n_uint32]) const
+{
+ for (std::size_t i = 0; i < n_uint32; i++)
+ {
+ // Position in the byte-oriented data.
+ std::size_t p = 4 * i;
+ // Pack m_data[p + 0], ..., m_data[p + 3] into a single uint32_t
+ seed_seq_data[i] = 0;
+ seed_seq_data[i] |= (uint32_t(m_data[p + 0]) << (0 * 8));
+ seed_seq_data[i] |= (uint32_t(m_data[p + 1]) << (1 * 8));
+ seed_seq_data[i] |= (uint32_t(m_data[p + 2]) << (2 * 8));
+ seed_seq_data[i] |= (uint32_t(m_data[p + 3]) << (3 * 8));
+ }
+}
diff --git a/src/seed.hpp b/src/seed.hpp
new file mode 100644
index 00000000..91ec7eba
--- /dev/null
+++ b/src/seed.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <array>
+#include <cstdint>
+
+class seed_t {
+
+public:
+ // Number of seed bytes.
+ static constexpr std::size_t n_bytes = 64;
+
+ // Sanity check; we're relying on converting to uint32_t elsewhere,
+ // so let's just keep it as easy as possible.
+ static_assert(n_bytes % 4 == 0, "n_bytes must be multiple of 4");
+
+ // Number of uint32_t's required to store the seed.
+ static constexpr std::size_t n_uint32 = n_bytes / 4;
+
+private:
+ std::array<std::uint8_t, n_bytes> m_data;
+
+ // Default constructor is private. Use the static
+ // factory functions instead.
+ seed_t()
+ {
+ // Factory functions do explicit initialization.
+ };
+
+public:
+
+ /**
+ * Create a seed from system entropy.
+ */
+ static seed_t system();
+
+ /**
+ * Create a seed from the given bytes.
+ */
+ static seed_t from_bytes(std::uint8_t bytes[n_bytes]);
+
+ /**
+ * Convert seed to bytes.
+ */
+ void to_bytes(std::uint8_t bytes[n_bytes]) const;
+
+ /**
+ * Convert seed to uint32_t's suitable for seed_seq.
+ */
+ void to_uint32(std::uint32_t seed_seq_data[n_uint32]) const;
+
+};
diff --git a/src/seed_fwd.hpp b/src/seed_fwd.hpp
new file mode 100644
index 00000000..291a6063
--- /dev/null
+++ b/src/seed_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+class seed_t;
diff --git a/src/spells2.cc b/src/spells2.cc
index bfd513e6..0cdf08ed 100644
--- a/src/spells2.cc
+++ b/src/spells2.cc
@@ -78,8 +78,8 @@ void grow_things(s16b type, int rad)
for (a = 0; a < rad * rad + 11; a++)
{
- i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
- j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ i = (rand_int((rad * 2) + 1)-rad + rand_int((rad * 2) + 1)-rad) / 2;
+ j = (rand_int((rad * 2) + 1)-rad + rand_int((rad * 2) + 1)-rad) / 2;
if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
@@ -100,8 +100,8 @@ void grow_trees(int rad)
for (a = 0; a < rad * rad + 11; a++)
{
- i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
- j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ i = (rand_int((rad * 2) + 1)-rad + rand_int((rad * 2) + 1)-rad) / 2;
+ j = (rand_int((rad * 2) + 1)-rad + rand_int((rad * 2) + 1)-rad) / 2;
if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
@@ -122,8 +122,8 @@ void grow_grass(int rad)
for (a = 0; a < rad * rad + 11; a++)
{
- i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
- j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ i = (rand_int((rad * 2) + 1)-rad + rand_int((rad * 2) + 1)-rad) / 2;
+ j = (rand_int((rad * 2) + 1)-rad + rand_int((rad * 2) + 1)-rad) / 2;
if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
diff --git a/src/town_type.hpp b/src/town_type.hpp
index e119968b..0b903138 100644
--- a/src/town_type.hpp
+++ b/src/town_type.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "h-basic.h"
+#include "seed.hpp"
#include "store_type_fwd.hpp"
#include <vector>
@@ -12,7 +13,7 @@ struct town_type
{
cptr name = nullptr;
- u32b seed = 0; /* Seed for RNG */
+ seed_t seed = seed_t::system(); /* Seed for RNG */
std::vector<store_type> store; /* The stores [max_st_idx] */
diff --git a/src/variable.cc b/src/variable.cc
index 9bb78ca9..00fd340d 100644
--- a/src/variable.cc
+++ b/src/variable.cc
@@ -42,7 +42,11 @@ bool_ character_loaded; /* The character was loaded from a savefile */
bool_ character_icky; /* The game is in an icky full screen mode */
bool_ character_xtra; /* The game is in an icky startup mode */
-u32b seed_flavor; /* Hack -- consistent object colors */
+seed_t &seed_flavor()
+{
+ static seed_t *instance = new seed_t(seed_t::system());
+ return *instance;
+}
s16b command_cmd; /* Current "Angband Command" */
diff --git a/src/variable.hpp b/src/variable.hpp
index b69751a4..9d737930 100644
--- a/src/variable.hpp
+++ b/src/variable.hpp
@@ -48,6 +48,7 @@
#include "vault_type_fwd.hpp"
#include "wilderness_map_fwd.hpp"
#include "wilderness_type_info_fwd.hpp"
+#include "seed.hpp"
extern int max_macrotrigger;
extern char *macro_template;
@@ -58,7 +59,7 @@ extern char *macro_trigger_keycode[2][MAX_MACRO_TRIG];
extern bool_ character_dungeon;
extern bool_ character_loaded;
extern bool_ character_xtra;
-extern u32b seed_flavor;
+extern seed_t &seed_flavor();
extern s16b command_cmd;
extern s16b command_arg;
extern s16b command_rep;
diff --git a/src/wild.cc b/src/wild.cc
index 2d751b17..cbd899f7 100644
--- a/src/wild.cc
+++ b/src/wild.cc
@@ -186,11 +186,8 @@ static int generate_area(int y, int x, bool_ border, bool_ corner)
terrain[2][1] = wild_map[yp][x].feat;
terrain[2][2] = wild_map[yp][xp].feat;
- /* Hack -- Use the "simple" RNG */
- Rand_quick = TRUE;
-
/* Hack -- Induce consistant town layout */
- Rand_value = wild_map[y][x].seed;
+ set_quick_rng(wild_map[y][x].seed);
/* Create level background */
for (y1 = 0; y1 < MAX_HGT; y1++)
@@ -217,9 +214,6 @@ static int generate_area(int y, int x, bool_ border, bool_ corner)
plasma_recursive(1, 1, MAX_WID - 2, MAX_HGT - 2, MAX_WILD_TERRAIN - 1, roughness);
}
- /* Use the complex RNG */
- Rand_quick = FALSE;
-
for (y1 = 1; y1 < MAX_HGT - 1; y1++)
{
for (x1 = 1; x1 < MAX_WID - 1; x1++)
@@ -229,6 +223,8 @@ static int generate_area(int y, int x, bool_ border, bool_ corner)
}
}
+ /* Change back to "complex" RNG */
+ set_complex_rng();
}
/* Should we create a town ? */
@@ -297,11 +293,8 @@ static int generate_area(int y, int x, bool_ border, bool_ corner)
}
}
- /* Hack -- Use the "simple" RNG */
- Rand_quick = TRUE;
-
/* Hack -- Induce consistant town layout */
- Rand_value = wild_map[y][x].seed;
+ set_quick_rng(wild_map[y][x].seed);
entrance = wf_info[wild_map[y][x].feat].entrance;
if (!entrance) entrance = wild_map[y][x].entrance;
@@ -320,7 +313,7 @@ static int generate_area(int y, int x, bool_ border, bool_ corner)
}
/* Use the complex RNG */
- Rand_quick = FALSE;
+ set_complex_rng();
/* MEGA HACK -- set at least one floor grid */
for (y1 = 1; y1 < cur_hgt - 1; y1++)
diff --git a/src/wilderness_map.hpp b/src/wilderness_map.hpp
index e1d795f5..3db36101 100644
--- a/src/wilderness_map.hpp
+++ b/src/wilderness_map.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "h-basic.h"
+#include "seed.hpp"
/**
* A structure describing a wilderness map
@@ -8,7 +9,7 @@
struct wilderness_map
{
int feat = 0; /* Wilderness feature */
- u32b seed = 0; /* Seed for the RNG */
+ seed_t seed = seed_t::system(); /* Seed for the RNG when building tile */
u16b entrance = 0; /* Entrance for dungeons */
bool_ known = FALSE; /* Is it seen by the player ? */
};
diff --git a/src/z-rand.cc b/src/z-rand.cc
index e2960a55..0f507cb6 100644
--- a/src/z-rand.cc
+++ b/src/z-rand.cc
@@ -4,214 +4,113 @@
#include "z-rand.hpp"
-
-
-
-/*
- * Angband 2.7.9 introduced a new (optimized) random number generator,
- * based loosely on the old "random.c" from Berkeley but with some major
- * optimizations and algorithm changes. See below for more details.
- *
- * Code by myself (benh@phial.com) and Randy (randy@stat.tamu.edu).
- *
- * This code provides (1) a "decent" RNG, based on the "BSD-degree-63-RNG"
- * used in Angband 2.7.8, but rather optimized, and (2) a "simple" RNG,
- * based on the simple "LCRNG" currently used in Angband, but "corrected"
- * to give slightly better values. Both of these are available in two
- * flavors, first, the simple "mod" flavor, which is fast, but slightly
- * biased at high values, and second, the simple "div" flavor, which is
- * less fast (and potentially non-terminating) but which is not biased
- * and is much less subject to low-bit-non-randomness problems.
- *
- * You can select your favorite flavor by proper definition of the
- * "rand_int()" macro in the "defines.h" file.
- *
- * Note that, in Angband 2.8.0, the "state" table will be saved in the
- * savefile, so a special "initialization" phase will be necessary.
- *
- * Note the use of the "simple" RNG, first you activate it via
- * "Rand_quick = TRUE" and "Rand_value = seed" and then it is used
- * automatically used instead of the "complex" RNG, and when you are
- * done, you de-activate it via "Rand_quick = FALSE" or choose a new
- * seed via "Rand_value = seed".
- */
-
-
-/*
- * Random Number Generator -- Linear Congruent RNG
- */
-#define LCRNG(X) ((X) * 1103515245 + 12345)
-
-
-
-/*
- * Use the "simple" LCRNG
- */
-bool_ Rand_quick = TRUE;
-
-
-/*
- * Current "value" of the "simple" RNG
+#include <assert.h>
+#include <cstdint>
+#include <limits>
+#include <random>
+#include <sstream>
+
+#include "pcg_random.hpp"
+#include "seed.hpp"
+
+/**
+ * Choice of RNG; we use the "statistically most powerful" (per the
+ * documentation) RNG. The "insecure" bit just means that the RNG
+ * is definitely known to *not* be cryptographically secure.
*/
-u32b Rand_value;
+using rng_t = pcg64_once_insecure;
-
-/*
- * Current "index" for the "complex" RNG
- */
-u16b Rand_place;
-
-/*
- * Current "state" table for the "complex" RNG
+/**
+ * Reseed the given RNG.
*/
-u32b Rand_state[RAND_DEG];
-
-
+static void reseed_rng(rng_t *rng, seed_t const &seed)
+{
+ assert(rng != nullptr);
+ // Create a seed_seq from the seed data.
+ std::uint32_t data[seed_t::n_uint32];
+ std::seed_seq seed_seq(
+ std::begin(data),
+ std::end(data)
+ );
+ // Seed the RNG.
+ rng->seed(seed_seq);
+}
-/*
- * Initialize the "complex" RNG using a new seed
+/**
+ * Allocate a new RNG and initialize with the given seed.
*/
-void Rand_state_init(u32b seed)
+static rng_t *new_seeded_rng(seed_t const &seed)
{
- int i, j;
-
- /* Seed the table */
- Rand_state[0] = seed;
-
- /* Propagate the seed */
- for (i = 1; i < RAND_DEG; i++) Rand_state[i] = LCRNG(Rand_state[i - 1]);
-
- /* Cycle the table ten times per degree */
- for (i = 0; i < RAND_DEG * 10; i++)
- {
- /* Acquire the next index */
- j = Rand_place + 1;
- if (j == RAND_DEG) j = 0;
-
- /* Update the table, extract an entry */
- Rand_state[j] += Rand_state[Rand_place];
-
- /* Advance the index */
- Rand_place = j;
- }
+ rng_t *rng = new rng_t;
+ reseed_rng(rng, seed);
+ return rng;
}
-
-/*
- * Extract a "random" number from 0 to m-1, via "modulus"
- *
- * Note that "m" should probably be less than 500000, or the
- * results may be rather biased towards low values.
+/**
+ * The "quick" RNG is used for fixed-seed applications.
*/
-s32b Rand_mod(s32b m)
+static rng_t *quick_rng()
{
- int j;
- u32b r;
-
- /* Hack -- simple case */
- if (m <= 1) return (0);
-
- /* Use the "simple" RNG */
- if (Rand_quick)
- {
- /* Cycle the generator */
- r = (Rand_value = LCRNG(Rand_value));
-
- /* Mutate a 28-bit "random" number */
- r = (r >> 4) % m;
- }
-
- /* Use the "complex" RNG */
- else
- {
- /* Acquire the next index */
- j = Rand_place + 1;
- if (j == RAND_DEG) j = 0;
-
- /* Update the table, extract an entry */
- r = (Rand_state[j] += Rand_state[Rand_place]);
-
- /* Advance the index */
- Rand_place = j;
-
- /* Extract a "random" number */
- r = (r >> 4) % m;
- }
-
- /* Use the value */
- return (r);
+ // Note that the "quick_rng" will always be seeded explicitly
+ // whenever it's used, so we don't need to do any seeding here.
+ static rng_t *instance = new rng_t();
+ return instance;
}
-
-/*
- * Extract a "random" number from 0 to m-1, via "division"
- *
- * This method selects "random" 28-bit numbers, and then uses
- * division to drop those numbers into "m" different partitions,
- * plus a small non-partition to reduce bias, taking as the final
- * value the first "good" partition that a number falls into.
- *
- * This method has no bias, and is much less affected by patterns
- * in the "low" bits of the underlying RNG's.
+/**
+ * The "complex" RNG is used when we really want non-deterministic
+ * random numbers.
*/
-static s32b Rand_div(s32b m)
+static rng_t *complex_rng()
{
- u32b r, n;
+ static rng_t *instance = new_seeded_rng(seed_t::system());
+ return instance;
+}
- /* Hack -- simple case */
- if (m <= 1) return (0);
- /* Partition size */
- n = (0x10000000 / m);
+/**
+ * Current RNG.
+ */
+static rng_t *current_rng = nullptr;
- /* Use a simple RNG */
- if (Rand_quick)
+/**
+ * Get the current RNG.
+ */
+static rng_t *get_current_rng()
+{
+ // Do we need to initialize?
+ if (current_rng == nullptr)
{
- /* Wait for it */
- while (1)
- {
- /* Cycle the generator */
- r = (Rand_value = LCRNG(Rand_value));
-
- /* Mutate a 28-bit "random" number */
- r = ((r >> 4) & 0x0FFFFFFF) / n;
-
- /* Done */
- if (r < (u32b)m) break;
- }
+ // We start with the complex RNG.
+ current_rng = complex_rng();
}
- /* Use a complex RNG */
- else
- {
- /* Wait for it */
- while (1)
- {
- int j;
-
- /* Acquire the next index */
- j = Rand_place + 1;
- if (j == RAND_DEG) j = 0;
-
- /* Update the table, extract an entry */
- r = (Rand_state[j] += Rand_state[Rand_place]);
-
- /* Hack -- extract a 28-bit "random" number */
- r = ((r >> 4) & 0x0FFFFFFF) / n;
-
- /* Advance the index */
- Rand_place = j;
+ return current_rng;
+}
- /* Done */
- if (r < (u32b)m) break;
- }
- }
+void set_quick_rng(seed_t const &seed)
+{
+ reseed_rng(quick_rng(), seed);
+ current_rng = quick_rng();
+}
- /* Use the value */
- return (r);
+void set_complex_rng()
+{
+ current_rng = complex_rng();
}
+std::string get_complex_rng_state()
+{
+ std::stringstream s;
+ s << *complex_rng();
+ return s.str();
+}
+void set_complex_rng_state(std::string const &state)
+{
+ std::stringstream s(state);
+ s >> *complex_rng();
+}
/*
@@ -357,20 +256,41 @@ bool magik(s32b p) {
s32b rand_int(s32b m)
{
- return Rand_div(m);
+ /* Degenerate case */
+ if (m < 1)
+ {
+ return 0;
+ }
+ /* Normal case */
+ std::uniform_int_distribution<s32b> distribution(0, m - 1);
+ return distribution(*get_current_rng());
}
s32b randint(s32b m)
{
- return rand_int(m) + 1;
+ /* Degenerate case */
+ if (m < 2)
+ {
+ return 1;
+ }
+ /* Normal case */
+ std::uniform_int_distribution<s32b> distribution(1, m);
+ return distribution(*get_current_rng());
}
s32b rand_range(s32b a, s32b b)
{
- return a + rand_int(1 + b - a);
+ /* Degenerate case */
+ if (b < a)
+ {
+ return a;
+ }
+ /* Normal case */
+ std::uniform_int_distribution<s32b> distribution(a, b);
+ return distribution(*get_current_rng());
}
s32b rand_spread(s32b a, s32b d)
{
- return a + rand_int(1 + d + d) - d;
+ return rand_range(a-d, a+d);
}
diff --git a/src/z-rand.hpp b/src/z-rand.hpp
index cac28167..235b9503 100644
--- a/src/z-rand.hpp
+++ b/src/z-rand.hpp
@@ -1,8 +1,9 @@
#pragma once
#include "h-basic.h"
+#include "seed_fwd.hpp"
-
+#include <string>
/**** Available constants ****/
@@ -19,17 +20,34 @@
/**** Available Variables ****/
-extern bool_ Rand_quick;
-extern u32b Rand_value;
-extern u16b Rand_place;
-extern u32b Rand_state[RAND_DEG];
+/**
+ * Change to "quick" RNG, using the given seed.
+ */
+void set_quick_rng(seed_t const &seed);
+
+
+/**
+ * Change to "complex" RNG which uses the "non-deterministic" seed.
+ */
+void set_complex_rng();
+
+
+/**
+ * Get a copy of the state of the "complex" RNG.
+ */
+std::string get_complex_rng_state();
+/**
+ * Set the state of the "complex" RNG. The given array must have
+ * been previously obtained via the get_complex_rng_state() function.
+ */
+void set_complex_rng_state(std::string const &state);
+
/**** Available Functions ****/
-void Rand_state_init(u32b seed);
-s32b Rand_mod(s32b m);
+void Rand_state_init();
s16b randnor(int mean, int stand);
s32b damroll(s16b num, s16b sides);
s32b maxroll(s16b num, s16b sides);