random.cpp

Go to the documentation of this file.
00001 /* $Id: random.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 by David White <dave@whitevine.net>
00004    Copyright (C) 2005 - 2012 by Yann Dirson <ydirson@altern.org>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 /**
00017  *  @file
00018  *  Generate random numbers.
00019  *
00020  *  There are various ways to get a random number.
00021  *  rand()              This can be used for things that never are send over the
00022  *                      network e.g. generate a random map (the final result the
00023  *                      map is send, but the other players don't need to generate
00024  *                      the map.
00025  *
00026  *  get_random()        A random generator which is synchronized over the network
00027  *                      this only seems to work when it's used by 1 player at the
00028  *                      same time. It's synchronized after an event so if an event
00029  *                      runs at two clients at the same time it gets out of sync
00030  *                      and sets the entire game out of sync.
00031  *
00032  *  game_state::get_random()
00033  *                      A random generator which is seeded by the host of an MP
00034  *                      game. This generator is (not yet) synchronized over the
00035  *                      network. It's only used by [set_variable]rand=. The map
00036  *                      designer has to make sure it stays in sync. This
00037  *                      generator can be used at the same time at multiple client
00038  *                      since the generators are always in sync.
00039  */
00040 
00041 #include "global.hpp"
00042 
00043 #include "config.hpp"
00044 #include "log.hpp"
00045 #include "network.hpp"
00046 #include "random.hpp"
00047 #include "rng.hpp"
00048 #include "serialization/string_utils.hpp"
00049 #include "simple_rng.hpp"
00050 #include "util.hpp"
00051 
00052 static lg::log_domain log_random("random");
00053 #define DBG_RND LOG_STREAM(debug, log_random)
00054 #define LOG_RND LOG_STREAM(info, log_random)
00055 #define WRN_RND LOG_STREAM(warn, log_random)
00056 #define ERR_RND LOG_STREAM(err, log_random)
00057 
00058 namespace {
00059   rand_rng::rng *random_generator = NULL ;
00060   int last_seed;
00061   bool seed_valid = false;
00062   boost::function<void (int)> new_seed_callback;
00063 }
00064 
00065 
00066 int get_random()
00067 {
00068   assert(random_generator!=NULL);
00069   int r = random_generator->get_random();
00070   return r ;
00071 }
00072 
00073 int get_random_nocheck()
00074 {
00075   assert(random_generator!=NULL);
00076   int r = random_generator->get_random_nocheck();
00077   return r ;
00078 }
00079 
00080 const config* get_random_results()
00081 {
00082   assert(random_generator!=NULL);
00083   return random_generator->get_random_results();
00084 }
00085 
00086 
00087 void set_random_results(const config& cfg)
00088 {
00089   assert(random_generator!=NULL);
00090   random_generator->set_random_results(cfg);
00091 }
00092 
00093 
00094 namespace rand_rng
00095 {
00096 
00097 void set_seed(int seed)
00098 {
00099     LOG_RND << "set_seed with " << seed << "\n";
00100     assert(random_generator!=NULL);
00101     last_seed = seed;
00102     seed_valid = true;
00103     random_generator->set_seed(seed);
00104     if (new_seed_callback) {
00105         LOG_RND << "set_seed calling new_seed_callback\n";
00106         new_seed_callback(seed);
00107     }
00108 }
00109 
00110 void invalidate_seed()
00111 {
00112     LOG_RND << "invalidate_seed\n";
00113     assert(random_generator!=NULL);
00114     last_seed = rand() & 0x7FFFFFFF;
00115     if (has_valid_seed()) { //aka SRNG is disabled
00116         random_generator->set_seed(last_seed);
00117     }
00118     seed_valid = false;
00119 }
00120 
00121 bool has_valid_seed()
00122 {
00123     //if we're in a SP game the seed is always valid
00124     return (network::nconnections() == 0) || seed_valid;
00125 }
00126 
00127 int get_last_seed()
00128 {
00129     return last_seed;
00130 }
00131 
00132 void set_new_seed_callback(boost::function<void (int)> f)
00133 {
00134     DBG_RND << "set_new_seed_callback\n";
00135     new_seed_callback = f;
00136 }
00137 
00138 bool has_new_seed_callback()
00139 {
00140     return new_seed_callback != NULL;
00141 }
00142 
00143 void clear_new_seed_callback()
00144 {
00145     DBG_RND << "clear_new_seed_callback\n";
00146     new_seed_callback = NULL;
00147 }
00148 
00149 
00150 
00151 rng::rng() : random_(NULL), random_child_(0), generator_()
00152 {
00153 }
00154 
00155 int rng::get_random()
00156 {
00157     return get_random_private(true);
00158 }
00159 
00160 int rng::get_random_nocheck()
00161 {
00162     return get_random_private(false);
00163 }
00164 
00165 int rng::get_random_private(bool check)
00166 {
00167     if (!random_) {
00168         int r = generator_.get_next_random();
00169         LOG_RND << "get_random() returning " << r << " (random_ is null)\n";
00170         return r;
00171     }
00172 
00173     size_t random_size = random_->child_count("random");
00174     if (random_child_ >= random_size) {
00175         random_child_ = random_size + 1;
00176         int res = generator_.get_next_random() & 0x7FFFFFFF;
00177         (random_->add_child("random"))["value"] = res;
00178         LOG_RND << "get_random() returning " << res << " (added to random_)\n";
00179         return res;
00180     } else {
00181         int mine = generator_.get_next_random();
00182         int stored = random_->child("random", random_child_++)["value"];
00183         if (mine != stored) {
00184             if (check) {
00185                 ERR_RND << "Random number mismatch, mine " << mine << " vs " << stored << "\n";
00186                 //OOS here?
00187             } else {
00188                 LOG_RND << "Random number mismatch (nocheck), mine " << mine << " vs " << stored << "\n";
00189             }
00190         }
00191         LOG_RND << "get_random() returning " << stored << "\n";
00192         return stored;
00193     }
00194 }
00195 
00196 const config* rng::get_random_results()
00197 {
00198     assert(random_ != NULL);
00199 
00200     if (random_child_ <= 0 ||random_child_ > random_->child_count("random")) return NULL;
00201     const config &res = random_->child("random", random_child_ - 1).child("results");
00202     return res ? &res : NULL;
00203 }
00204 
00205 void rng::set_random_results(const config& cfg)
00206 {
00207     assert(random_ != NULL);
00208 
00209     if (random_child_ <= 0 ||random_child_ > random_->child_count("random")) return;
00210     config &r = random_->child("random", random_child_ - 1);
00211     r.clear_children("results");
00212     r.add_child("results", cfg);
00213 }
00214 
00215 config* rng::random()
00216 {
00217     return random_;
00218 }
00219 
00220 void rng::set_random(config* random)
00221 {
00222     random_ = random;
00223     random_child_ = 0;
00224     return;
00225 }
00226 
00227 void rng::set_seed(int seed)
00228 {
00229     LOG_RND << "Set random seed to " << seed << "\n";
00230     generator_.seed_random(seed, 0);
00231 }
00232 
00233 
00234 set_random_generator::set_random_generator(rng* r) : old_(random_generator)
00235 {
00236     random_generator = r;
00237 }
00238 
00239 set_random_generator::~set_random_generator()
00240 {
00241     random_generator = old_;
00242 }
00243 
00244 simple_rng::simple_rng() :
00245     random_seed_(rand() & 0x7FFFFFFF),
00246     random_pool_(random_seed_),
00247     random_calls_(0)
00248 {
00249 }
00250 
00251 simple_rng::simple_rng(const config& cfg) :
00252     random_seed_(cfg["random_seed"]),
00253     random_pool_(random_seed_),
00254     random_calls_(0)
00255 {
00256 }
00257 
00258 int simple_rng::get_next_random()
00259 {
00260     random_next();
00261     ++random_calls_;
00262     DBG_RND << "pulled user random " << random_pool_
00263         << " for call " << random_calls_
00264         << " with seed " << random_seed_ << '\n';
00265 
00266     return (random_pool_ / 65536) % 32768;
00267 }
00268 
00269 void simple_rng::seed_random(const unsigned call_count)
00270 {
00271     seed_random(random_seed_, call_count);
00272 }
00273 
00274 void simple_rng::rotate_random()
00275 {
00276     random_seed_ = random_pool_ & 0x7FFFFFFF;
00277     random_calls_ = 0;
00278 }
00279 
00280 void simple_rng::seed_random(const int seed, const unsigned call_count)
00281 {
00282     random_pool_ = seed;
00283     random_seed_ = seed;
00284     for(random_calls_ = 0; random_calls_ < call_count; ++random_calls_) {
00285         random_next();
00286     }
00287     DBG_RND << "Seeded random with " << random_seed_ << " with "
00288         << random_calls_ << " calls, pool is now at "
00289         << random_pool_ << '\n';
00290 }
00291 
00292 void simple_rng::random_next()
00293 {
00294     // Use the simple random generator as shown in man rand(3).
00295     // The division is done separately since we also want to
00296     // quickly go the the wanted index in the random list.
00297     random_pool_ = random_pool_ * 1103515245 + 12345;
00298 }
00299 
00300 
00301 } // ends rand_rng namespace
00302 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:08 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs