Committing RandomGenerator patch from Allen King, with a few small changes
authorRyan Flegel <rflegel@gmail.com>
Wed, 26 Apr 2006 02:13:42 +0000 (02:13 +0000)
committerRyan Flegel <rflegel@gmail.com>
Wed, 26 Apr 2006 02:13:42 +0000 (02:13 +0000)
Created scripting wrapper for the random number generator
 - could someone check this over to make sure I did it properly? :)

SVN-Revision: 3435

28 files changed:
data/levels/world1/intro.nut
src/badguy/dispenser.cpp
src/badguy/kugelblitz.cpp
src/badguy/skullyhop.cpp
src/badguy/stalactite.cpp
src/badguy/zeekling.cpp
src/game_session.cpp
src/gameconfig.cpp
src/gameconfig.hpp
src/main.cpp
src/mainloop.cpp
src/object/fireworks.cpp
src/object/gameobjs.cpp
src/object/particles.cpp
src/object/particlesystem.cpp
src/object/particlesystem_interactive.cpp
src/object/player.cpp
src/object/skull_tile.cpp
src/object/unstable_tile.cpp
src/random_generator.cpp [new file with mode: 0644]
src/random_generator.hpp [new file with mode: 0644]
src/scripting/player.hpp
src/scripting/random_generator.hpp [new file with mode: 0644]
src/scripting/squirrel_util.cpp
src/scripting/wrapper.cpp
src/scripting/wrapper.hpp
src/scripting/wrapper.interface.hpp
src/title.cpp

index e418a7f..03c14fd 100644 (file)
@@ -85,7 +85,7 @@ function shake_bush()
   local bushx = BUSH.get_pos_x();
   local bushy = BUSH.get_pos_y();
   for(local i = 0; i < 20; ++i) {
-    BUSH.set_pos(bushx + rand() % 6 - 3, bushy);
+    BUSH.set_pos(bushx + RandomGenerator.rand1i(6) - 3, bushy);
     wait(0.05);
   }
 }
index 09f8d39..260fbc8 100644 (file)
@@ -28,6 +28,7 @@
 #include "badguy/poisonivy.hpp"
 #include "badguy/snowsnail.hpp"
 #include "badguy/skullyhop.hpp"
