From: Ryan Flegel Date: Wed, 26 Apr 2006 02:13:42 +0000 (+0000) Subject: Committing RandomGenerator patch from Allen King, with a few small changes X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=d963f8dc3d2c4e432d3eeecd15351169e10243da;p=supertux.git Committing RandomGenerator patch from Allen King, with a few small changes Created scripting wrapper for the random number generator - could someone check this over to make sure I did it properly? :) SVN-Revision: 3435 --- diff --git a/data/levels/world1/intro.nut b/data/levels/world1/intro.nut index e418a7f5a..03c14fd78 100644 --- a/data/levels/world1/intro.nut +++ b/data/levels/world1/intro.nut @@ -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); } } diff --git a/src/badguy/dispenser.cpp b/src/badguy/dispenser.cpp index 09f8d394e..260fbc8c0 100644 --- a/src/badguy/dispenser.cpp +++ b/src/badguy/dispenser.cpp @@ -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; diff --git a/src/badguy/kugelblitz.cpp b/src/badguy/kugelblitz.cpp index a3e21286b..8d0c77b83 100644 --- a/src/badguy/kugelblitz.cpp +++ b/src/badguy/kugelblitz.cpp @@ -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); } diff --git a/src/badguy/skullyhop.cpp b/src/badguy/skullyhop.cpp index 68be79c43..2679ee7f0 100644 --- a/src/badguy/skullyhop.cpp +++ b/src/badguy/skullyhop.cpp @@ -20,6 +20,7 @@ #include #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) { diff --git a/src/badguy/stalactite.cpp b/src/badguy/stalactite.cpp index f0ce5cfc0..ab980a011 100644 --- a/src/badguy/stalactite.cpp +++ b/src/badguy/stalactite.cpp @@ -20,6 +20,7 @@ #include #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); } diff --git a/src/badguy/zeekling.cpp b/src/badguy/zeekling.cpp index 9030ec9b0..282d1aa0d 100644 --- a/src/badguy/zeekling.cpp +++ b/src/badguy/zeekling.cpp @@ -23,6 +23,7 @@ #include #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); diff --git a/src/game_session.cpp b/src/game_session.cpp index 2f6637276..d843f5ef3 100644 --- a/src/game_session.cpp +++ b/src/game_session.cpp @@ -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 " <random_seed <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" <seekg(0); // old style w/o seed, restart at beg + log_info << "Demo file contains no random number" << std::endl; + } } void diff --git a/src/gameconfig.cpp b/src/gameconfig.cpp index 416d10432..c2d143134 100644 --- a/src/gameconfig.cpp +++ b/src/gameconfig.cpp @@ -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) { diff --git a/src/gameconfig.hpp b/src/gameconfig.hpp index 109df1a04..160278799 100644 --- a/src/gameconfig.hpp +++ b/src/gameconfig.hpp @@ -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; diff --git a/src/main.cpp b/src/main.cpp index c1bd12781..8bf4657d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 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; diff --git a/src/mainloop.cpp b/src/mainloop.cpp index 95104c6de..fb45247c7 100644 --- a/src/mainloop.cpp +++ b/src/mainloop.cpp @@ -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 << "==" <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)); } } diff --git a/src/object/gameobjs.cpp b/src/object/gameobjs.cpp index dff136d42..caf93ed7c 100644 --- a/src/object/gameobjs.cpp +++ b/src/object/gameobjs.cpp @@ -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); } diff --git a/src/object/particles.cpp b/src/object/particles.cpp index 74db7fb7f..cea6c82f8 100644 --- a/src/object/particles.cpp +++ b/src/object/particles.cpp @@ -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 diff --git a/src/object/particlesystem.cpp b/src/object/particlesystem.cpp index 56d33b834..56e4414e3 100644 --- a/src/object/particlesystem.cpp +++ b/src/object/particlesystem.cpp @@ -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; ipos.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; ipos.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); } diff --git a/src/object/particlesystem_interactive.cpp b/src/object/particlesystem_interactive.cpp index b2f55ca36..0a9228abc 100644 --- a/src/object/particlesystem_interactive.cpp +++ b/src/object/particlesystem_interactive.cpp @@ -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; ipos.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; ipos.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; diff --git a/src/object/player.cpp b/src/object/player.cpp index f7c56054a..91013b111 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -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 (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); diff --git a/src/object/skull_tile.cpp b/src/object/skull_tile.cpp index 1b6d7ea84..66e4c90ee 100644 --- a/src/object/skull_tile.cpp +++ b/src/object/skull_tile.cpp @@ -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); diff --git a/src/object/unstable_tile.cpp b/src/object/unstable_tile.cpp index 68d2bbf65..a7ac4f909 100644 --- a/src/object/unstable_tile.cpp +++ b/src/object/unstable_tile.cpp @@ -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 index 000000000..ecb95ba9d --- /dev/null +++ b/src/random_generator.cpp @@ -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 +#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 + +// +// 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 index 000000000..3bd391d42 --- /dev/null +++ b/src/random_generator.hpp @@ -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(randf(static_cast(v))); } + float rand2f(float u, float v) + { return static_cast(randf(static_cast(u), + static_cast(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__ diff --git a/src/scripting/player.hpp b/src/scripting/player.hpp index 63cea9ba5..047ea82e7 100644 --- a/src/scripting/player.hpp +++ b/src/scripting/player.hpp @@ -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 index 000000000..ebd33b51e --- /dev/null +++ b/src/scripting/random_generator.hpp @@ -0,0 +1,63 @@ +// $Id: player.hpp 3350 2006-04-16 14:43:57Z sommer $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 + diff --git a/src/scripting/squirrel_util.cpp b/src/scripting/squirrel_util.cpp index b9c9ffa35..fedc64c61 100644 --- a/src/scripting/squirrel_util.cpp +++ b/src/scripting/squirrel_util.cpp @@ -31,6 +31,7 @@ #include "log.hpp" #include "level.hpp" #include "physfs/physfs_stream.hpp" +#include "../random_generator.hpp" #ifdef ENABLE_SQDBG #include @@ -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); diff --git a/src/scripting/wrapper.cpp b/src/scripting/wrapper.cpp index cb77fabf1..6f1f20280 100644 --- a/src/scripting/wrapper.cpp +++ b/src/scripting/wrapper.cpp @@ -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 (ptr); + delete _this; + return 0; +} + +static int RandomGenerator_srand_wrapper(HSQUIRRELVM vm) +{ + Scripting::RandomGenerator* _this; + if(SQ_FAILED(sq_getinstanceup(vm, 1, reinterpret_cast (&_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 (&_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 (&_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 (&_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 (&_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 (&_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 diff --git a/src/scripting/wrapper.hpp b/src/scripting/wrapper.hpp index 8f55ffc45..61d464b7d 100644 --- a/src/scripting/wrapper.hpp +++ b/src/scripting/wrapper.hpp @@ -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); } diff --git a/src/scripting/wrapper.interface.hpp b/src/scripting/wrapper.interface.hpp index 14135a801..62e1e80b5 100644 --- a/src/scripting/wrapper.interface.hpp +++ b/src/scripting/wrapper.interface.hpp @@ -8,4 +8,4 @@ #include "player.hpp" #include "floating_image.hpp" #include "anchor_points.hpp" - +#include "random_generator.hpp" diff --git a/src/title.cpp b/src/title.cpp index fd8856b49..5b31fe3b1 100644 --- a/src/title.cpp +++ b/src/title.cpp @@ -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