diff options
author | Bardur Arantsson <bardur@scientician.net> | 2016-09-17 09:58:14 +0200 |
---|---|---|
committer | Bardur Arantsson <bardur@scientician.net> | 2016-09-17 09:58:14 +0200 |
commit | 6d11bb4a2d5bc8ab7c1491639f1083532b1b8fd1 (patch) | |
tree | 0a1ee125b7c5ef7ec37ff0781392989be4f75e26 /src | |
parent | 36969a9058806e71078efcb04a91cc263612d42e (diff) |
Replace RNG with PCG random number generator
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/birth.cc | 4 | ||||
-rw-r--r-- | src/dungeon.cc | 27 | ||||
-rw-r--r-- | src/generate.cc | 3 | ||||
-rw-r--r-- | src/loadsave.cc | 78 | ||||
-rw-r--r-- | src/object1.cc | 9 | ||||
-rw-r--r-- | src/seed.cc | 53 | ||||
-rw-r--r-- | src/seed.hpp | 51 | ||||
-rw-r--r-- | src/seed_fwd.hpp | 3 | ||||
-rw-r--r-- | src/spells2.cc | 12 | ||||
-rw-r--r-- | src/town_type.hpp | 3 | ||||
-rw-r--r-- | src/variable.cc | 6 | ||||
-rw-r--r-- | src/variable.hpp | 3 | ||||
-rw-r--r-- | src/wild.cc | 17 | ||||
-rw-r--r-- | src/wilderness_map.hpp | 3 | ||||
-rw-r--r-- | src/z-rand.cc | 294 | ||||
-rw-r--r-- | src/z-rand.hpp | 32 |
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); |