+#include "random_generator.hpp"
 
 Dispenser::Dispenser(const lisp::Lisp& reader)
 {
@@ -109,7 +110,7 @@ Dispenser::launch_badguy()
       Sector::current()->add_object(new SkullyHop(get_pos().x, get_pos().y+44, dir));
     else if (badguy == "random")
     {
-      switch (rand()%7)
+      switch (systemRandom.rand(7))
       {
         case 0: Sector::current()->add_object(new SnowBall(get_pos().x, get_pos().y+32, dir)); break;
         case 1: Sector::current()->add_object(new BouncingSnowball(get_pos().x, get_pos().y+32, dir)); break;
index a3e2128..8d0c77b 100644 (file)
@@ -23,6 +23,7 @@
 #include "object/tilemap.hpp"
 #include "object/camera.hpp"
 #include "tile.hpp"
+#include "random_generator.hpp"
 
 #define  LIFETIME 5
 #define  MOVETIME 0.75
@@ -111,8 +112,8 @@ Kugelblitz::hit(const CollisionHit& chit)
     sprite->set_action("flying");
     physic.set_velocity_y(0);
     //Set random initial speed and direction
-    if ((rand() % 2) == 1) direction = 1; else direction = -1;
-    int speed = (BASE_SPEED + (rand() % RAND_SPEED)) * direction;
+    direction = systemRandom.rand(2)? 1: -1;
+    int speed = (BASE_SPEED + (systemRandom.rand(RAND_SPEED))) * direction;
     physic.set_velocity_x(speed);
     movement_timer.start(MOVETIME);
     lifetime.start(LIFETIME);
@@ -134,7 +135,7 @@ Kugelblitz::active_update(float elapsed_time)
     if (groundhit_pos_set) {
       if (movement_timer.check()) {
         if (direction == 1) direction = -1; else direction = 1;
-        int speed = (BASE_SPEED + (rand() % RAND_SPEED)) * direction;
+        int speed = (BASE_SPEED + (systemRandom.rand(RAND_SPEED))) * direction;
         physic.set_velocity_x(speed);
         movement_timer.start(MOVETIME);
       }
index 68be79c..2679ee7 100644 (file)
@@ -20,6 +20,7 @@
 #include <config.h>
 
 #include "skullyhop.hpp"
+#include "random_generator.hpp"
 
 namespace {
   const float VERTICAL_SPEED = 450;   /**< y-speed when jumping */
@@ -74,7 +75,7 @@ SkullyHop::set_state(SkullyHopState newState)
     physic.set_velocity_y(0);
     sprite->set_action(dir == LEFT ? "standing-left" : "standing-right");
 
-    float recover_time = MIN_RECOVER_TIME + (float)rand() / RAND_MAX * (MAX_RECOVER_TIME - MIN_RECOVER_TIME);
+    float recover_time = systemRandom.randf(MIN_RECOVER_TIME,MAX_RECOVER_TIME);
     recover_timer.start(recover_time);
   } else
   if (newState == CHARGING) {
index f0ce5cf..ab980a0 100644 (file)
@@ -20,6 +20,7 @@
 #include <config.h>
 
 #include "stalactite.hpp"
+#include "random_generator.hpp"
 
 static const int SHAKE_RANGE = 40;
 static const float SHAKE_TIME = .8;
@@ -109,7 +110,7 @@ Stalactite::draw(DrawingContext& context)
     return;
     
   if(state == STALACTITE_SHAKING) {
-    sprite->draw(context, get_pos() + Vector((rand() % 6)-3, 0), LAYER_OBJECTS);
+    sprite->draw(context, get_pos() + Vector(systemRandom.rand(-3,3), 0), LAYER_OBJECTS);
   } else {
     sprite->draw(context, get_pos(), LAYER_OBJECTS);
   }
index 9030ec9..282d1aa 100644 (file)
@@ -23,6 +23,7 @@
 #include <math.h>
 
 #include "zeekling.hpp"
+#include "random_generator.hpp"
 
 Zeekling::Zeekling(const lisp::Lisp& reader)
 {
@@ -59,7 +60,7 @@ Zeekling::write(lisp::Writer& writer)
 void
 Zeekling::activate()
 {
-  speed = 130 + (rand() % 41);
+  speed = systemRandom.rand(130, 171);
   if (set_direction) {dir = initial_direction;}
   physic.set_velocity_x(dir == LEFT ? -speed : speed);
   physic.enable_gravity(false);
index 2f66372..d843f5e 100644 (file)
@@ -66,6 +66,7 @@
 #include "console.hpp"
 #include "flip_level_transformer.hpp"
 #include "trigger/secretarea_trigger.hpp"
+#include "random_generator.hpp"
 
 // the engine will be run with a logical framerate of 64fps.
 // We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
@@ -141,8 +142,14 @@ GameSession::restart_level(bool fromBeginning)
 
   currentsector->play_music(LEVEL_MUSIC);
 
-  if(capture_file != "")
+  if(capture_file != "") {
+    int newSeed=0;               // next run uses a new seed
+    while (newSeed == 0)            // which is the next non-zero random num.
+        newSeed = systemRandom.rand();
+    config->random_seed = systemRandom.srand(newSeed);
+    log_info << "Next run uses random seed " <<config->random_seed <<std::endl;
     record_demo(capture_file);
+  }
 }
 
 GameSession::~GameSession()
@@ -168,6 +175,11 @@ GameSession::record_demo(const std::string& filename)
     throw std::runtime_error(msg.str());
   }
   capture_file = filename;
+
+  char buf[30];                            // save the seed in the demo file
+  sprintf(buf, "random_seed=%010d", config->random_seed);
+  for (int i=0; i==0 || buf[i-1]; i++)
+    capture_demo_stream->put(buf[i]);
 }
 
 void
@@ -186,6 +198,19 @@ GameSession::play_demo(const std::string& filename)
   Player& tux = *currentsector->player;
   demo_controller = new CodeController();
   tux.set_controller(demo_controller);
+
+  char buf[30];                            // recall the seed from the demo file
+  int seed;
+  for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
+    playback_demo_stream->get(buf[i]);
+  if (sscanf(buf, "random_seed=%010d", &seed) == 1) {
+    config->random_seed = seed;            // save where it will be used
+    log_info << "Taking random seed " << seed << " from demo file" <<std::endl;
+  }
+  else {
+    playback_demo_stream->seekg(0);     // old style w/o seed, restart at beg
+    log_info << "Demo file contains no random number" << std::endl;
+  }
 }
 
 void
index 416d104..c2d1431 100644 (file)
@@ -40,6 +40,7 @@ Config::Config()
   sound_enabled = true;
   music_enabled = true;
   cheats_enabled = false;
+  random_seed = 0;          // set by time(), by default (unless in config)
 
   screenwidth = 800;
   screenheight = 600;
@@ -62,6 +63,7 @@ Config::load()
 
   config_lisp->get("show_fps", show_fps);
   config_lisp->get("cheats", cheats_enabled);
+  config_lisp->get("random_seed", random_seed);
 
   const lisp::Lisp* config_video_lisp = config_lisp->get_lisp("video");
   if(config_video_lisp) {
index 109df1a..1602787 100644 (file)
@@ -43,6 +43,8 @@ public:
   bool music_enabled;
   bool cheats_enabled;
 
+  int random_seed;            // initial random seed.  0 ==> set from time()
+
   /** this variable is set if supertux should start in a specific level */
   std::string start_level;
   bool enable_script_debugger;
index c1bd127..8bf4657 100644 (file)
@@ -53,6 +53,7 @@
 #include "scripting/squirrel_util.hpp"
 #include "file_system.hpp"
 #include "physfs/physfs_sdl.hpp"
+#include "random_generator.hpp"
 
 SDL_Surface* screen = 0;
 JoystickKeyboardController* main_controller = 0;
@@ -287,6 +288,15 @@ static void init_sdl()
       ;
 }
 
+static void init_rand()
+{
+  const char *how = config->random_seed? ", user fixed.": ", from time().";
+
+  config->random_seed = systemRandom.srand(config->random_seed);
+
+  log_info << "Using random seed " << config->random_seed << how << std::endl;
+}
+
 void init_video()
 {
   if(texture_manager != NULL)
@@ -434,7 +444,7 @@ int main(int argc, char** argv)
     
   try {
     Console::instance = new Console();
-    srand(time(0));
+//  srand(time(0));            // this breaks repeatability in random numbers
     init_physfs(argv[0]);
     init_sdl();
     
@@ -464,18 +474,24 @@ int main(int argc, char** argv)
       // So we simply mount that path here...
       std::string dir = FileSystem::dirname(config->start_level);
       PHYSFS_addToSearchPath(dir.c_str(), true);
+
+      init_rand();        // play_demo sets seed, record_demo uses it
+
       std::auto_ptr<GameSession> session
         (new GameSession(FileSystem::basename(config->start_level)));
       if(config->start_demo != "")
         session->play_demo(config->start_demo);
+
       if(config->record_demo != "")
         session->record_demo(config->record_demo);
       main_loop->push_screen(session.release());
     } else {
+      init_rand();
       main_loop->push_screen(new TitleScreen());
     }
 
     main_loop->run();
+
   } catch(std::exception& e) {
     log_fatal << "Unexpected exception: " << e.what() << std::endl;
     result = 1;
index 95104c6..fb45247 100644 (file)
@@ -35,6 +35,7 @@
 #include "screen_fade.hpp"
 #include "timer.hpp"
 #include "player_status.hpp"
+#include "random_generator.hpp"
 
 // the engine will be run with a logical framerate of 64fps.
 // We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
@@ -123,6 +124,7 @@ MainLoop::run()
   DrawingContext context; 
   
   unsigned int frame_count = 0;
+  unsigned int rand_prints = 0; 
   float fps_fps = 0;
   Uint32 fps_ticks = SDL_GetTicks();
   Uint32 fps_nextframe_ticks = SDL_GetTicks();
@@ -237,6 +239,11 @@ MainLoop::run()
     }
 
     sound_manager->update();
+    // insert calls for debug (there are few rand calls otherwise)
+    if (0 && rand_prints++ % 20 == 0)
+        log_info << "== periodic rand() call " << systemRandom.rand() << 
+                " at frame " << rand_prints << "==" <<std::endl;
   }
 }
 
index acfa881..a6333c5 100644 (file)
@@ -27,6 +27,7 @@
 #include "main.hpp"
 #include "video/drawing_context.hpp"
 #include "audio/sound_manager.hpp"
+#include "random_generator.hpp"
 
 Fireworks::Fireworks()
 {
@@ -43,13 +44,11 @@ Fireworks::update(float )
     if(timer.check()) {
         Sector* sector = Sector::current();
         Vector pos = sector->camera->get_translation();
-        pos += Vector(SCREEN_WIDTH * ((float) rand() / RAND_MAX),
-                      SCREEN_HEIGHT/2 * ((float) rand() / RAND_MAX));
+        pos += Vector(systemRandom.randf(SCREEN_WIDTH),
+                      systemRandom.randf(SCREEN_HEIGHT/2));
 
-        int r = rand() % 255;
-        int g = rand() % 255;
-        float red = r / 255.0;
-        float green = g / 255.0;
+        float red = systemRandom.randf(1.0);
+        float green = systemRandom.randf(1.0);
         //float red = 0.7;
         //float green = 0.9;
         (void) red;
@@ -58,7 +57,7 @@ Fireworks::update(float )
                 Vector(0, 0), 45, Color(red, green, 0), 3, 1.3,
                 LAYER_FOREGROUND1+1));
         sound_manager->play("sounds/fireworks.wav");
-        timer.start(((float) rand() / RAND_MAX) + .5);
+        timer.start(systemRandom.randf(1.0, 1.5));
     }
 }
 
