Merge pull request #322 from cbeck88/boost_rng_squash
use boost mersenne twister rng, add boost::random dependency
This commit is contained in:
commit
55078504a9
29 changed files with 812 additions and 57 deletions
|
@ -32,7 +32,7 @@ before_install:
|
|||
install:
|
||||
- sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ saucy main universe"
|
||||
- time sudo apt-get update -qq
|
||||
- time sudo apt-get install -qq libboost-filesystem-dev libboost-iostreams-dev libboost-program-options-dev libboost-regex-dev libboost-system-dev libboost-test-dev libboost-locale-dev libcairo2-dev libfribidi-dev libpango1.0-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-ttf2.0-dev gdb
|
||||
- time sudo apt-get install -qq libboost-filesystem-dev libboost-iostreams-dev libboost-random-dev libboost-program-options-dev libboost-regex-dev libboost-system-dev libboost-test-dev libboost-locale-dev libcairo2-dev libfribidi-dev libpango1.0-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-ttf2.0-dev gdb
|
||||
- if [ "$CXX" = "g++" ]; then time sudo apt-get -qq install g++-4.8; fi
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi
|
||||
- if [ "$CHECK_UTF8" = true ]; then time sudo apt-get install -qq moreutils; fi
|
||||
|
|
|
@ -22,6 +22,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
|||
|
||||
find_package(SDL 1.2.7 REQUIRED)
|
||||
find_package(Boost 1.36 REQUIRED COMPONENTS iostreams program_options regex system)
|
||||
find_package(Boost 1.40 REQUIRED COMPONENTS random)
|
||||
|
||||
# no, gettext executables are not required when NLS is deactivated
|
||||
find_package(Gettext)
|
||||
|
|
|
@ -376,6 +376,7 @@ if env["prereqs"]:
|
|||
conf.CheckBoost("iostreams", require_version = "1.34.1") & \
|
||||
conf.CheckBoostIostreamsGZip() & \
|
||||
conf.CheckBoostIostreamsBZip2() & \
|
||||
conf.CheckBoost("random",require_version = "1.40.0") & \
|
||||
conf.CheckBoost("smart_ptr", header_only = True) & \
|
||||
conf.CheckBoost("system") & \
|
||||
((not env["boostfilesystem"]) or
|
||||
|
|
|
@ -229,6 +229,8 @@ set(libwesnoth-core_STAT_SRC
|
|||
hash.cpp
|
||||
log.cpp
|
||||
md5.cpp
|
||||
mt_rng.cpp
|
||||
seed_rng.cpp
|
||||
thread.cpp
|
||||
tstring.cpp
|
||||
util.cpp
|
||||
|
@ -1380,8 +1382,9 @@ if(ENABLE_TESTS)
|
|||
tests/test_lexical_cast.cpp
|
||||
tests/test_map_location.cpp
|
||||
tests/test_make_enum.cpp
|
||||
tests/test_mp_connect.cpp
|
||||
tests/test_network_worker.cpp
|
||||
tests/test_rng.cpp
|
||||
tests/test_mp_connect.cpp
|
||||
tests/test_sdl_utils.cpp
|
||||
tests/test_serialization.cpp
|
||||
tests/test_team.cpp
|
||||
|
|
|
@ -21,6 +21,8 @@ libwesnoth_core_sources = Split("""
|
|||
map.cpp
|
||||
map_location.cpp
|
||||
md5.cpp
|
||||
mt_rng.cpp
|
||||
seed_rng.cpp
|
||||
network.cpp
|
||||
thread.cpp
|
||||
tstring.cpp
|
||||
|
@ -701,6 +703,7 @@ test_sources = Split("""
|
|||
tests/test_lexical_cast.cpp
|
||||
tests/test_map_location.cpp
|
||||
tests/test_make_enum.cpp
|
||||
tests/test_rng.cpp
|
||||
tests/test_mp_connect.cpp
|
||||
tests/test_network_worker.cpp
|
||||
tests/test_sdl_utils.cpp
|
||||
|
|
|
@ -890,8 +890,8 @@ namespace {
|
|||
int &abs_n = *(attacker_turn ? &abs_n_attack_ : &abs_n_defend_);
|
||||
bool &update_fog = *(attacker_turn ? &update_def_fog_ : &update_att_fog_);
|
||||
|
||||
int ran_num = random_new::generator->next_random();
|
||||
bool hits = (ran_num % 100) < attacker.cth_;
|
||||
int ran_num = random_new::generator->get_random_int(0,99);
|
||||
bool hits = (ran_num < attacker.cth_);
|
||||
|
||||
int damage = 0;
|
||||
if (hits) {
|
||||
|
@ -1421,7 +1421,7 @@ namespace
|
|||
//we are in the situation, that the unit is owned by a human, but he's not allowed to do this decision.
|
||||
//becasue it's a mp game and it's not his turn.
|
||||
//note that it doens't matter wether we call random_new::generator->next_random() or rand().
|
||||
res = random_new::generator->next_random() % nb_options_;
|
||||
res = random_new::generator->get_random_int(0, nb_options_-1);
|
||||
}
|
||||
LOG_NG << "unit at position " << loc_ << "choose advancement number " << res << "\n";
|
||||
config retv;
|
||||
|
|
|
@ -269,7 +269,7 @@ void carryover_info::transfer_to(config& level){
|
|||
|
||||
config::attribute_value & seed_value = level["random_seed"];
|
||||
if ( seed_value.empty() ) {
|
||||
seed_value = rng_.get_random_seed();
|
||||
seed_value = rng_.get_random_seed_str();
|
||||
level["random_calls"] = rng_.get_random_calls();
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ const config carryover_info::to_config()
|
|||
config& end_level = cfg.add_child("end_level_data");
|
||||
end_level_.write(end_level);
|
||||
|
||||
cfg["random_seed"] = rng_.get_random_seed();
|
||||
cfg["random_seed"] = rng_.get_random_seed_str();
|
||||
cfg["random_calls"] = rng_.get_random_calls();
|
||||
|
||||
cfg.add_child("variables", variables_);
|
||||
|
|
|
@ -11,7 +11,7 @@ class game_data;
|
|||
|
||||
#include "config.hpp"
|
||||
#include "game_end_exceptions.hpp"
|
||||
#include "simple_rng.hpp"
|
||||
#include "mt_rng.hpp"
|
||||
#include "game_events/wmi_container.hpp"
|
||||
|
||||
class carryover{
|
||||
|
@ -87,8 +87,8 @@ public:
|
|||
|
||||
game_events::wmi_container& get_wml_menu_items() { return wml_menu_items_; }
|
||||
|
||||
const rand_rng::simple_rng& rng() const { return rng_; }
|
||||
rand_rng::simple_rng& rng() { return rng_; }
|
||||
const rand_rng::mt_rng& rng() const { return rng_; }
|
||||
rand_rng::mt_rng& rng() { return rng_; }
|
||||
|
||||
const end_level_data& get_end_level() const;
|
||||
|
||||
|
@ -101,7 +101,7 @@ private:
|
|||
std::vector<carryover> carryover_sides_;
|
||||
end_level_data end_level_;
|
||||
config variables_;
|
||||
rand_rng::simple_rng rng_;
|
||||
rand_rng::mt_rng rng_;
|
||||
game_events::wmi_container wml_menu_items_;
|
||||
std::string next_scenario_; /**< the scenario coming next (for campaigns) */
|
||||
int next_underlying_unit_id_;
|
||||
|
|
|
@ -179,7 +179,7 @@ void game_data::write_snapshot(config& cfg) const {
|
|||
|
||||
cfg["can_end_turn"] = can_end_turn_;
|
||||
|
||||
cfg["random_seed"] = rng_.get_random_seed();
|
||||
cfg["random_seed"] = rng_.get_random_seed_str();
|
||||
cfg["random_calls"] = rng_.get_random_calls();
|
||||
|
||||
cfg.add_child("variables", variables_);
|
||||
|
@ -191,7 +191,7 @@ void game_data::write_config(config_writer& out){
|
|||
out.write_key_val("scenario", scenario_);
|
||||
out.write_key_val("next_scenario", next_scenario_);
|
||||
|
||||
out.write_key_val("random_seed", lexical_cast<std::string>(rng_.get_random_seed()));
|
||||
out.write_key_val("random_seed", rng_.get_random_seed_str());
|
||||
out.write_key_val("random_calls", lexical_cast<std::string>(rng_.get_random_calls()));
|
||||
out.write_child("variables", variables_);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "game_end_exceptions.hpp"
|
||||
#include "game_events/wmi_container.hpp"
|
||||
#include "map_location.hpp"
|
||||
#include "simple_rng.hpp"
|
||||
#include "mt_rng.hpp"
|
||||
#include "variable_info.hpp"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
|
@ -81,8 +81,8 @@ public:
|
|||
|
||||
game_events::wmi_container& get_wml_menu_items() { return wml_menu_items_; }
|
||||
|
||||
const rand_rng::simple_rng& rng() const { return rng_; }
|
||||
rand_rng::simple_rng& rng() { return rng_; }
|
||||
const rand_rng::mt_rng& rng() const { return rng_; }
|
||||
rand_rng::mt_rng& rng() { return rng_; }
|
||||
|
||||
enum PHASE {
|
||||
INITIAL,
|
||||
|
@ -130,7 +130,7 @@ private:
|
|||
}
|
||||
|
||||
game_events::wmi_container wml_menu_items_;
|
||||
rand_rng::simple_rng rng_;
|
||||
rand_rng::mt_rng rng_;
|
||||
config variables_;
|
||||
PHASE phase_;
|
||||
bool can_end_turn_;
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
||||
|
||||
static lg::log_domain log_engine("engine");
|
||||
#define DBG_NG LOG_STREAM(debug, log_engine)
|
||||
#define LOG_NG LOG_STREAM(info, log_engine)
|
||||
|
@ -2012,13 +2013,13 @@ WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg)
|
|||
<< 0x3fffffff
|
||||
<< ".\n";
|
||||
}
|
||||
long choice = random_new::generator->next_random();// gameinfo->rng().get_next_random();
|
||||
uint32_t choice = random_new::generator->next_random();// gameinfo->rng().get_next_random();
|
||||
if(num_choices >= 32768) {
|
||||
choice <<= 15;
|
||||
choice += random_new::generator->next_random();//gameinfo->rng().get_next_random();
|
||||
}
|
||||
choice %= num_choices;
|
||||
long tmp = 0;
|
||||
uint32_t tmp = 0;
|
||||
for(size_t i = 0; i < ranges.size(); ++i) {
|
||||
tmp += (ranges[i].second - ranges[i].first) + 1;
|
||||
if (tmp > choice) {
|
||||
|
|
100
src/mt_rng.cpp
Normal file
100
src/mt_rng.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "mt_rng.hpp"
|
||||
#include "seed_rng.hpp"
|
||||
#include "config.hpp"
|
||||
#include "log.hpp"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
static lg::log_domain log_random("random");
|
||||
#define DBG_RND LOG_STREAM(debug, log_random)
|
||||
#define LOG_RND LOG_STREAM(info, log_random)
|
||||
#define WRN_RND LOG_STREAM(warn, log_random)
|
||||
#define ERR_RND LOG_STREAM(err, log_random)
|
||||
|
||||
|
||||
namespace rand_rng
|
||||
{
|
||||
|
||||
mt_rng::mt_rng() :
|
||||
random_seed_(seed_rng::next_seed()),
|
||||
mt_(random_seed_),
|
||||
random_calls_(0)
|
||||
{
|
||||
}
|
||||
|
||||
mt_rng::mt_rng(const config& cfg) :
|
||||
random_seed_(42),
|
||||
mt_(random_seed_), //we don't have the seed at construction time, we have to seed after construction in this case. Constructing an mt19937 is somewhat expensive, apparently has about 2kb of private memory.
|
||||
random_calls_(0)
|
||||
{
|
||||
config::attribute_value seed = cfg["random_seed"];
|
||||
seed_random(seed.str(), cfg["random_calls"].to_int(0));
|
||||
}
|
||||
|
||||
bool mt_rng::operator== (const mt_rng & other) const {
|
||||
return random_seed_ == other.random_seed_
|
||||
&& random_calls_ == other.random_calls_
|
||||
&& mt_ == other.mt_;
|
||||
}
|
||||
|
||||
uint32_t mt_rng::get_next_random()
|
||||
{
|
||||
uint32_t result = mt_();
|
||||
++random_calls_;
|
||||
DBG_RND << "pulled user random " << result
|
||||
<< " for call " << random_calls_
|
||||
<< " with seed " << std::hex << random_seed_ << '\n';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void mt_rng::rotate_random()
|
||||
{
|
||||
seed_random(mt_(),0);
|
||||
}
|
||||
|
||||
void mt_rng::seed_random(const uint32_t seed, const unsigned int call_count)
|
||||
{
|
||||
random_seed_ = seed;
|
||||
mt_.seed(random_seed_);
|
||||
discard(call_count); //mt_.discard(call_count);
|
||||
DBG_RND << "Seeded random with " << std::hex << random_seed_ << " with "
|
||||
<< random_calls_ << " calls." << std::endl;
|
||||
}
|
||||
|
||||
void mt_rng::seed_random(const std::string & seed_str, const unsigned int call_count)
|
||||
{
|
||||
uint32_t new_seed;
|
||||
std::istringstream s(seed_str);
|
||||
s >> std::hex >> new_seed;
|
||||
seed_random(new_seed, call_count);
|
||||
}
|
||||
|
||||
std::string mt_rng::get_random_seed_str() const {
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << random_seed_;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
void mt_rng::discard(const unsigned int call_count)
|
||||
{
|
||||
for(unsigned int i = 0; i < call_count; ++i) {
|
||||
get_next_random();
|
||||
}
|
||||
}
|
||||
|
||||
} // ends rand_rng namespace
|
||||
|
96
src/mt_rng.hpp
Normal file
96
src/mt_rng.hpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef MT_RNG_HPP_INCLUDED
|
||||
#define MT_RNG_HPP_INCLUDED
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/random/mersenne_twister.hpp>
|
||||
|
||||
using boost::uint32_t;
|
||||
|
||||
class config;
|
||||
|
||||
namespace rand_rng
|
||||
{
|
||||
|
||||
/*
|
||||
This class provides an interface, similar to simple_rng, to the
|
||||
boost mt19937 generator.
|
||||
*/
|
||||
|
||||
class mt_rng
|
||||
{
|
||||
public:
|
||||
mt_rng();
|
||||
mt_rng(const config& cfg);
|
||||
|
||||
/** Get a new random number. */
|
||||
uint32_t get_next_random();
|
||||
|
||||
/**
|
||||
* Same as uint32_t version, but uses a stringstream to convert given
|
||||
* hex string.
|
||||
* @param seed A hex string. Should not have 0x leading.
|
||||
* @param call_count Value to set internal call counter to after seeding.
|
||||
*/
|
||||
void seed_random(const std::string & seed, const unsigned int call_count = 0);
|
||||
|
||||
/**
|
||||
* Resets the random to the 0 calls and the seed to the random
|
||||
* this way we stay in the same sequence but don't have a lot
|
||||
* calls. Used when moving to the next scenario.
|
||||
*/
|
||||
void rotate_random();
|
||||
|
||||
uint32_t get_random_seed() const { return random_seed_; }
|
||||
std::string get_random_seed_str() const;
|
||||
unsigned int get_random_calls() const { return random_calls_; }
|
||||
|
||||
//Comparisons, mainly used for testing
|
||||
bool operator== (const mt_rng &other) const;
|
||||
bool operator!= (const mt_rng &other) const
|
||||
{ return !operator==(other); }
|
||||
|
||||
private:
|
||||
/** Initial seed for the pool. */
|
||||
uint32_t random_seed_;
|
||||
|
||||
/** State for the random pool (boost mersenne twister random generator). */
|
||||
boost::mt19937 mt_;
|
||||
|
||||
/** Number of time a random number is generated. */
|
||||
unsigned int random_calls_;
|
||||
|
||||
/** On my local version of boost::random, I can use mt_.discard to discard a number of rng results.
|
||||
In older versions this seems to be unavailable. I'm implementing as a private method of mt_rng,
|
||||
following description here: http://www.boost.org/doc/libs/1_51_0/doc/html/boost/random/mersenne_twister_engine.html#id1408119-bb
|
||||
*/
|
||||
void discard(const unsigned int call_count);
|
||||
|
||||
/**
|
||||
* Seeds the random pool. This is the old version, I would like to mark this private.
|
||||
*
|
||||
* @param seed The initial value for the random engine.
|
||||
* @param call_count Upon loading we need to restore the state at saving
|
||||
* so set the number of times a random number is
|
||||
* generated for replays the orginal value is
|
||||
* required.
|
||||
*/
|
||||
void seed_random(const uint32_t seed, const unsigned int call_count = 0);
|
||||
};
|
||||
|
||||
} // ends rand_rng namespace
|
||||
|
||||
#endif
|
|
@ -16,8 +16,9 @@
|
|||
#include "log.hpp"
|
||||
|
||||
|
||||
|
||||
#include <cassert>
|
||||
#include <stdlib.h>
|
||||
|
||||
static lg::log_domain log_random("random");
|
||||
#define DBG_RND LOG_STREAM(debug, log_random)
|
||||
#define LOG_RND LOG_STREAM(info, log_random)
|
||||
|
@ -44,19 +45,39 @@ namespace random_new
|
|||
return random_calls_;
|
||||
}
|
||||
|
||||
int rng::next_random()
|
||||
uint32_t rng::next_random()
|
||||
{
|
||||
random_calls_++;
|
||||
return next_random_impl();
|
||||
}
|
||||
|
||||
int rng::next_random_impl()
|
||||
/**
|
||||
* This code is based on the boost implementation of uniform_smallint.
|
||||
* http://www.boost.org/doc/libs/1_55_0/boost/random/uniform_smallint.hpp
|
||||
* Using that code would be ideal, except that boost, and C++11, do not
|
||||
* guarantee that it will work the same way on all platforms, or that the
|
||||
* results may not be different in future versions of the library.
|
||||
* The simplified version I have written should work the same on all
|
||||
* platforms, which is the most important thing for us.
|
||||
* The existence of "modulo bias" seems less important when we have moved
|
||||
* to boost::mt19937, since it guarantees that there are no "bad bits"
|
||||
* and has a very large range.
|
||||
*
|
||||
* If a standard cross platform version becomes available then this should
|
||||
* be replaced.
|
||||
*/
|
||||
int rng::get_random_int_in_range_zero_to(int max)
|
||||
{
|
||||
assert(max >= 0);
|
||||
return static_cast<int> (next_random() % (static_cast<uint32_t>(max)+1));
|
||||
}
|
||||
|
||||
uint32_t rng::next_random_impl()
|
||||
{
|
||||
//getting here means random was called form outsiude a synced context.
|
||||
int retv = rand();
|
||||
|
||||
uint32_t retv = rand();
|
||||
|
||||
LOG_RND << "random_new::rng::next_random returned " << retv;
|
||||
return retv;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#ifndef RANDOM_NEW_H_INCLUDED
|
||||
#define RANDOM_NEW_H_INCLUDED
|
||||
|
||||
#include <cstdlib> //needed for RAND_MAX
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
using boost::uint32_t;
|
||||
|
||||
namespace random_new
|
||||
{
|
||||
/**
|
||||
|
@ -23,12 +28,38 @@ namespace random_new
|
|||
{
|
||||
public:
|
||||
rng();
|
||||
int next_random();
|
||||
/**
|
||||
* Provides the next random draw. This is raw PRG output.
|
||||
*/
|
||||
uint32_t next_random();
|
||||
virtual ~rng();
|
||||
/**
|
||||
* Provides the number of random calls to the rng in this context.
|
||||
* Note that this may be different from the number of random calls to
|
||||
* the underlying rng, and to the random_calls number in save files!
|
||||
*/
|
||||
unsigned int get_random_calls();
|
||||
|
||||
/**
|
||||
* This helper method provides a random int from the underlying generator,
|
||||
* using results of next_random in a manner guaranteed to be cross platform.
|
||||
* The result will be random in range [min,max] inclusive.
|
||||
* @param min The minimum value produced.
|
||||
* @param max The maximum value produced.
|
||||
*/
|
||||
int get_random_int(int min, int max)
|
||||
{ return min + get_random_int_in_range_zero_to(max - min); }
|
||||
|
||||
protected:
|
||||
virtual int next_random_impl();
|
||||
virtual uint32_t next_random_impl();
|
||||
unsigned int random_calls_;
|
||||
|
||||
private:
|
||||
/** Does the hard work of get_random_int.
|
||||
* The result will be random in range [0,max] inclusive.
|
||||
* @param max The maximum value produced.
|
||||
*/
|
||||
int get_random_int_in_range_zero_to(int max);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
namespace random_new
|
||||
{
|
||||
|
||||
rng_deterministic::rng_deterministic(rand_rng::simple_rng& gen)
|
||||
rng_deterministic::rng_deterministic(rand_rng::mt_rng& gen)
|
||||
: generator_(gen)
|
||||
{
|
||||
|
||||
|
@ -28,13 +28,13 @@ namespace random_new
|
|||
|
||||
}
|
||||
|
||||
int rng_deterministic::next_random_impl()
|
||||
uint32_t rng_deterministic::next_random_impl()
|
||||
{
|
||||
return generator_.get_next_random();
|
||||
}
|
||||
|
||||
|
||||
set_random_determinstic::set_random_determinstic(rand_rng::simple_rng& rng)
|
||||
set_random_determinstic::set_random_determinstic(rand_rng::mt_rng& rng)
|
||||
: old_rng_(generator), new_rng_(rng)
|
||||
{
|
||||
generator = &new_rng_;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#ifndef RANDOM_NEW_DETERMINISTIC_H_INCLUDED
|
||||
#define RANDOM_NEW_DETERMINISTIC_H_INCLUDED
|
||||
#include "random_new.hpp"
|
||||
#include "simple_rng.hpp"
|
||||
#include "mt_rng.hpp"
|
||||
|
||||
namespace random_new
|
||||
{
|
||||
|
@ -27,12 +27,13 @@ namespace random_new
|
|||
class rng_deterministic : public random_new::rng
|
||||
{
|
||||
public:
|
||||
rng_deterministic(rand_rng::simple_rng& gen);
|
||||
rng_deterministic(rand_rng::mt_rng& gen);
|
||||
virtual ~rng_deterministic();
|
||||
|
||||
protected:
|
||||
virtual int next_random_impl();
|
||||
virtual uint32_t next_random_impl();
|
||||
private:
|
||||
rand_rng::simple_rng& generator_;
|
||||
rand_rng::mt_rng& generator_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -41,7 +42,7 @@ namespace random_new
|
|||
class set_random_determinstic
|
||||
{
|
||||
public:
|
||||
set_random_determinstic(rand_rng::simple_rng& rng);
|
||||
set_random_determinstic(rand_rng::mt_rng& rng);
|
||||
~set_random_determinstic();
|
||||
private :
|
||||
rng* old_rng_;
|
||||
|
|
|
@ -22,18 +22,18 @@ static lg::log_domain log_random("random");
|
|||
|
||||
namespace random_new
|
||||
{
|
||||
synced_rng::synced_rng(boost::function0<int> seed_generator)
|
||||
synced_rng::synced_rng(boost::function0<std::string> seed_generator)
|
||||
: has_valid_seed_(false), seed_generator_(seed_generator), gen_()
|
||||
{
|
||||
}
|
||||
int synced_rng::next_random_impl()
|
||||
uint32_t synced_rng::next_random_impl()
|
||||
{
|
||||
if(!has_valid_seed_)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
//getting here means random was called form inside a synced context.
|
||||
int retv = gen_.get_next_random();
|
||||
uint32_t retv = gen_.get_next_random();
|
||||
|
||||
LOG_RND << "random_new::rng::next_random_impl returned " << retv;
|
||||
return retv;
|
||||
|
@ -41,7 +41,7 @@ namespace random_new
|
|||
|
||||
void synced_rng::initialize()
|
||||
{
|
||||
int new_seed = seed_generator_();
|
||||
std::string new_seed = seed_generator_();
|
||||
gen_.seed_random(new_seed, 0);
|
||||
has_valid_seed_ = true;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
|
||||
#include "random_new.hpp"
|
||||
#include "simple_rng.hpp"
|
||||
#include "mt_rng.hpp"
|
||||
|
||||
#include "utils/boost_function_guarded.hpp"
|
||||
|
||||
|
@ -31,16 +31,16 @@ namespace random_new
|
|||
class synced_rng : public random_new::rng
|
||||
{
|
||||
public:
|
||||
synced_rng(boost::function0<int> seed_generator);
|
||||
synced_rng(boost::function0<std::string> seed_generator);
|
||||
virtual ~synced_rng();
|
||||
|
||||
protected:
|
||||
virtual int next_random_impl();
|
||||
virtual uint32_t next_random_impl();
|
||||
private:
|
||||
void initialize();
|
||||
bool has_valid_seed_;
|
||||
boost::function0<int> seed_generator_;
|
||||
//TODO: replayce this by boost::random::mt19937 or similar
|
||||
rand_rng::simple_rng gen_;
|
||||
boost::function0<std::string> seed_generator_;
|
||||
rand_rng::mt_rng gen_;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -334,7 +334,8 @@ void replay_controller::reset_replay()
|
|||
|
||||
gui_->labels().read(level_);
|
||||
|
||||
resources::gamedata->rng().seed_random(level_["random_seed"], level_["random_calls"]);
|
||||
config::attribute_value random_seed = level_["random_seed"];
|
||||
resources::gamedata->rng().seed_random(random_seed.str(), level_["random_calls"]);
|
||||
statistics::fresh_stats();
|
||||
set_victory_when_enemies_defeated(level_["victory_when_enemies_defeated"].to_bool(true));
|
||||
|
||||
|
|
70
src/seed_rng.cpp
Normal file
70
src/seed_rng.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/* This file selects a seed source -- "nondeterministic" random number
|
||||
generator in boost documentation. It should be a wrapper for
|
||||
boost::random_device on platforms where this is available, otherwise
|
||||
it should most likely be the system time.
|
||||
*/
|
||||
|
||||
#include "seed_rng.hpp"
|
||||
|
||||
/*****
|
||||
Use preprocessor tests to decide whether to try to include and
|
||||
use boost random device, or fallback to system time.
|
||||
*****/
|
||||
|
||||
#define SEED_RNG_USE_BOOST_RANDOM_DEVICE
|
||||
|
||||
//Boost does not support random device on windows before v 1.43.0
|
||||
//http://www.boost.org/users/history/version_1_43_0.html
|
||||
#if (defined(_WIN32) && (BOOST_VERSION < 104300))
|
||||
#undef SEED_RNG_USE_BOOST_RANDOM_DEVICE
|
||||
#endif
|
||||
|
||||
/*****
|
||||
End preprocessor checks
|
||||
*****/
|
||||
|
||||
#ifdef SEED_RNG_USE_BOOST_RANDOM_DEVICE
|
||||
#include <boost/nondet_random.hpp>
|
||||
#else
|
||||
#include <ctime>
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace seed_rng {
|
||||
|
||||
#ifdef SEED_RNG_USE_BOOST_RANDOM_DEVICE
|
||||
uint32_t next_seed() {
|
||||
static boost::random_device rnd_;
|
||||
return rnd_();
|
||||
}
|
||||
#else
|
||||
uint32_t next_seed() {
|
||||
return static_cast<uint32_t> (std::time(0));
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string next_seed_str() {
|
||||
uint32_t random_seed_ = next_seed();
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << random_seed_;
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
} //ends seed_rng namespace
|
38
src/seed_rng.hpp
Normal file
38
src/seed_rng.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file provides a name space to store a source for seeds for
|
||||
prgs. It should be boost::random_device on platforms that provide
|
||||
this with our version of boost random, and otherwise should be the
|
||||
system time I suppose.
|
||||
|
||||
The seed_rng::next_seed function provided probably shouldn't be used
|
||||
anywhere except for default constructors of prg classes, or similar.
|
||||
*/
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <string>
|
||||
|
||||
#ifndef SEED_RNG_HPP_INCLUDED
|
||||
#define SEED_RNG_HPP_INCLUDED
|
||||
|
||||
namespace seed_rng {
|
||||
|
||||
uint32_t next_seed();
|
||||
std::string next_seed_str();
|
||||
|
||||
} // ends seed_rng namespace
|
||||
|
||||
#endif
|
|
@ -24,6 +24,8 @@
|
|||
#include "serialization/string_utils.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
static lg::log_domain log_server("server");
|
||||
|
@ -67,7 +69,8 @@ game::game(player_map& players, const network::connection host,
|
|||
termination_(),
|
||||
save_replays_(save_replays),
|
||||
replay_save_path_(replay_save_path),
|
||||
global_wait_side_(0)
|
||||
global_wait_side_(0),
|
||||
rng_()
|
||||
{
|
||||
assert(owner_);
|
||||
players_.push_back(owner_);
|
||||
|
@ -1019,13 +1022,17 @@ bool game::process_turn(simple_wml::document& data, const player_map::const_iter
|
|||
void game::require_random(const simple_wml::document &/*data*/, const player_map::iterator /*user*/)
|
||||
{
|
||||
// note, that during end turn events, it's side=1 for the server but side= side_count() on the clients.
|
||||
|
||||
uint32_t seed = rng_.get_next_random();
|
||||
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed;
|
||||
|
||||
int seed = rand() & 0x7FFFFFFF;
|
||||
simple_wml::document* mdata = new simple_wml::document;
|
||||
simple_wml::node& turn = mdata->root().add_child("turn");
|
||||
simple_wml::node& command = turn.add_child("command");
|
||||
simple_wml::node& random_seed = command.add_child("random_seed");
|
||||
random_seed.set_attr_int("new_seed",seed);
|
||||
random_seed.set_attr_dup("new_seed",stream.str().c_str());
|
||||
command.set_attr("from_side", "server");
|
||||
command.set_attr("dependent", "yes");
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "../mt_rng.hpp"
|
||||
|
||||
//class player;
|
||||
|
||||
namespace wesnothd {
|
||||
|
@ -392,6 +394,9 @@ private:
|
|||
|
||||
/** The side from which global variable data is expected*/
|
||||
int global_wait_side_;
|
||||
|
||||
/** A wrapper for mersenne twister rng which generates randomness for this game */
|
||||
rand_rng::mt_rng rng_;
|
||||
};
|
||||
|
||||
struct game_is_member {
|
||||
|
|
|
@ -33,12 +33,15 @@
|
|||
#include "play_controller.hpp"
|
||||
#include "actions/undo.hpp"
|
||||
#include "game_end_exceptions.hpp"
|
||||
#include "seed_rng.hpp"
|
||||
#include "syncmp_handler.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
static lg::log_domain log_replay("replay");
|
||||
#define DBG_REPLAY LOG_STREAM(debug, log_replay)
|
||||
#define LOG_REPLAY LOG_STREAM(info, log_replay)
|
||||
|
@ -150,11 +153,13 @@ void synced_context::set_synced_state(synced_state newstate)
|
|||
state_ = newstate;
|
||||
}
|
||||
|
||||
int synced_context::generate_random_seed()
|
||||
std::string synced_context::generate_random_seed()
|
||||
{
|
||||
random_seed_choice cho;
|
||||
config retv_c = synced_context::ask_server("random_seed", cho);
|
||||
return retv_c["new_seed"];
|
||||
config::attribute_value seed_val = retv_c["new_seed"];
|
||||
|
||||
return seed_val.str();
|
||||
}
|
||||
|
||||
bool synced_context::is_simultaneously()
|
||||
|
@ -475,7 +480,8 @@ config random_seed_choice::query_user(int /*side*/) const
|
|||
|
||||
|
||||
config retv;
|
||||
retv["new_seed"] = rand();
|
||||
|
||||
retv["new_seed"] = seed_rng::next_seed_str();
|
||||
return retv;
|
||||
}
|
||||
config random_seed_choice::random_choice(int /*side*/) const
|
||||
|
@ -485,6 +491,6 @@ config random_seed_choice::random_choice(int /*side*/) const
|
|||
assert(false && "random_seed_choice::random_choice called");
|
||||
|
||||
config retv;
|
||||
retv["new_seed"] = 0;
|
||||
retv["new_seed"] = "deadbeef";
|
||||
return retv;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
/*
|
||||
Generates a new seed for a synced event, by asking the 'server'
|
||||
*/
|
||||
static int generate_random_seed();
|
||||
static std::string generate_random_seed();
|
||||
/**
|
||||
called from get_user_choice while waiting for a remove user choice.
|
||||
*/
|
||||
|
|
|
@ -111,6 +111,46 @@ BOOST_AUTO_TEST_CASE( test_send_client )
|
|||
|
||||
}
|
||||
|
||||
void try_send_random_seed ( const std::string seed_str, const unsigned int random_calls)
|
||||
{
|
||||
config cfg_send;
|
||||
config& child = cfg_send.add_child("command");
|
||||
|
||||
child["random_seed"] = seed_str;
|
||||
child["random_calls"] = random_calls;
|
||||
|
||||
network::send_data(cfg_send, client_client1);
|
||||
|
||||
network::connection receive_from;
|
||||
config received;
|
||||
|
||||
receive_from = receive(received);
|
||||
|
||||
BOOST_CHECK_MESSAGE( receive_from == server_client1, "Received data is not from test client 1" );
|
||||
|
||||
BOOST_CHECK_EQUAL(cfg_send, received);
|
||||
|
||||
config rec_command = received.child("command");
|
||||
|
||||
std::string rec_seed_str = rec_command["random_seed"].str();
|
||||
unsigned int rec_calls = rec_command["random_calls"];
|
||||
|
||||
BOOST_CHECK_EQUAL(seed_str, rec_seed_str);
|
||||
BOOST_CHECK_EQUAL(random_calls, rec_calls);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_send_random_seed )
|
||||
{
|
||||
try_send_random_seed("0000badd",0);
|
||||
try_send_random_seed("00001234",1);
|
||||
try_send_random_seed("deadbeef",2);
|
||||
try_send_random_seed("12345678",3);
|
||||
try_send_random_seed("00009999",4);
|
||||
try_send_random_seed("ffffaaaa",5);
|
||||
try_send_random_seed("11110000",6);
|
||||
try_send_random_seed("10101010",7);
|
||||
try_send_random_seed("aaaa0000",8);
|
||||
}
|
||||
|
||||
class connect_aborter : public threading::waiter
|
||||
{
|
||||
|
|
331
src/tests/test_rng.cpp
Normal file
331
src/tests/test_rng.cpp
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-test"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "random_new_synced.hpp"
|
||||
#include "random_new_deterministic.hpp"
|
||||
#include "config.hpp"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( rng )
|
||||
|
||||
/* this test adapted from validation routine at
|
||||
http://www.boost.org/doc/libs/1_38_0/libs/random/random_test.cpp
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE( validate_mt19937 )
|
||||
{
|
||||
boost::mt19937 rng;
|
||||
for (int i = 0; i < 9999 ; i++) {
|
||||
rng();
|
||||
}
|
||||
unsigned long val = rng();
|
||||
BOOST_CHECK_EQUAL( val , 4123659995U );
|
||||
}
|
||||
|
||||
/* this test checks the soundness of mt_rng string manipulations */
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_seed_manip )
|
||||
{
|
||||
uint32_t seed = 42;
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed;
|
||||
|
||||
std::string seed_str = stream.str();
|
||||
|
||||
rand_rng::mt_rng rng;
|
||||
rng.seed_random(seed_str);
|
||||
|
||||
BOOST_CHECK (rng.get_random_seed() == seed);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == seed_str);
|
||||
|
||||
std::string seed_str2 = rng.get_random_seed_str();
|
||||
rng.seed_random(seed_str2);
|
||||
|
||||
BOOST_CHECK (rng.get_random_seed() == seed);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == seed_str);
|
||||
|
||||
|
||||
uint32_t seed3 = 1123581321; //try the same with a different number
|
||||
std::stringstream stream2;
|
||||
stream2 << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed3;
|
||||
std::string seed_str3 = stream2.str();
|
||||
|
||||
rng.seed_random(seed_str3);
|
||||
BOOST_CHECK (rng.get_random_seed() == seed3);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == seed_str3);
|
||||
|
||||
std::string seed_str4 = rng.get_random_seed_str();
|
||||
rng.seed_random(seed_str4);
|
||||
|
||||
BOOST_CHECK (rng.get_random_seed() == seed3);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == seed_str3);
|
||||
|
||||
|
||||
//now check that the results that shouldn't match don't
|
||||
BOOST_CHECK (seed != seed3);
|
||||
BOOST_CHECK (seed_str != seed_str3);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_config_seed_manip )
|
||||
{
|
||||
uint32_t seed = 42;
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed;
|
||||
std::string seed_str = stream.str();
|
||||
|
||||
config cfg;
|
||||
cfg["random_seed"] = seed_str;
|
||||
cfg["random_calls"] = 0;
|
||||
|
||||
rand_rng::mt_rng rng(cfg);
|
||||
|
||||
BOOST_CHECK (rng.get_random_seed() == seed);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == seed_str);
|
||||
|
||||
std::string seed_str2 = rng.get_random_seed_str();
|
||||
rng.seed_random(seed_str2);
|
||||
|
||||
BOOST_CHECK (rng.get_random_seed() == seed);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == seed_str);
|
||||
|
||||
|
||||
uint32_t seed3 = 1123581321; //try the same with a different number
|
||||
std::stringstream stream2;
|
||||
stream2 << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed3;
|
||||
std::string seed_str3 = stream2.str();
|
||||
|
||||
config cfg2;
|
||||
cfg2["random_seed"] = seed_str3;
|
||||
cfg2["random_calls"] = 0;
|
||||
|
||||
rand_rng::mt_rng rng2(cfg2);
|
||||
|
||||
BOOST_CHECK (rng2.get_random_seed() == seed3);
|
||||
BOOST_CHECK (rng2.get_random_seed_str() == seed_str3);
|
||||
|
||||
std::string seed_str4 = rng2.get_random_seed_str();
|
||||
rng2.seed_random(seed_str4);
|
||||
|
||||
BOOST_CHECK (rng2.get_random_seed() == seed3);
|
||||
BOOST_CHECK (rng2.get_random_seed_str() == seed_str3);
|
||||
|
||||
|
||||
//now check that the results that shouldn't match don't
|
||||
BOOST_CHECK (seed != seed3);
|
||||
BOOST_CHECK (seed_str != seed_str3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility )
|
||||
{
|
||||
config cfg;
|
||||
cfg["random_seed"] = "5eedf00d";
|
||||
cfg["random_calls"] = 0;
|
||||
|
||||
rand_rng::mt_rng rng1(cfg);
|
||||
rand_rng::mt_rng rng2(cfg);
|
||||
|
||||
BOOST_CHECK(rng1 == rng2);
|
||||
for (int i = 0; i < 10 ; i++) {
|
||||
BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility2 )
|
||||
{
|
||||
config cfg;
|
||||
cfg["random_seed"] = "18da5eed";
|
||||
cfg["random_calls"] = 9999;
|
||||
|
||||
rand_rng::mt_rng rng1(cfg);
|
||||
rand_rng::mt_rng rng2(cfg);
|
||||
|
||||
BOOST_CHECK(rng1 == rng2);
|
||||
for (int i = 0; i < 10 ; i++) {
|
||||
BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility3 )
|
||||
{
|
||||
rand_rng::mt_rng rng1;
|
||||
config cfg;
|
||||
cfg["random_seed"] = rng1.get_random_seed_str();
|
||||
cfg["random_calls"] = rng1.get_random_calls();
|
||||
|
||||
rand_rng::mt_rng rng2(cfg);
|
||||
|
||||
BOOST_CHECK(rng1 == rng2);
|
||||
for (int i = 0; i < 10 ; i++) {
|
||||
BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility4 )
|
||||
{
|
||||
rand_rng::mt_rng rng1;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
rng1.get_next_random();
|
||||
}
|
||||
|
||||
config cfg;
|
||||
cfg["random_seed"] = rng1.get_random_seed_str();
|
||||
cfg["random_calls"] = rng1.get_random_calls();
|
||||
|
||||
rand_rng::mt_rng rng2(cfg);
|
||||
|
||||
BOOST_CHECK(rng1 == rng2);
|
||||
BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility5 )
|
||||
{
|
||||
config cfg;
|
||||
cfg["random_seed"] = "5eedc0de";
|
||||
cfg["random_calls"] = 0;
|
||||
|
||||
rand_rng::mt_rng rng(cfg);
|
||||
|
||||
for (int i = 0; i < 9999 ; i++) {
|
||||
rng.get_next_random();
|
||||
}
|
||||
|
||||
config cfg2;
|
||||
cfg2["random_seed"] = rng.get_random_seed_str();
|
||||
cfg2["random_calls"] = rng.get_random_calls();
|
||||
|
||||
rand_rng::mt_rng rng2(cfg2);
|
||||
|
||||
uint32_t result1 = rng.get_next_random();
|
||||
uint32_t result2 = rng2.get_next_random();
|
||||
|
||||
BOOST_CHECK (rng == rng2);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == rng2.get_random_seed_str());
|
||||
BOOST_CHECK (rng.get_random_calls() == rng2.get_random_calls());
|
||||
BOOST_CHECK (result1 == result2);
|
||||
|
||||
config cfg_save;
|
||||
cfg_save["random_seed"] = rng.get_random_seed_str();
|
||||
cfg_save["random_calls"] = rng.get_random_calls();
|
||||
|
||||
uint32_t result3 = rng.get_next_random();
|
||||
|
||||
rand_rng::mt_rng rng3(cfg_save);
|
||||
uint32_t result4 = rng3.get_next_random();
|
||||
|
||||
BOOST_CHECK (rng == rng3);
|
||||
BOOST_CHECK (rng.get_random_seed_str() == rng3.get_random_seed_str());
|
||||
BOOST_CHECK (rng.get_random_calls() == rng3.get_random_calls());
|
||||
BOOST_CHECK (result3 == result4);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void validate_seed_string(std::string seed_str)
|
||||
{
|
||||
config cfg;
|
||||
cfg["random_seed"] = seed_str;
|
||||
cfg["random_calls"] = 0;
|
||||
|
||||
rand_rng::mt_rng rng1(cfg);
|
||||
|
||||
for (int i = 0; i < 9999 ; i++) {
|
||||
rng1.get_next_random();
|
||||
}
|
||||
|
||||
config cfg2;
|
||||
cfg2["random_seed"] = rng1.get_random_seed_str();
|
||||
cfg2["random_calls"] = rng1.get_random_calls();
|
||||
|
||||
rand_rng::mt_rng rng2(cfg2);
|
||||
|
||||
for (int i = 0; i < 9999 ; i++) {
|
||||
rng1.get_next_random();
|
||||
rng2.get_next_random();
|
||||
}
|
||||
|
||||
BOOST_CHECK(rng1 == rng2);
|
||||
BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility_coverage )
|
||||
{
|
||||
validate_seed_string("0000badd");
|
||||
validate_seed_string("00001234");
|
||||
validate_seed_string("deadbeef");
|
||||
validate_seed_string("12345678");
|
||||
validate_seed_string("00009999");
|
||||
validate_seed_string("ffffaaaa");
|
||||
validate_seed_string("11110000");
|
||||
validate_seed_string("10101010");
|
||||
validate_seed_string("aaaa0000");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string validate_get_random_int_seed_generator()
|
||||
{
|
||||
return "dada5eed";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define validation_get_random_int_num_draws 19999
|
||||
|
||||
#define validation_get_random_int_max 32000
|
||||
|
||||
#define validation_get_random_int_correct_answer 10885
|
||||
|
||||
/**
|
||||
* This test and the next validate that we are getting the correct values
|
||||
* from the get_random_int function, in the class random_new.
|
||||
* We test both subclasses of random_new.
|
||||
* If these tests fail but the seed manipulation tests all pass,
|
||||
* and validate_mt19937 passes, then it suggests that the implementation
|
||||
* of get_random_int may not be working properly on your platform.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE( validate_get_random_int )
|
||||
{
|
||||
config cfg;
|
||||
cfg["random_seed"] = validate_get_random_int_seed_generator();
|
||||
cfg["random_calls"] = validation_get_random_int_num_draws;
|
||||
|
||||
rand_rng::mt_rng mt_(cfg);
|
||||
|
||||
boost::shared_ptr<random_new::rng> gen_ (new random_new::rng_deterministic(mt_));
|
||||
|
||||
int val = gen_->get_random_int(0, validation_get_random_int_max);
|
||||
BOOST_CHECK_EQUAL ( val , validation_get_random_int_correct_answer );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( validate_get_random_int2 )
|
||||
{
|
||||
boost::shared_ptr<random_new::rng> gen_ (new random_new::synced_rng(validate_get_random_int_seed_generator));
|
||||
|
||||
for (int i = 0; i < validation_get_random_int_num_draws; i++) {
|
||||
gen_->next_random();
|
||||
}
|
||||
|
||||
int val = gen_->get_random_int(0,validation_get_random_int_max);
|
||||
BOOST_CHECK_EQUAL ( val , validation_get_random_int_correct_answer );
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
|
@ -148,8 +148,7 @@ static unit_race::GENDER generate_gender(const unit_type & type, bool random_gen
|
|||
if ( random_gender == false || genders.size() == 1 ) {
|
||||
return genders.front();
|
||||
} else {
|
||||
int random = random_new::generator->next_random();
|
||||
return genders[random % genders.size()];
|
||||
return genders[random_new::generator->get_random_int(0,genders.size()-1)];
|
||||
// Note: genders is guaranteed to be non-empty, so this is not a
|
||||
// potential division by zero.
|
||||
// Note: Whoever wrote this code, you should have used an assertion, to save others hours of work...
|
||||
|
@ -758,7 +757,7 @@ void unit::generate_traits(bool musthaveonly)
|
|||
int max_traits = u_type.num_traits();
|
||||
for (; nb_traits < max_traits && !candidate_traits.empty(); ++nb_traits)
|
||||
{
|
||||
int num = random_new::generator->next_random() % candidate_traits.size();
|
||||
int num = random_new::generator->get_random_int(0,candidate_traits.size()-1);
|
||||
modifications_.add_child("trait", candidate_traits[num]);
|
||||
candidate_traits.erase(candidate_traits.begin() + num);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue