From 8312cd1b5f8255d0b3fe0cdd53b795317e26b5e6 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Wed, 11 Mar 2015 21:33:25 -0400 Subject: [PATCH] use the mersenne twister rng for random faction, side shuffling This is because players were able to notice problems when the rand rng was used for random map pools, so it cannot hurt to use the good rng for these things also. --- src/game_initialization/connect_engine.cpp | 18 +++++++++++------- src/game_initialization/connect_engine.hpp | 4 +++- src/game_initialization/flg_manager.cpp | 9 +++++---- src/game_initialization/flg_manager.hpp | 3 ++- src/tests/test_mp_connect.cpp | 12 ++++++++---- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/game_initialization/connect_engine.cpp b/src/game_initialization/connect_engine.cpp index 70ff98a4baff..d1437fdd378a 100644 --- a/src/game_initialization/connect_engine.cpp +++ b/src/game_initialization/connect_engine.cpp @@ -21,6 +21,7 @@ #include "map.hpp" #include "multiplayer_ui.hpp" #include "mp_game_utils.hpp" +#include "mt_rng.hpp" #include "tod_manager.hpp" #include @@ -391,8 +392,9 @@ void connect_engine::start_game(LOAD_USERS load_users) { DBG_MP << "starting a new game" << std::endl; - // Resolves the "random faction", "random gender" and "random message" - // Must be done before shuffle sides, or some cases will cause errors + // Resolves the "random faction", "random gender" and "random message" + // Must be done before shuffle sides, or some cases will cause errors + rand_rng::mt_rng rng; // Make an RNG for all the shuffling and random faction operations BOOST_FOREACH(side_engine_ptr side, side_engines_) { std::vector avoid_faction_ids; @@ -415,7 +417,7 @@ void connect_engine::start_game(LOAD_USERS load_users) } } } - side->resolve_random(avoid_faction_ids); + side->resolve_random(rng, avoid_faction_ids); } // Shuffle sides (check settings and if it is a re-loaded game). @@ -434,7 +436,7 @@ void connect_engine::start_game(LOAD_USERS load_users) // Fisher-Yates shuffle. for (int i = playable_sides.size(); i > 1; i--) { - int j_side = playable_sides[rand() % i]; + int j_side = playable_sides[rng.get_next_random() % i]; int i_side = playable_sides[i - 1]; if (i_side == j_side) continue; //nothing to swap @@ -485,6 +487,8 @@ void connect_engine::start_game_commandline( typedef boost::tuple mp_option; + rand_rng::mt_rng rng; + unsigned num = 0; BOOST_FOREACH(side_engine_ptr side, side_engines_) { num++; @@ -535,7 +539,7 @@ void connect_engine::start_game_commandline( // Finally, resolve "random faction", // "random gender" and "random message", if any remains unresolved. - side->resolve_random(); + side->resolve_random(rng); } // end top-level loop update_and_send_diff(true); @@ -1189,7 +1193,7 @@ bool side_engine::swap_sides_on_drop_target(const unsigned drop_target) { return true; } -void side_engine::resolve_random(const std::vector & avoid_faction_ids) +void side_engine::resolve_random(rand_rng::mt_rng & rng, const std::vector & avoid_faction_ids) { if (parent_.params_.saved_game) { return; @@ -1197,7 +1201,7 @@ void side_engine::resolve_random(const std::vector & avoid_faction_ chose_random_ = flg_.is_random_faction(); - flg_.resolve_random(avoid_faction_ids); + flg_.resolve_random(rng, avoid_faction_ids); LOG_MP << "side " << (index_ + 1) << ": faction=" << (flg_.current_faction())["name"] << ", leader=" << diff --git a/src/game_initialization/connect_engine.hpp b/src/game_initialization/connect_engine.hpp index b835bc373d15..81506c506e8c 100644 --- a/src/game_initialization/connect_engine.hpp +++ b/src/game_initialization/connect_engine.hpp @@ -22,6 +22,8 @@ #include "saved_game.hpp" #include +namespace rand_rng { class mt_rng; } + namespace ng { enum controller { @@ -157,7 +159,7 @@ class side_engine bool swap_sides_on_drop_target(const unsigned drop_target); - void resolve_random(const std::vector & avoid_faction_ids = std::vector()); + void resolve_random( rand_rng::mt_rng & rng, const std::vector & avoid_faction_ids = std::vector()); // Resets this side to its default state. void reset(); diff --git a/src/game_initialization/flg_manager.cpp b/src/game_initialization/flg_manager.cpp index 704c828497d8..c2cb7bf8d5b8 100644 --- a/src/game_initialization/flg_manager.cpp +++ b/src/game_initialization/flg_manager.cpp @@ -16,6 +16,7 @@ #include "config.hpp" #include "formula_string_utils.hpp" #include "gettext.hpp" +#include "mt_rng.hpp" #include "unit_types.hpp" #include "wml_separators.hpp" @@ -215,7 +216,7 @@ bool flg_manager::is_random_faction() // assigning as we would have if it were off. // If there is still no options we throw a config error because it means the // era is misconfigured. -void flg_manager::resolve_random(const std::vector & avoid) { +void flg_manager::resolve_random(rand_rng::mt_rng & rng, const std::vector & avoid) { if (is_random_faction()) { std::vector faction_choices, faction_excepts; @@ -272,7 +273,7 @@ void flg_manager::resolve_random(const std::vector & avoid) { } const int faction_index = - nonrandom_sides[rand() % nonrandom_sides.size()]; + nonrandom_sides[rng.get_next_random() % nonrandom_sides.size()]; current_faction_ = available_factions_[faction_index]; update_available_leaders(); @@ -297,7 +298,7 @@ void flg_manager::resolve_random(const std::vector & avoid) { "Unable to find a leader type for faction $faction", i18n_symbols)); } else { - const int lchoice = rand() % nonrandom_leaders.size(); + const int lchoice = rng.get_next_random() % nonrandom_leaders.size(); current_leader_ = nonrandom_leaders[lchoice]; update_available_genders(); @@ -316,7 +317,7 @@ void flg_manager::resolve_random(const std::vector & avoid) { } } - const int gchoice = rand() % nonrandom_genders.size(); + const int gchoice = rng.get_next_random() % nonrandom_genders.size(); current_gender_ = nonrandom_genders[gchoice]; } else { utils::string_map i18n_symbols; diff --git a/src/game_initialization/flg_manager.hpp b/src/game_initialization/flg_manager.hpp index d7dc0943860e..22cfe39f3581 100644 --- a/src/game_initialization/flg_manager.hpp +++ b/src/game_initialization/flg_manager.hpp @@ -20,6 +20,7 @@ #include class config; +namespace rand_rng { class mt_rng; } namespace ng { @@ -55,7 +56,7 @@ class flg_manager void reset_gender_combo(gui::combo& combo_gender) const; bool is_random_faction(); - void resolve_random(const std::vector & avoid); //Argument is a list of faction ids we don't want to match, used to implement random faction modes. If it is not possible to resolve then we just proceed anyways rather than give an error. + void resolve_random(rand_rng::mt_rng & rng, const std::vector & avoid); //Second Argument is a list of faction ids we don't want to match, used to implement random faction modes. If it is not possible to resolve then we just proceed anyways rather than give an error. // Picks the first faction with the greater amount of data // matching the criteria. diff --git a/src/tests/test_mp_connect.cpp b/src/tests/test_mp_connect.cpp index bb3d60b876ae..157d40d35de2 100644 --- a/src/tests/test_mp_connect.cpp +++ b/src/tests/test_mp_connect.cpp @@ -21,6 +21,7 @@ #include "game_initialization/multiplayer_connect.hpp" #include "game_initialization/multiplayer_ui.hpp" #include "hotkey/hotkey_manager.hpp" +#include "mt_rng.hpp" #include "saved_game.hpp" #include @@ -53,6 +54,7 @@ namespace { boost::scoped_ptr disp; boost::scoped_ptr state; +boost::scoped_ptr rng; } @@ -87,6 +89,8 @@ struct mp_connect_fixture { game_config().find_child(lexical_cast(game_classification::MULTIPLAYER), "id", state->mp_settings().name)); state->mp_settings().num_turns = state->get_starting_pos()["turns"]; + + rng.reset(new rand_rng::mt_rng()); } ~mp_connect_fixture() { @@ -299,7 +303,7 @@ BOOST_AUTO_TEST_CASE( flg_map_settings ) side.clear(); side["faction"] = "Random"; side_engine.reset(create_side_engine(side, connect_engine.get())); - side_engine->resolve_random(); + side_engine->resolve_random(*rng); BOOST_CHECK( side_engine->flg().current_faction()["id"] != "Random" ); BOOST_CHECK( side_engine->flg().current_leader() != "random" && side_engine->flg().current_leader() != "null"); @@ -311,7 +315,7 @@ BOOST_AUTO_TEST_CASE( flg_map_settings ) side["faction"] = "Random"; side["type"] = "Troll"; side_engine.reset(create_side_engine(side, connect_engine.get())); - side_engine->resolve_random(); + side_engine->resolve_random(*rng); BOOST_CHECK( side_engine->flg().current_faction()["id"] != "Random" ); BOOST_CHECK_EQUAL( side_engine->flg().current_leader(), "Troll" ); BOOST_CHECK( side_engine->flg().current_gender() != "random" && @@ -323,7 +327,7 @@ BOOST_AUTO_TEST_CASE( flg_map_settings ) side["type"] = "White Mage"; side["gender"] = "male"; side_engine.reset(create_side_engine(side, connect_engine.get())); - side_engine->resolve_random(); + side_engine->resolve_random(*rng); BOOST_CHECK( side_engine->flg().current_faction()["id"] != "Random" ); BOOST_CHECK_EQUAL( side_engine->flg().current_leader(), "White Mage" ); BOOST_CHECK_EQUAL( side_engine->flg().current_gender(), "male" ); @@ -332,7 +336,7 @@ BOOST_AUTO_TEST_CASE( flg_map_settings ) side.clear(); side["type"] = "random"; side_engine.reset(create_side_engine(side, connect_engine.get())); - side_engine->resolve_random(); + side_engine->resolve_random(*rng); BOOST_CHECK( side_engine->flg().current_leader() != "random" ); }