index dff136d..caf93ed 100644 (file)
@@ -35,6 +35,7 @@
 #include "video/drawing_context.hpp"
 #include "camera.hpp"
 #include "main.hpp"
+#include "random_generator.hpp"
 
 BouncyCoin::BouncyCoin(const Vector& pos)
   : position(pos)
@@ -91,7 +92,7 @@ void
 BrokenBrick::draw(DrawingContext& context)
 {
   sprite->draw_part(context,
-      Vector(rand() % 16, rand() % 16), Vector(16, 16),
+      Vector(systemRandom.rand(16), systemRandom.rand(16)), Vector(16, 16),
       position, LAYER_OBJECTS + 1);
 }
 
index 74db7fb..cea6c82 100644 (file)
@@ -25,6 +25,7 @@
 #include "sector.hpp"
 #include "camera.hpp"
 #include "main.hpp"
+#include "random_generator.hpp"
 
 Particles::Particles(const Vector& epicenter, int min_angle, int max_angle,
         const Vector& initial_velocity, const Vector& acceleration, int number,
@@ -44,8 +45,8 @@ Particles::Particles(const Vector& epicenter, int min_angle, int max_angle,
     Particle* particle = new Particle;
     particle->pos = epicenter;
 
-    float angle = ((rand() % (max_angle-min_angle))+min_angle)
-                      * (M_PI / 180);  // convert to radius
+    float angle = systemRandom.rand(min_angle, max_angle)
+                      * (M_PI / 180);  // convert to radius (radians?)
     particle->vel.x = /*fabs*/(sin(angle)) * initial_velocity.x;
 //    if(angle >= M_PI && angle < M_PI*2)
 //      particle->vel.x *= -1;  // work around to fix signal
index 56d33b8..56e4414 100644 (file)
@@ -30,6 +30,7 @@
 #include "resources.hpp"
 #include "main.hpp"
 #include "object/camera.hpp"
+#include "random_generator.hpp"
 
 ParticleSystem::ParticleSystem()
 {
@@ -85,12 +86,12 @@ SnowParticleSystem::SnowParticleSystem()
   size_t snowflakecount = size_t(virtual_width/10.0);
   for(size_t i=0; i<snowflakecount; ++i) {
     SnowParticle* particle = new SnowParticle;
-    particle->pos.x = fmodf(rand(), virtual_width);
-    particle->pos.y = fmodf(rand(), SCREEN_HEIGHT);
-    int snowsize = rand() % 3;
+    particle->pos.x = systemRandom.randf(virtual_width);
+    particle->pos.y = systemRandom.randf(SCREEN_HEIGHT);
+    int snowsize = systemRandom.rand(3);
     particle->texture = snowimages[snowsize];
     do {
-      particle->speed = snowsize*.2 + (float(rand()%10)*.4);
+      particle->speed = snowsize*.2 + systemRandom.randf(3.6);
     } while(particle->speed < 1);
     particle->speed *= 10; // gravity
 
@@ -126,7 +127,7 @@ void SnowParticleSystem::update(float elapsed_time)
     particle->pos.y += particle->speed * elapsed_time;
     if(particle->pos.y > SCREEN_HEIGHT + Sector::current()->camera->get_translation().y) {
       particle->pos.y = fmodf(particle->pos.y , virtual_height);
-      particle->pos.x = rand() % int(virtual_width);
+      particle->pos.x = systemRandom.rand((int)virtual_width);
     }
   }
 }
@@ -144,12 +145,12 @@ GhostParticleSystem::GhostParticleSystem()
   size_t ghostcount = 2;
   for(size_t i=0; i<ghostcount; ++i) {
     GhostParticle* particle = new GhostParticle;
-    particle->pos.x = fmodf(rand(), virtual_width);
-    particle->pos.y = fmodf(rand(), SCREEN_HEIGHT);
-    int size = rand() % 2;
+    particle->pos.x = systemRandom.randf(virtual_width);
+    particle->pos.y = systemRandom.randf(SCREEN_HEIGHT);
+    int size = systemRandom.rand(2);
     particle->texture = ghosts[size];
     do {
-      particle->speed = size*.2 + (float(rand()%10)*.4);
+      particle->speed = size*.2 + systemRandom.randf(3.6);
     } while(particle->speed < 1);
     particle->speed *= 50;
     particles.push_back(particle);
@@ -185,7 +186,7 @@ void GhostParticleSystem::update(float elapsed_time)
     particle->pos.x -= particle->speed * elapsed_time;
     if(particle->pos.y > SCREEN_HEIGHT) {
       particle->pos.y = fmodf(particle->pos.y , virtual_height);
-      particle->pos.x = rand() % int(virtual_width);
+      particle->pos.x = systemRandom.rand((int)virtual_width);
     }
   }
 }
@@ -199,10 +200,10 @@ CloudParticleSystem::CloudParticleSystem()
   // create some random clouds
   for(size_t i=0; i<15; ++i) {
     CloudParticle* particle = new CloudParticle;
-    particle->pos.x = rand() % int(virtual_width);
-    particle->pos.y = rand() % int(virtual_height);
+    particle->pos.x = systemRandom.rand((int)virtual_width);
+    particle->pos.y = systemRandom.rand((int)virtual_height);
     particle->texture = cloudimage;
-    particle->speed = -float(25 + rand() % 30);
+    particle->speed = -systemRandom.randf(25, 54);
 
     particles.push_back(particle);
   }
index b2f55ca..0a9228a 100644 (file)
@@ -38,6 +38,7 @@
 #include "object/camera.hpp"
 #include "object/rainsplash.hpp"
 #include "badguy/bomb.hpp"
+#include "random_generator.hpp"
 
 //TODO: Find a way to make rain collide with objects like bonus blocks
 //      Add an option to set rain strength
@@ -152,12 +153,12 @@ RainParticleSystem::RainParticleSystem()
     size_t raindropcount = size_t(virtual_width/6.0);
     for(size_t i=0; i<raindropcount; ++i) {
         RainParticle* particle = new RainParticle;
-        particle->pos.x = rand() % int(virtual_width);
-        particle->pos.y = rand() % int(virtual_height);
-        int rainsize = rand() % 2;
+        particle->pos.x = systemRandom.rand(int(virtual_width));
+        particle->pos.y = systemRandom.rand(int(virtual_height));
+        int rainsize = systemRandom.rand(2);
         particle->texture = rainimages[rainsize];
         do {
-            particle->speed = (rainsize+1)*45 + (float(rand()%10)*.4);
+            particle->speed = (rainsize+1)*45 + systemRandom.randf(3.6);
         } while(particle->speed < 1);
         particle->speed *= 10; // gravity
 
@@ -214,7 +215,7 @@ void RainParticleSystem::update(float elapsed_time)
                 Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
               } */
             }
-            int new_x = (rand() % int(virtual_width)) + int(abs_x);
+            int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
             int new_y = 0;
             //FIXME: Don't move particles over solid tiles
             particle->pos.x = new_x;
@@ -234,12 +235,12 @@ CometParticleSystem::CometParticleSystem()
     size_t cometcount = 2;
     for(size_t i=0; i<cometcount; ++i) {
         CometParticle* particle = new CometParticle;
-        particle->pos.x = rand() % int(virtual_width);
-        particle->pos.y = rand() % int(virtual_height);
-        int cometsize = rand() % 2;
+        particle->pos.x = systemRandom.rand(int(virtual_width));
+        particle->pos.y = systemRandom.rand(int(virtual_height));
+        int cometsize = systemRandom.rand(2);
         particle->texture = cometimages[cometsize];
         do {
-            particle->speed = (cometsize+1)*30 + (float(rand()%10)*.4);
+            particle->speed = (cometsize+1)*30 + systemRandom.randf(3.6);
         } while(particle->speed < 1);
         particle->speed *= 10; // gravity
 
@@ -283,7 +284,7 @@ void CometParticleSystem::update(float elapsed_time)
             if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)) {
               Sector::current()->add_object(new Bomb(particle->pos, LEFT));
             }
-            int new_x = (rand() % int(virtual_width)) + int(abs_x);
+            int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
             int new_y = 0;
             //FIXME: Don't move particles over solid tiles
             particle->pos.x = new_x;
index f7c5605..91013b1 100644 (file)
@@ -47,6 +47,7 @@
 #include "player_status.hpp"
 #include "log.hpp"
 #include "falling_coin.hpp"
+#include "random_generator.hpp"
 
 static const int TILES_FOR_BUTTJUMP = 3;
 static const float SHOOTING_TIME = .150;
@@ -155,7 +156,7 @@ void
 Player::expose(HSQUIRRELVM vm, int table_idx)
 {
   Scripting::Player* interface = static_cast<Scripting::Player*> (this);
-  expose_object(vm, table_idx, interface, "Tux", false);
+  Scripting::expose_object(vm, table_idx, interface, "Tux", false);
 }
 
 void
@@ -902,12 +903,14 @@ Player::kill(HurtMode mode)
     }
   else
     {
-      srand(time(0));
+//       systemRandom.srand(time(0));// 060422 PAK: gone for repeatibility
       int i;
       for (i = 0; (i < 5) && (i < player_status->coins); i++)
       {
         // the numbers: starting x, starting y, velocity y
-        Sector::current()->add_object(new FallingCoin(get_pos() + Vector(rand()%5, rand()%50 - 32), rand()%200 - 100));
+        Sector::current()->add_object(new FallingCoin(get_pos() + 
+            Vector(systemRandom.rand(5), systemRandom.rand(-32,18)), 
+            systemRandom.rand(-100,100)));
       }
       physic.enable_gravity(true);
       physic.set_acceleration(0, 0);
index 1b6d7ea..66e4c90 100644 (file)
@@ -27,6 +27,7 @@
 #include "resources.hpp"
 #include "sprite/sprite_manager.hpp"
 #include "sprite/sprite.hpp"
+#include "random_generator.hpp"
 
 static const float CRACKTIME = 0.3;
 static const float FALLTIME = 0.8;
@@ -67,7 +68,7 @@ SkullTile::draw(DrawingContext& context)
   Vector pos = get_pos();
   // shacking
   if(timer.get_timegone() > CRACKTIME) {
-    pos.x += (rand() % 6) - 3;
+    pos.x += systemRandom.rand(-3, 3);
   } 
 
   sprite->draw(context, pos, LAYER_TILES);
index 68d2bbf..a7ac4f9 100644 (file)
@@ -27,6 +27,7 @@
 #include "resources.hpp"
 #include "sprite/sprite_manager.hpp"
 #include "sprite/sprite.hpp"
+#include "random_generator.hpp"
 
 static const float CRACKTIME = 0.3;
 static const float FALLTIME = 0.8;
@@ -66,7 +67,7 @@ UnstableTile::draw(DrawingContext& context)
   Vector pos = get_pos();
   // shacking
   if(timer.get_timegone() > CRACKTIME) {
-    pos.x += (rand() % 6) - 3;
+    pos.x += systemRandom.rand(-3, 3);
   } 
 
   sprite->draw(context, pos, LAYER_TILES);
diff --git a/src/random_generator.cpp b/src/random_generator.cpp
new file mode 100644 (file)
index 0000000..ecb95ba
--- /dev/null
@@ -0,0 +1,561 @@
+// $Id$
+// 
+// A strong random number generator
+//
+// Copyright (C) 2006 Allen King
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer.  
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.  
+// 3. Neither the name of the project nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission. 
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+// SUCH DAMAGE.
+
+// Transliterated into C++ Allen King 060417, from sources on
+//          http://www.jbox.dk/sanos/source/lib/random.c.html
+
+
+
+#include <stdexcept>
+#include "random_generator.hpp"
+#include "scripting/squirrel_util.hpp"
+
+RandomGenerator systemRandom;               // global random number generator
+
+RandomGenerator::RandomGenerator() {
+    assert(sizeof(int) >= 4);
+    initialized = 0;
+    initialize();
+}
+
+RandomGenerator::~RandomGenerator() {
+}
+
+int RandomGenerator::srand(int x)    {
+    while (x == 0)                          // random seed of zero means
+        x = time(0);                        // randomize with time
+    assert(x < RAND_MAX);                   // only allow posative 31-bit seeds
+    assert(sizeof(int) >= 4);
+    srandom(x);
+    return x;                               // let caller know seed used
+}
+
+int RandomGenerator::rand()                 {        return random();    }
+
+int RandomGenerator::rand(int v) {
+    assert(v != 0 && v <= RAND_MAX);        // illegal arg: 0 or too big
+    return RandomGenerator::random() % v;
+}
+
+int RandomGenerator::rand(int u, int v) {
+    assert(v > u);    
+    return u + RandomGenerator::rand(v-u);
+}
+
+double RandomGenerator::randf(double v) {
+    float rv;
+    while ((rv = (double)RandomGenerator::random() / RAND_MAX * v) >= v)
+        ;                                   // never return v!
+    return rv;
+}
+
+double RandomGenerator::randf(double u, double v) {
+    return u + RandomGenerator::randf(v-u);
+}
+
+//-----------------------------------------------------------------------
+//        
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer.  
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.  
+// 3. Neither the name of the project nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission. 
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+// SUCH DAMAGE.
+// 
+
+//**#include <os.h>
+
+//
+// An improved random number generation package.  In addition to the standard
+// rand()/srand() like interface, this package also has a special state info
+// interface.  The initstate() routine is called with a seed, an array of
+// bytes, and a count of how many bytes are being passed in; this array is
+// then initialized to contain information for random number generation with
+// that much state information.  Good sizes for the amount of state
+// information are 32, 64, 128, and 256 bytes.  The state can be switched by
+// calling the setstate() routine with the same array as was initiallized
+// with initstate().  By default, the package runs with 128 bytes of state
+// information and generates far better random numbers than a linear
+// congruential generator.  If the amount of state information is less than
+// 32 bytes, a simple linear congruential R.N.G. is used.
+//
+// Internally, the state information is treated as an array of longs; the
+// zeroeth element of the array is the type of R.N.G. being used (small
+// integer); the remainder of the array is the state information for the
+// R.N.G.  Thus, 32 bytes of state information will give 7 longs worth of
+// state information, which will allow a degree seven polynomial.  (Note:
+// the zeroeth word of state information also has some other information
+// stored in it -- see setstate() for details).
+//
+// The random number generation technique is a linear feedback shift register
+// approach, employing trinomials (since there are fewer terms to sum up that
+// way).  In this approach, the least significant bit of all the numbers in
+// the state table will act as a linear feedback shift register, and will
+// have period 2^deg - 1 (where deg is the degree of the polynomial being
+// used, assuming that the polynomial is irreducible and primitive).  The
+// higher order bits will have longer periods, since their values are also
+// influenced by pseudo-random carries out of the lower bits.  The total
+// period of the generator is approximately deg*(2**deg - 1); thus doubling
+// the amount of state information has a vast influence on the period of the
+// generator.  Note: the deg*(2**deg - 1) is an approximation only good for
+// large deg, when the period of the shift is the dominant factor.
+// With deg equal to seven, the period is actually much longer than the
+// 7*(2**7 - 1) predicted by this formula.
+//
+// Modified 28 December 1994 by Jacob S. Rosenberg.
+//
+
+//
+// For each of the currently supported random number generators, we have a
+// break value on the amount of state information (you need at least this
+// many bytes of state info to support this random number generator), a degree
+// for the polynomial (actually a trinomial) that the R.N.G. is based on, and
+// the separation between the two lower order coefficients of the trinomial.
+
+void RandomGenerator::initialize() {
+
+#define NSHUFF 100      // To drop part of seed -> 1st value correlation
+
+//static long degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
+//static long seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
+
+    degrees[0] = DEG_0;
+    degrees[1] = DEG_1;
+    degrees[2] = DEG_2;
+    degrees[3] = DEG_3;
+    degrees[4] = DEG_4;
+
+    seps [0] = SEP_0;
+    seps [1] = SEP_1;
+    seps [2] = SEP_2;
+    seps [3] = SEP_3;
+    seps [4] = SEP_4;
+
+//
+// Initially, everything is set up as if from:
+//
+//  initstate(1, randtbl, 128);
+//
+// Note that this initialization takes advantage of the fact that srandom()
+// advances the front and rear pointers 10*rand_deg times, and hence the
+// rear pointer which starts at 0 will also end up at zero; thus the zeroeth
+// element of the state information, which contains info about the current
+// position of the rear pointer is just
+//
+//  MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3.
+
+    randtbl[ 0] =  TYPE_3;
+    randtbl[ 1] =  0x991539b1;
+    randtbl[ 2] =  0x16a5bce3;
+    randtbl[ 3] =  0x6774a4cd;
+    randtbl[ 4] =  0x3e01511e;
+    randtbl[ 5] =  0x4e508aaa;
+    randtbl[ 6] =  0x61048c05;
+    randtbl[ 7] =  0xf5500617;
+    randtbl[ 8] =  0x846b7115;
+    randtbl[ 9] =  0x6a19892c;
+    randtbl[10] =  0x896a97af;
+    randtbl[11] =  0xdb48f936;
+    randtbl[12] =  0x14898454;
+    randtbl[13] =  0x37ffd106;
+    randtbl[14] =  0xb58bff9c;
+    randtbl[15] =  0x59e17104;
+    randtbl[16] =  0xcf918a49;
+    randtbl[17] =  0x09378c83;
+    randtbl[18] =  0x52c7a471;
+    randtbl[19] =  0x8d293ea9;
+    randtbl[20] =  0x1f4fc301;
+    randtbl[21] =  0xc3db71be;
+    randtbl[22] =  0x39b44e1c;
+    randtbl[23] =  0xf8a44ef9;
+    randtbl[24] =  0x4c8b80b1;
+    randtbl[25] =  0x19edc328;
+    randtbl[26] =  0x87bf4bdd;
+    randtbl[27] =  0xc9b240e5;
+    randtbl[28] =  0xe9ee4b1b;
+    randtbl[29] =  0x4382aee7;
+    randtbl[30] =  0x535b6b41;
+    randtbl[31] =  0xf3bec5da;
+
+// static long randtbl[DEG_3 + 1] = 
+// {
+//   TYPE_3;
+//   0x991539b1, 0x16a5bce3, 0x6774a4cd, 0x3e01511e, 0x4e508aaa, 0x61048c05,
+//   0xf5500617, 0x846b7115, 0x6a19892c, 0x896a97af, 0xdb48f936, 0x14898454,
+//   0x37ffd106, 0xb58bff9c, 0x59e17104, 0xcf918a49, 0x09378c83, 0x52c7a471,
+//   0x8d293ea9, 0x1f4fc301, 0xc3db71be, 0x39b44e1c, 0xf8a44ef9, 0x4c8b80b1,
+//   0x19edc328, 0x87bf4bdd, 0xc9b240e5, 0xe9ee4b1b, 0x4382aee7, 0x535b6b41,
+//   0xf3bec5da
+// };
+
+
+//
+// fptr and rptr are two pointers into the state info, a front and a rear
+// pointer.  These two pointers are always rand_sep places aparts, as they
+// cycle cyclically through the state information.  (Yes, this does mean we
+// could get away with just one pointer, but the code for random() is more
+// efficient this way).  The pointers are left positioned as they would be
+// from the call
+//
+//  initstate(1, randtbl, 128);
+//
+// (The position of the rear pointer, rptr, is really 0 (as explained above
+// in the initialization of randtbl) because the state table pointer is set
+// to point to randtbl[1] (as explained below).
+//
+
+    fptr = &randtbl[SEP_3 + 1];
+    rptr = &randtbl[1];
+
+//
+// The following things are the pointer to the state information table, the
+// type of the current generator, the degree of the current polynomial being
+// used, and the separation between the two pointers.  Note that for efficiency
+// of random(), we remember the first location of the state information, not
+// the zeroeth.  Hence it is valid to access state[-1], which is used to
+// store the type of the R.N.G.  Also, we remember the last location, since
+// this is more efficient than indexing every time to find the address of
+// the last element to see if the front and rear pointers have wrapped.
+//
+
+    state = &randtbl[1];
+    rand_type = TYPE_3;
+    rand_deg = DEG_3;
+    rand_sep = SEP_3;
+    end_ptr = &randtbl[DEG_3 + 1];
+
+}
+
+//
+// Compute x = (7^5 * x) mod (2^31 - 1)
+// wihout overflowing 31 bits:
+//      (2^31 - 1) = 127773 * (7^5) + 2836
+// From "Random number generators: good ones are hard to find",
+// Park and Miller, Communications of the ACM, vol. 31, no. 10,
+// October 1988, p. 1195.
+//
+
+__inline static long good_rand(long x)
+{
+  long hi, lo;
+
+  // Can't be initialized with 0, so use another value.
+  if (x == 0) x = 123459876;
+  hi = x / 127773;
+  lo = x % 127773;
+  x = 16807 * lo - 2836 * hi;
+  if (x < 0) x += 0x7fffffff;
+  return x;
+}
+
+//
+// srandom
+//
+// Initialize the random number generator based on the given seed.  If the
+// type is the trivial no-state-information type, just remember the seed.
+// Otherwise, initializes state[] based on the given "seed" via a linear
+// congruential generator.  Then, the pointers are set to known locations
+// that are exactly rand_sep places apart.  Lastly, it cycles the state
+// information a given number of times to get rid of any initial dependencies
+// introduced by the L.C.R.N.G.  Note that the initialization of randtbl[]
+// for default usage relies on values produced by this routine.
+
+void RandomGenerator::srandom(unsigned long x)
+{
+  long i, lim;
+
+  state[0] = x;
+  if (rand_type == TYPE_0)
+    lim = NSHUFF;
+  else 
+  {
+    for (i = 1; i < rand_deg; i++) state[i] = good_rand(state[i - 1]);
+    fptr = &state[rand_sep];
+    rptr = &state[0];
+    lim = 10 * rand_deg;
+  }
+
+  initialized = 1;
+  for (i = 0; i < lim; i++) random();
+}
+
+#ifdef NOT_FOR_SUPERTUX     // use in supertux doesn't require these methods,
+                            // which are not portable to as many platforms as
+                            // SDL.  The cost is that the variability of the
+                            // initial seed is reduced to only 32 bits of
+                            // randomness, seemingly enough. PAK 060420
+//
+// srandomdev
+//
+// Many programs choose the seed value in a totally predictable manner.
+// This often causes problems.  We seed the generator using the much more
+// secure random() interface.  Note that this particular seeding
+// procedure can generate states which are impossible to reproduce by
+// calling srandom() with any value, since the succeeding terms in the
+// state buffer are no longer derived from the LC algorithm applied to
+// a fixed seed.
+
+void RandomGenerator::srandomdev()
+{
+  int fd, done;
+  size_t len;
+
+  if (rand_type == TYPE_0)
+    len = sizeof state[0];
+  else
+    len = rand_deg * sizeof state[0];
+
+  done = 0;
+  fd = open("/dev/urandom", O_RDONLY);
+  if (fd >= 0) 
+   {
+     if (read(fd, state, len) == len) done = 1;
+     close(fd);
+   }
+
+  if (!done) 
+  {
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    srandom(tv.tv_sec ^ tv.tv_usec);
+    return;
+  }
+
+  if (rand_type != TYPE_0) 
+  {
+    fptr = &state[rand_sep];
+    rptr = &state[0];
+  }
+  initialized = 1;
+}
+
+//
+// initstate
+//
+// Initialize the state information in the given array of n bytes for future
+// random number generation.  Based on the number of bytes we are given, and
+// the break values for the different R.N.G.'s, we choose the best (largest)
+// one we can and set things up for it.  srandom() is then called to
+// initialize the state information.
+//
+// Note that on return from srandom(), we set state[-1] to be the type
+// multiplexed with the current value of the rear pointer; this is so
+// successive calls to initstate() won't lose this information and will be
+// able to restart with setstate().
+//
+// Note: the first thing we do is save the current state, if any, just like
+// setstate() so that it doesn't matter when initstate is called.
+//
+// Returns a pointer to the old state.
+//
+
+char * RandomGenerator::initstate(unsigned long seed, char *arg_state, long n)
+{
+  char *ostate = (char *) (&state[-1]);
+  long *long_arg_state = (long *) arg_state;
+
+  if (rand_type == TYPE_0)
+    state[-1] = rand_type;
+  else
+    state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+
+  if (n < BREAK_0) return NULL;
+
+  if (n < BREAK_1) 
+  {
+    rand_type = TYPE_0;
+    rand_deg = DEG_0;
+    rand_sep = SEP_0;
+  } 
+  else if (n < BREAK_2) 
+  {
+    rand_type = TYPE_1;
+    rand_deg = DEG_1;
+    rand_sep = SEP_1;
+  } 
+  else if (n < BREAK_3) 
+  {
+    rand_type = TYPE_2;
+    rand_deg = DEG_2;
+    rand_sep = SEP_2;
+  } 
+  else if (n < BREAK_4) 
+  {
+    rand_type = TYPE_3;
+    rand_deg = DEG_3;
+    rand_sep = SEP_3;
+  } 
+  else 
+  {
+    rand_type = TYPE_4;
+    rand_deg = DEG_4;
+    rand_sep = SEP_4;
+  }
+  
+  state = (long *) (long_arg_state + 1); // First location
+  end_ptr = &state[rand_deg]; // Must set end_ptr before srandom
+  srandom(seed);
+
+  if (rand_type == TYPE_0)
+    long_arg_state[0] = rand_type;
+  else
+    long_arg_state[0] = MAX_TYPES * (rptr - state) + rand_type;
+
+  initialized = 1;
+  return ostate;
+}
+
+//
+// setstate
+//
+// Restore the state from the given state array.
+//
+// Note: it is important that we also remember the locations of the pointers
+// in the current state information, and restore the locations of the pointers
+// from the old state information.  This is done by multiplexing the pointer
+// location into the zeroeth word of the state information.
+//
+// Note that due to the order in which things are done, it is OK to call
+// setstate() with the same state as the current state.
+//
+// Returns a pointer to the old state information.
+//
+
+char * RandomGenerator::setstate(char *arg_state)
+{
+  long *new_state = (long *) arg_state;
+  long type = new_state[0] % MAX_TYPES;
+  long rear = new_state[0] / MAX_TYPES;
+  char *ostate = (char *) (&state[-1]);
+
+  if (rand_type == TYPE_0)
+    state[-1] = rand_type;
+  else
+    state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+
+  switch(type) 
+  {
+    case TYPE_0:
+    case TYPE_1:
+    case TYPE_2:
+    case TYPE_3:
+    case TYPE_4:
+      rand_type = type;
+      rand_deg = degrees[type];
+      rand_sep = seps[type];
+      break;
+  }
+
+  state = (long *) (new_state + 1);
+  if (rand_type != TYPE_0) 
+  {
+    rptr = &state[rear];
+    fptr = &state[(rear + rand_sep) % rand_deg];
+  }
+  end_ptr = &state[rand_deg];   // Set end_ptr too
+
+  initialized = 1;
+  return ostate;
+}
+#endif //NOT_FOR_SUPERTUX
+//
+// random:
+//
+// If we are using the trivial TYPE_0 R.N.G., just do the old linear
+// congruential bit.  Otherwise, we do our fancy trinomial stuff, which is
+// the same in all the other cases due to all the global variables that have
+// been set up.  The basic operation is to add the number at the rear pointer
+// into the one at the front pointer.  Then both pointers are advanced to
+// the next location cyclically in the table.  The value returned is the sum
+// generated, reduced to 31 bits by throwing away the "least random" low bit.
+//
+// Note: the code takes advantage of the fact that both the front and
+// rear pointers can't wrap on the same call by not testing the rear
+// pointer if the front one has wrapped.
+//
+// Returns a 31-bit random number.
+//
+
+long RandomGenerator::random()
+{
+  long i;
+  long *f, *r;
+  if (!initialized) {
+      throw std::runtime_error("uninitialized RandomGenerator object");
+  }
+
+  if (rand_type == TYPE_0) 
+  {
+    i = state[0];
+    state[0] = i = (good_rand(i)) & 0x7fffffff;
+  } 
+  else 
+  {
+    f = fptr; r = rptr;
+    *f += *r;
+    i = (*f >> 1) & 0x7fffffff; // Chucking least random bit
+    if (++f >= end_ptr) 
+    {
+      f = state;
+      ++r;
+    }
+    else if (++r >= end_ptr) 
+      r = state;
+
+    fptr = f; rptr = r;
+  }
+
+  return i;
+}
diff --git a/src/random_generator.hpp b/src/random_generator.hpp
new file mode 100644 (file)
index 0000000..3bd391d
--- /dev/null
@@ -0,0 +1,128 @@
+// $Id$
+// 
+// A strong random number generator
+//
+// Copyright (C) 2006 Allen King
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer.  
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.  
+// 3. Neither the name of the project nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission. 
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+// SUCH DAMAGE.
+
+#ifndef __RANDOM_GENERATOR__
+#define __RANDOM_GENERATOR__
+
+#include "scripting/random_generator.hpp"
+#include "script_interface.hpp"
+
+class RandomGenerator : public Scripting::RandomGenerator
+{
+private:
+// Array versions of the above information to make code run faster --
+// relies on fact that TYPE_i == i.
+    static const int TYPE_0 = 0;   // Linear congruential
+    static const int BREAK_0 = 8;
+    static const int DEG_0 = 0;
+    static const int SEP_0 = 0;
+
+    static const int TYPE_1 = 1;   // x**7 + x**3 + 1
+    static const int BREAK_1 = 32;
+    static const int DEG_1 = 7;
+    static const int SEP_1 = 3;
+
+    static const int TYPE_2 = 2;   // x**15 + x + 1
+    static const int BREAK_2 = 64;
+    static const int DEG_2 = 15;
+    static const int SEP_2 = 1;
+
+    static const int TYPE_3 = 3;   // x**31 + x**3 + 1
+    static const int BREAK_3 = 128;
+    static const int DEG_3 = 31;
+    static const int SEP_3 = 3;
+
+    static const int TYPE_4 = 4;   // x**63 + x + 1
+    static const int BREAK_4 = 256;
+    static const int DEG_4 = 63;
+    static const int SEP_4 = 1;
+
+    static const int MAX_TYPES = 5;     // Max number of types above
+
+    bool initialized;
+    long degrees[MAX_TYPES];
+    long seps [MAX_TYPES];
+    long randtbl[DEG_3 + 1];
+
+    long *fptr;
+    long *rptr;
+
+    long *state;
+    long rand_type;
+    long rand_deg;
+    long rand_sep;
+    long *end_ptr;
+
+public:
+    RandomGenerator();
+    ~RandomGenerator();
+
+// Documentation of user-visible calls:
+
+     // Initialize the RNG with a 31-bit seed
+    // if x is zero or absent, calls to time() will get a time-randomized seed
+    // the value returned is the value of the seed used.
+    int srand(int x=0);
+
+     // generate random 31-bit numbers
+    // calls to the following return a value evenly distributed between u (or
+    // 0 if not specified) and v (or RAND_MAX if not specified).  Return
+    // values may include u, but never v.
+    int rand();
+    int rand(int v);
+    int rand(int u, int v);
+    double randf(double v);
+    double randf(double u, double v);
+
+    // For Squirrel wrapper, since miniswig (and even squirrel?) doesn't
+    // support function overloading or doubles
+    int rand1i(int v) { return rand(v); }
+    int rand2i(int u, int v) { return rand(u, v); }
+    float rand1f(float v)
+      { return static_cast<float>(randf(static_cast<double>(v))); }
+    float rand2f(float u, float v)
+      { return static_cast<float>(randf(static_cast<double>(u),
+                                      static_cast<double>(v))); }
+
+//private:
+    void initialize();
+    void srandom(unsigned long x);
+//  void srandomdev();
+//  char *initstate(unsigned long seed, char *arg_state, long n);
+//  char *setstate(char *arg_state);
+    long random();
+};
+
+extern RandomGenerator systemRandom;
+
+#endif //__RANDOM_GENERATOR__
index 63cea9b..047ea82 100644 (file)
@@ -17,8 +17,8 @@
 //  along with this program; if not, write to the Free Software
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-#ifndef __PLAYER_H__
-#define __PLAYER_H__
+#ifndef __SCRIPTING_PLAYER_H__
+#define __SCRIPTING_PLAYER_H__
 
 namespace Scripting
 {
diff --git a/src/scripting/random_generator.hpp b/src/scripting/random_generator.hpp
new file mode 100644 (file)
index 0000000..ebd33b5
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id: player.hpp 3350 2006-04-16 14:43:57Z sommer $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_RANDOM_GENERATOR_H__
+#define __SCRIPTING_RANDOM_GENERATOR_H__
+
+namespace Scripting
+{
+
+class RandomGenerator
+{
+public:
+#ifndef SCRIPTING_API
+  virtual ~RandomGenerator()
+  {}
+#endif
+
+  /**
+   * Seed random number generator
+   */
+  virtual int srand(int x) = 0;
+  /**
+   * Return random number in range [0, RAND_MAX)
+   */
+  virtual int rand() = 0;
+  /**
+   * Return random number in range [0, v)
+   */
+  virtual int rand1i(int v) = 0;
+  /**
+   * Return random number in range [u, v)
+   */
+  virtual int rand2i(int u, int v) = 0;
+  /**
+   * Return random number in range [0, v)
+   */
+  virtual float rand1f(float v) = 0;
+  /**
+   * Return random number in range [u, v)
+   */
+  virtual float rand2f(float u, float v) = 0;
+};
+
+}
+
+#endif
+
index b9c9ffa..fedc64c 100644 (file)
@@ -31,6 +31,7 @@
 #include "log.hpp"
 #include "level.hpp"
 #include "physfs/physfs_stream.hpp"
+#include "../random_generator.hpp"
 
 #ifdef ENABLE_SQDBG
 #include <sqdbg/sqrdbg.h>
@@ -86,6 +87,7 @@ void init_squirrel(bool enable_debugger)
 
   // TODO remove this at some point... it shoud just be functions not an object
   expose_object(global_vm, -1, new Scripting::Level(), "Level", true);
+  expose_object(global_vm, -1, &systemRandom, "RandomGenerator", false);
   
   sq_pop(global_vm, 1);
 
index cb77fab..6f1f202 100644 (file)
@@ -1465,6 +1465,192 @@ static int FloatingImage_get_action_wrapper(HSQUIRRELVM vm)
   
 }
 
+static int RandomGenerator_release_hook(SQUserPointer ptr, int )
+{
+  Scripting::RandomGenerator* _this = reinterpret_cast<Scripting::RandomGenerator*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static int RandomGenerator_srand_wrapper(HSQUIRRELVM vm)
+{
+  Scripting::RandomGenerator* _this;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, reinterpret_cast<SQUserPointer*> (&_this), 0))) {
+    sq_throwerror(vm, _SC("'srand' called without instance"));
+    return SQ_ERROR;
+  }
+  int arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+  
+  try {
+    int return_value = _this->srand(arg0);
+  
+    sq_pushinteger(vm, return_value);
+    return 1;
+  
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'srand'"));
+    return SQ_ERROR;
+  }
+  
+}
+
+static int RandomGenerator_rand_wrapper(HSQUIRRELVM vm)
+{
+  Scripting::RandomGenerator* _this;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, reinterpret_cast<SQUserPointer*> (&_this), 0))) {
+    sq_throwerror(vm, _SC("'rand' called without instance"));
+    return SQ_ERROR;
+  }
+  
+  try {
+    int return_value = _this->rand();
+  
+    sq_pushinteger(vm, return_value);
+    return 1;
+  
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'rand'"));
+    return SQ_ERROR;
+  }
+  
+}
+
+static int RandomGenerator_rand1i_wrapper(HSQUIRRELVM vm)
+{
+  Scripting::RandomGenerator* _this;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, reinterpret_cast<SQUserPointer*> (&_this), 0))) {
+    sq_throwerror(vm, _SC("'rand1i' called without instance"));
+    return SQ_ERROR;
+  }
+  int arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+  
+  try {
+    int return_value = _this->rand1i(arg0);
+  
+    sq_pushinteger(vm, return_value);
+    return 1;
+  
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'rand1i'"));
+    return SQ_ERROR;
+  }
+  
+}
+
+static int RandomGenerator_rand2i_wrapper(HSQUIRRELVM vm)
+{
+  Scripting::RandomGenerator* _this;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, reinterpret_cast<SQUserPointer*> (&_this), 0))) {
+    sq_throwerror(vm, _SC("'rand2i' called without instance"));
+    return SQ_ERROR;
+  }
+  int arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+  int arg1;
+  if(SQ_FAILED(sq_getinteger(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not an integer"));
+    return SQ_ERROR;
+  }
+  
+  try {
+    int return_value = _this->rand2i(arg0, arg1);
+  
+    sq_pushinteger(vm, return_value);
+    return 1;
+  
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'rand2i'"));
+    return SQ_ERROR;
+  }
+  
+}
+
+static int RandomGenerator_rand1f_wrapper(HSQUIRRELVM vm)
+{
+  Scripting::RandomGenerator* _this;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, reinterpret_cast<SQUserPointer*> (&_this), 0))) {
+    sq_throwerror(vm, _SC("'rand1f' called without instance"));
+    return SQ_ERROR;
+  }
+  float arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  
+  try {
+    float return_value = _this->rand1f(arg0);
+  
+    sq_pushfloat(vm, return_value);
+    return 1;
+  
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'rand1f'"));
+    return SQ_ERROR;
+  }
+  
+}
+
+static int RandomGenerator_rand2f_wrapper(HSQUIRRELVM vm)
+{
+  Scripting::RandomGenerator* _this;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, reinterpret_cast<SQUserPointer*> (&_this), 0))) {
+    sq_throwerror(vm, _SC("'rand2f' called without instance"));
+    return SQ_ERROR;
+  }
+  float arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  float arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+  
+  try {
+    float return_value = _this->rand2f(arg0, arg1);
+  
+    sq_pushfloat(vm, return_value);
+    return 1;
+  
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'rand2f'"));
+    return SQ_ERROR;
+  }
+  
+}
+
 static int display_wrapper(HSQUIRRELVM vm)
 {
   return Scripting::display(vm);
@@ -2237,6 +2423,32 @@ void create_squirrel_instance(HSQUIRRELVM v, Scripting::FloatingImage* object, b
   sq_remove(v, -2); // remove root table
 }
 
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::RandomGenerator* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "RandomGenerator", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'RandomGenerator'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'RandomGenerator'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, RandomGenerator_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
 void register_supertux_wrapper(HSQUIRRELVM v)
 {
   using namespace Wrapper;
@@ -2852,6 +3064,53 @@ void register_supertux_wrapper(HSQUIRRELVM v)
     throw SquirrelError(v, "Couldn't register class 'FloatingImage'");
   }
 
+  // Register class RandomGenerator
+  sq_pushstring(v, "RandomGenerator", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'RandomGenerator'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "srand", -1);
+  sq_newclosure(v, &RandomGenerator_srand_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'srand'");
+  }
+
+  sq_pushstring(v, "rand", -1);
+  sq_newclosure(v, &RandomGenerator_rand_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'rand'");
+  }
+
+  sq_pushstring(v, "rand1i", -1);
+  sq_newclosure(v, &RandomGenerator_rand1i_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'rand1i'");
+  }
+
+  sq_pushstring(v, "rand2i", -1);
+  sq_newclosure(v, &RandomGenerator_rand2i_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'rand2i'");
+  }
+
+  sq_pushstring(v, "rand1f", -1);
+  sq_newclosure(v, &RandomGenerator_rand1f_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'rand1f'");
+  }
+
+  sq_pushstring(v, "rand2f", -1);
+  sq_newclosure(v, &RandomGenerator_rand2f_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'rand2f'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'RandomGenerator'");
+  }
+
 }
 
 } // end of namespace Scripting
index 8f55ffc..61d464b 100644 (file)
@@ -21,6 +21,7 @@ void create_squirrel_instance(HSQUIRRELVM v, Scripting::ScriptedObject* object,
 void create_squirrel_instance(HSQUIRRELVM v, Scripting::Text* object, bool setup_releasehook = false);
 void create_squirrel_instance(HSQUIRRELVM v, Scripting::Player* object, bool setup_releasehook = false);
 void create_squirrel_instance(HSQUIRRELVM v, Scripting::FloatingImage* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::RandomGenerator* object, bool setup_releasehook = false);
 
 }
 
index 14135a8..62e1e80 100644 (file)
@@ -8,4 +8,4 @@
 #include "player.hpp"
 #include "floating_image.hpp"
 #include "anchor_points.hpp"
-
+#include "random_generator.hpp"
index fd8856b..5b31fe3 100644 (file)
@@ -63,6 +63,7 @@
 #include "log.hpp"
 #include "options_menu.hpp"
 #include "console.hpp"
+#include "random_generator.hpp"
 
 enum MainMenuIDs {
   MNID_STARTGAME,
@@ -239,9 +240,9 @@ TitleScreen::make_tux_jump()
     if(pathBlocked)
       jumpDuration = 0.5;
     else
-      jumpDuration = float(rand() % 500 + 300) / 1000.0;
+      jumpDuration = systemRandom.randf(0.3, 0.8);
     jumpPushTimer.start(jumpDuration);
-    randomWaitTimer.start(float(rand() % 3000 + 3000) / 1000.0);
+    randomWaitTimer.start(systemRandom.randf(3.0, 6.0));
   }
 
   // Keep jump button pressed