mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 21:49:55 +00:00
Update to latest Monster, and minor simplification of the scripts.
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@83 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
parent
656007cf01
commit
6dcf6c565a
25 changed files with 187 additions and 276 deletions
|
@ -31,7 +31,6 @@ import std.path;
|
|||
|
||||
import monster.util.aa;
|
||||
import monster.util.string;
|
||||
import util.random;
|
||||
|
||||
import bsa.bsafile;
|
||||
|
||||
|
@ -51,9 +50,6 @@ import nif.nif;
|
|||
import core.filefinder;
|
||||
//import core.config;
|
||||
|
||||
// Random number generator
|
||||
DRand rnd;
|
||||
|
||||
// These are handles for various resources. They may refer to a file
|
||||
// in the file system, an entry in a BSA archive, or point to an
|
||||
// already loaded resource. Resource handles that are not implemented
|
||||
|
@ -129,8 +125,6 @@ struct ResourceManager
|
|||
|
||||
void initResources()
|
||||
{
|
||||
rnd = new DRand;
|
||||
|
||||
bsa = new FileFinder(config.bsaDir, "bsa", Recurse.No);
|
||||
archives.length = bsa.length;
|
||||
foreach(int i, ref BSAFile f; archives)
|
||||
|
|
|
@ -25,7 +25,7 @@ module esm.loadlevlist;
|
|||
import esm.imports;
|
||||
import esm.loadcrea;
|
||||
|
||||
import util.random;
|
||||
import monster.modules.random : randInt;
|
||||
|
||||
/*
|
||||
* Leveled lists. Since these have identical layout, I only bothered
|
||||
|
@ -103,7 +103,7 @@ struct LeveledListT(bool creature)
|
|||
|
||||
// TODO: Find out if this is indeed correct.
|
||||
// Test if no creature is to be selected
|
||||
if(rnd.randInt(0, 255) < chanceNone) return -1;
|
||||
if(randInt(0, 255) < chanceNone) return -1;
|
||||
|
||||
// Find the highest item below or equal to the Player level
|
||||
for(i=list.length-1; i>=0; i--)
|
||||
|
@ -126,7 +126,7 @@ struct LeveledListT(bool creature)
|
|||
}
|
||||
|
||||
// Select a random item
|
||||
return rnd.randInt(bottom, top);
|
||||
return randInt(bottom, top);
|
||||
}
|
||||
|
||||
void load()
|
||||
|
|
|
@ -28,7 +28,7 @@ import std.stdio;
|
|||
import core.memory;
|
||||
import esm.esmmain;
|
||||
import monster.util.string;
|
||||
import mscripts.object;
|
||||
import mscripts.setup;
|
||||
|
||||
import std.gc;
|
||||
import gcstats;
|
||||
|
|
|
@ -274,6 +274,8 @@ struct Function
|
|||
auto fd = new FuncDeclaration;
|
||||
// Parse and comile the function
|
||||
fd.parseFile(tokens, this);
|
||||
name.str = file;
|
||||
name.loc.fname = file;
|
||||
fd.resolve(mc.sc);
|
||||
fd.resolveBody();
|
||||
fd.compile();
|
||||
|
@ -287,7 +289,7 @@ struct Function
|
|||
|
||||
private:
|
||||
|
||||
static const char[] int_class = "class _Func_Internal_;";
|
||||
static const char[] int_class = "class _ScriptFile_;";
|
||||
static MonsterClass int_mc;
|
||||
static MonsterObject *int_mo;
|
||||
}
|
||||
|
|
|
@ -338,6 +338,13 @@ abstract class Scope
|
|||
void registerImport(MonsterClass mc)
|
||||
{ registerImport(new ImportHolder(mc)); }
|
||||
|
||||
// Even more user-friendly version. Takes a list of class names.
|
||||
void registerImport(char[][] cls ...)
|
||||
{
|
||||
foreach(c; cls)
|
||||
registerImport(MonsterClass.find(c));
|
||||
}
|
||||
|
||||
// Used for summing up stack level. Redeclared in StackScope.
|
||||
int getTotLocals() { return 0; }
|
||||
int getLocals() { assert(0); }
|
||||
|
|
|
@ -3,6 +3,7 @@ module monster.modules.all;
|
|||
import monster.modules.io;
|
||||
import monster.modules.timer;
|
||||
import monster.modules.frames;
|
||||
import monster.modules.random;
|
||||
import monster.modules.threads;
|
||||
|
||||
void initAllModules()
|
||||
|
@ -10,5 +11,6 @@ void initAllModules()
|
|||
initIOModule();
|
||||
initTimerModule();
|
||||
initFramesModule();
|
||||
initThreadModule;
|
||||
initThreadModule();
|
||||
initRandomModule();
|
||||
}
|
||||
|
|
49
monster/modules/random.d
Normal file
49
monster/modules/random.d
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
// This module provides simple random number generation. Since this is
|
||||
// intended for game development, speed and simplicity is favored over
|
||||
// flexibility and random number quality.
|
||||
module monster.modules.random;
|
||||
|
||||
import monster.monster;
|
||||
import std.random;
|
||||
|
||||
const char[] moduleDef =
|
||||
"module random;
|
||||
|
||||
native uint rand(); // Return a number between 0 and uint.max, inclusive
|
||||
native float frand(); // Return a number between 0 and 1, inclusive
|
||||
|
||||
// Return a random number between a and b, inclusive. Allows negative
|
||||
// numbers, and works with a>b, a<b and a==b
|
||||
native int randInt(int a, int b);
|
||||
"; //"
|
||||
|
||||
const float _frandFactor = 1.0/uint.max;
|
||||
|
||||
// Return a random integer between a and b, inclusive.
|
||||
int randInt(int a, int b)
|
||||
out(result)
|
||||
{
|
||||
// Result must be in range m <= result <= M, where m=min(a,b) and M=max(a,b)
|
||||
if(b >= a) assert( (a <= result) && (result <= b) );
|
||||
else if(a > b) assert( (b <= result) && (result <= a) );
|
||||
}
|
||||
body
|
||||
{
|
||||
if(a>b) return cast(int)(rand() % (a-b+1)) + b;
|
||||
else if(b>a) return cast(int)(rand() % (b-a+1)) + a;
|
||||
else return a;
|
||||
}
|
||||
|
||||
void initRandomModule()
|
||||
{
|
||||
static MonsterClass mc;
|
||||
if(mc !is null) return;
|
||||
|
||||
mc = new MonsterClass(MC.String, moduleDef, "random");
|
||||
|
||||
mc.bind("rand", { stack.pushInt(rand()); });
|
||||
mc.bind("frand", { stack.pushFloat(rand()*_frandFactor); });
|
||||
mc.bind("randInt", { stack.pushInt(randInt(stack.popInt,
|
||||
stack.popInt)); });
|
||||
}
|
8
monster/modules/random.mn
Normal file
8
monster/modules/random.mn
Normal file
|
@ -0,0 +1,8 @@
|
|||
module random;
|
||||
|
||||
native uint rand(); // Return a number between 0 and uint.max, inclusive
|
||||
native float frand(); // Return a number between 0 and 1, inclusive
|
||||
|
||||
// Return a random number between a and b, inclusive. Allows negative
|
||||
// numbers, and works with a>b, a<b and a==b
|
||||
native int randInt(int a, int b);
|
|
@ -28,20 +28,12 @@ native restart();
|
|||
|
||||
// Call a (paused) thread directly - returns when the thread exits or
|
||||
// calls an idle function.
|
||||
idle resume();
|
||||
idle call();
|
||||
|
||||
// Wait for a thread to finish. Will not return until the thread is
|
||||
// dead.
|
||||
idle wait();
|
||||
|
||||
// Call a function as a thread
|
||||
thread call(char[] name)
|
||||
{
|
||||
var t = create(name);
|
||||
t.resume();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Start a function as a thread in the background
|
||||
thread start(char[] name)
|
||||
{
|
||||
|
@ -51,6 +43,18 @@ thread start(char[] name)
|
|||
}
|
||||
"; //"
|
||||
|
||||
/*
|
||||
The char[] name stuff above will of course be replaced with real
|
||||
function pointers once those are done. We will also add:
|
||||
|
||||
function() wrap(function f())
|
||||
{
|
||||
var t = create(f);
|
||||
return {{ t.call(); }
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
MonsterObject *trdSing;
|
||||
|
||||
class Kill : IdleFunction
|
||||
|
@ -134,49 +138,34 @@ void create()
|
|||
nd = cthread.fstack.list.getNext(nd);
|
||||
auto mo = nd.obj;
|
||||
|
||||
// Find the function
|
||||
auto fn = mo.cls.findFunction(name);
|
||||
|
||||
if(fn.paramSize > 0)
|
||||
fail("create(): function " ~ name ~ " cannot have parameters");
|
||||
|
||||
// Create a new thread
|
||||
Thread *trd = Thread.getNew();
|
||||
|
||||
// Schedule the thread run the next frame
|
||||
trd.pushFunc(fn, mo);
|
||||
assert(trd.isPaused);
|
||||
|
||||
// This will mess with the stack frame though, so set it up
|
||||
// correctly.
|
||||
trd.fstack.cur.frame = stack.getStartInt(0);
|
||||
cthread.fstack.restoreFrame();
|
||||
auto trd = mo.thread(name);
|
||||
|
||||
stack.pushObject(createObj(trd));
|
||||
}
|
||||
|
||||
// Resume is used to restore a thread that was previously paused. It
|
||||
// will enter the thread immediately, like call. If you wish to run it
|
||||
// later, use restart instead.
|
||||
class Resume : IdleFunction
|
||||
// Call is used to restore a thread that was previously paused. It
|
||||
// will enter the thread immediately, like a normal function call, but
|
||||
// it will still run in its own thread. If you only wish to schedule
|
||||
// it for later, use restart instead.
|
||||
class Call : IdleFunction
|
||||
{
|
||||
override:
|
||||
IS initiate(Thread *t)
|
||||
{
|
||||
if(params.obj is trdSing)
|
||||
fail("Cannot use resume() on our own thread.");
|
||||
fail("Cannot use call() on our own thread.");
|
||||
|
||||
// Get the thread we're resuming
|
||||
auto trd = getOwner();
|
||||
|
||||
if(trd is t)
|
||||
fail("Cannot use resume() on our own thread.");
|
||||
fail("Cannot use call() on our own thread.");
|
||||
|
||||
if(trd.isDead)
|
||||
fail("Cannot resume a dead thread.");
|
||||
fail("Cannot call a dead thread.");
|
||||
|
||||
if(!trd.isPaused)
|
||||
fail("Can only use resume() on paused threads");
|
||||
fail("Can only use call() on paused threads");
|
||||
|
||||
// Background the current thread. Move it to the pause list
|
||||
// first, so background doesn't inadvertently delete it.
|
||||
|
@ -234,18 +223,7 @@ class Wait : IdleFunction
|
|||
}
|
||||
|
||||
void restart()
|
||||
{
|
||||
auto trd = getOwner();
|
||||
|
||||
if(trd.isDead)
|
||||
fail("Cannot restart a dead thread");
|
||||
|
||||
if(!trd.isPaused)
|
||||
fail("Can only use restart() on paused threads");
|
||||
|
||||
// Move to the runlist
|
||||
trd.moveTo(scheduler.runNext);
|
||||
}
|
||||
{ getOwner().restart(); }
|
||||
|
||||
void isDead()
|
||||
{ stack.pushBool(getOwner().isDead); }
|
||||
|
@ -270,7 +248,7 @@ void initThreadModule()
|
|||
trdSing = _threadClass.getSing();
|
||||
|
||||
_threadClass.bind("kill", new Kill);
|
||||
_threadClass.bind("resume", new Resume);
|
||||
_threadClass.bind("call", new Call);
|
||||
_threadClass.bind("pause", new Pause);
|
||||
_threadClass.bind("wait", new Wait);
|
||||
|
||||
|
|
|
@ -175,8 +175,6 @@ struct BufferList(int size)
|
|||
void free(void* p) { remove(cast(ValuePtr)p); }
|
||||
}
|
||||
|
||||
import std.stdio;
|
||||
|
||||
struct Buffers
|
||||
{
|
||||
static:
|
||||
|
@ -204,8 +202,7 @@ struct Buffers
|
|||
// Too large for our lists - just use malloc
|
||||
else
|
||||
{
|
||||
writefln("WARNING: using malloc for %s ints (%s bytes)",
|
||||
size, size*int.sizeof);
|
||||
//writefln("WARNING: using malloc for %s ints (%s bytes)", size, size*int.sizeof);
|
||||
return ( cast(int*)malloc(size*int.sizeof) )[0..size];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,9 @@ struct StackPoint
|
|||
name = func.name.str;
|
||||
|
||||
if(isIdle) type = "idle";
|
||||
else type = "function";
|
||||
else if(isNormal) type = "script";
|
||||
else if(isNative) type = "native";
|
||||
else assert(0);
|
||||
}
|
||||
|
||||
// Function location and name
|
||||
|
@ -322,8 +324,17 @@ struct FunctionStack
|
|||
{
|
||||
char[] res;
|
||||
|
||||
int i;
|
||||
foreach(ref c; list)
|
||||
res = c.toString ~ '\n' ~ res;
|
||||
{
|
||||
char[] msg;
|
||||
if(i == 0)
|
||||
msg = " (<---- current function)";
|
||||
else if(i == list.length-1)
|
||||
msg = " (<---- start of function stack)";
|
||||
res = c.toString ~ msg ~ '\n' ~ res;
|
||||
i++;
|
||||
}
|
||||
|
||||
return "Trace:\n" ~ res;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import monster.vm.thread;
|
|||
import monster.vm.error;
|
||||
import monster.vm.mclass;
|
||||
import monster.vm.arrays;
|
||||
import monster.vm.stack;
|
||||
|
||||
import monster.util.freelist;
|
||||
import monster.util.list;
|
||||
|
@ -34,6 +35,7 @@ import monster.util.list;
|
|||
import monster.compiler.states;
|
||||
import monster.compiler.variables;
|
||||
import monster.compiler.scopes;
|
||||
import monster.compiler.functions;
|
||||
|
||||
import std.string;
|
||||
import std.stdio;
|
||||
|
@ -262,6 +264,44 @@ struct MonsterObject
|
|||
cls.findFunction(name).call(this);
|
||||
}
|
||||
|
||||
// Create a paused thread that's set up to call the given
|
||||
// function. It must be started with Thread.call() or
|
||||
// Thread.restart().
|
||||
Thread *thread(char[] name)
|
||||
{ return thread(cls.findFunction(name)); }
|
||||
Thread *thread(Function *fn)
|
||||
{
|
||||
assert(fn !is null);
|
||||
if(fn.paramSize > 0)
|
||||
fail("thread(): function " ~ fn.name.str ~ " cannot have parameters");
|
||||
|
||||
Thread *trd = Thread.getNew();
|
||||
|
||||
// Schedule the function to run the next frame
|
||||
trd.pushFunc(fn, this);
|
||||
assert(trd.isPaused);
|
||||
assert(trd.fstack.cur !is null);
|
||||
|
||||
// pushFunc will mess with the stack frame though, so fix it.
|
||||
trd.fstack.cur.frame = stack.getStart();
|
||||
if(cthread !is null)
|
||||
cthread.fstack.restoreFrame();
|
||||
|
||||
return trd;
|
||||
}
|
||||
|
||||
// Create a thread containing the function and schedule it to start
|
||||
// the next frame
|
||||
Thread *start(char[] name)
|
||||
{ return start(cls.findFunction(name)); }
|
||||
Thread *start(Function *fn)
|
||||
{
|
||||
assert(fn !is null);
|
||||
auto trd = thread(fn);
|
||||
trd.restart();
|
||||
return trd;
|
||||
}
|
||||
|
||||
// Call a function non-virtually. In other words, ignore
|
||||
// derived objects.
|
||||
void nvcall(char[] name)
|
||||
|
|
|
@ -139,12 +139,9 @@ struct CodeStack
|
|||
}
|
||||
|
||||
// Get the pointer from the start of the stack
|
||||
int *getStartInt(int pos)
|
||||
int *getStart()
|
||||
{
|
||||
if(pos < 0 || pos >= getPos)
|
||||
fail("CodeStack.getStartInt() pointer out of range");
|
||||
|
||||
return &data[pos];
|
||||
return cast(int*)data.ptr;
|
||||
}
|
||||
|
||||
// Get the pointer to an int at the given position backwards from
|
||||
|
|
|
@ -124,6 +124,20 @@ struct Thread
|
|||
return cn;
|
||||
}
|
||||
|
||||
// Schedule the function to run the next frame. Can only be used on
|
||||
// paused threads.
|
||||
void restart()
|
||||
{
|
||||
if(isDead)
|
||||
fail("Cannot restart a dead thread");
|
||||
|
||||
if(!isPaused)
|
||||
fail("Can only use restart() on paused threads");
|
||||
|
||||
// Move to the runlist
|
||||
moveTo(scheduler.runNext);
|
||||
}
|
||||
|
||||
// Stop the thread and return it to the freelist
|
||||
void kill()
|
||||
{
|
||||
|
@ -238,6 +252,7 @@ struct Thread
|
|||
bool isTransient() { return list is &scheduler.transient; }
|
||||
bool isRunning() { return cthread is this; }
|
||||
bool isDead() { return list is null; }
|
||||
bool isAlive() { return !isDead; }
|
||||
bool isPaused() { return list is &scheduler.paused; }
|
||||
|
||||
// Get the next node in the freelist
|
||||
|
@ -293,6 +308,7 @@ struct Thread
|
|||
|
||||
// Background the thread
|
||||
background();
|
||||
assert(cthread is null);
|
||||
}
|
||||
|
||||
// Put this thread in the background. Acquires the stack and
|
||||
|
@ -401,6 +417,8 @@ struct Thread
|
|||
if(fstack.cur !is null)
|
||||
fl = fstack.cur.getFloc();
|
||||
|
||||
msg ~= '\n' ~ fstack.toString();
|
||||
|
||||
.fail(msg, fl);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
*/
|
||||
|
||||
singleton Config : Object;
|
||||
singleton Config;
|
||||
|
||||
// Only some config options have been moved into Monster. Key bindings
|
||||
// and other low-level settings are still handled in D.
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// Small script that prints the FPS to screen with regular intervals.
|
||||
|
||||
import io, timer, frames;
|
||||
import frames;
|
||||
|
||||
// Sleep one frame. This makes sure that we won't start running until
|
||||
// the rendering begins. Not critically important, but it prevents the
|
||||
// first printed value from being 'nan'.
|
||||
fsleep(1);
|
||||
|
||||
// counter and totalTime (in the 'frames' module) are updated
|
||||
// automatically by the system.
|
||||
ulong lastFrame = counter;
|
||||
float lastTime = totalTime;
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
// An object that exists inside a cell. All cell objects must have a
|
||||
// position in space.
|
||||
class GameObject : Object;
|
||||
class GameObject;
|
||||
|
||||
// Is this object placed in a cell? isPlaced is true if the object is
|
||||
// displayed inside a cell with a mesh and given 3D coordinates, and
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
// Contains all the game settings (GMST) variables of Morrowind,
|
||||
// Tribunal and Bloodmoon. Based on "Morrowind Scripting for Dummies"
|
||||
// (v9).
|
||||
singleton GMST : Object;
|
||||
singleton GMST;
|
||||
|
||||
// Most of the comments are copied from MSfD. A bit of cleanup is
|
||||
// still needed.
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (object.mn) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
// This is the base class of all OpenMW Monster classes.
|
||||
class Object;
|
||||
|
||||
import io, timer;
|
||||
|
||||
// Get a random number between a and b (inclusive)
|
||||
native int randInt(int a, int b);
|
|
@ -21,16 +21,13 @@
|
|||
|
||||
*/
|
||||
|
||||
module mscripts.object;
|
||||
module mscripts.setup;
|
||||
|
||||
import monster.monster;
|
||||
import monster.compiler.scopes : global;
|
||||
import monster.modules.timer;
|
||||
|
||||
import std.stdio;
|
||||
import std.date;
|
||||
import core.resource : rnd;
|
||||
import core.config;
|
||||
import sound.music;
|
||||
|
||||
// Set up the base Monster classes we need in OpenMW
|
||||
void initMonsterScripts()
|
||||
|
@ -43,17 +40,13 @@ void initMonsterScripts()
|
|||
vm.addPath("mscripts/gameobjects/");
|
||||
vm.addPath("mscripts/sound/");
|
||||
|
||||
// Make sure the Object class is loaded
|
||||
auto mc = new MonsterClass("Object", "object.mn");
|
||||
// Import some modules into the global scope, so we won't have to
|
||||
// import them manually in each script.
|
||||
global.registerImport("io", "random", "timer");
|
||||
|
||||
// Get the Config singleton object
|
||||
config.mo = (new MonsterClass("Config")).getSing();
|
||||
|
||||
// Bind various functions
|
||||
mc.bind("randInt",
|
||||
{ stack.pushInt(rnd.randInt
|
||||
(stack.popInt,stack.popInt));});
|
||||
|
||||
// Run the fps ticker
|
||||
vm.run("fpsticker.mn");
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
fade in and fade out, and adjust volume.
|
||||
*/
|
||||
|
||||
class Jukebox : Object;
|
||||
class Jukebox;
|
||||
|
||||
// Between 0 (off) and 1 (full volume)
|
||||
float fadeLevel = 0.0;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
*/
|
||||
|
||||
// This class controls all the music.
|
||||
singleton Music : Object;
|
||||
singleton Music;
|
||||
|
||||
// Create one jukebox for normal music, and one for battle music. This
|
||||
// way we can pause / fade out one while the other resumes / fades in.
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// A short example script
|
||||
|
||||
import io, timer;
|
||||
|
||||
sleep(6);
|
||||
|
||||
while(true)
|
||||
|
|
2
openmw.d
2
openmw.d
|
@ -43,7 +43,7 @@ import core.config;
|
|||
|
||||
import monster.util.string;
|
||||
import monster.vm.mclass;
|
||||
import mscripts.object;
|
||||
import mscripts.setup;
|
||||
|
||||
import sound.audio;
|
||||
|
||||
|
|
155
util/random.d
155
util/random.d
|
@ -1,155 +0,0 @@
|
|||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (random.d) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
module util.random;
|
||||
|
||||
private import std.date;
|
||||
private import std.random;
|
||||
|
||||
abstract class Random
|
||||
{
|
||||
static const double scale = 1.0/uint.max;
|
||||
|
||||
// Initialize from current time
|
||||
this() { initialize(); }
|
||||
|
||||
// Initialize from parameter
|
||||
this(long seed) { initialize(seed); }
|
||||
|
||||
// Reinitialize with seed
|
||||
abstract void initialize(long newSeed);
|
||||
|
||||
// Produce random numbers between 0 and uint.max
|
||||
abstract uint rand();
|
||||
|
||||
// Default is to initialize using current time as seed
|
||||
void initialize() { initialize(getUTCtime()); }
|
||||
|
||||
// Produce a uniform random number between 0 and 1
|
||||
double random()
|
||||
{
|
||||
return rand() * scale;
|
||||
}
|
||||
|
||||
// Return a uniform random number between a and b. Works for the
|
||||
// both the cases a < b and a > b.
|
||||
double random(double a, double b)
|
||||
in
|
||||
{
|
||||
// We only disallow nan parameters
|
||||
assert(a <>= b);
|
||||
}
|
||||
out(result)
|
||||
{
|
||||
// Result must be in range m < result < M, where m=min(a,b) and M=max(a,b)
|
||||
if(b > a) assert( (a < result) && (result < b) );
|
||||
else if(a > b) assert( (b < result) && (result < a) );
|
||||
}
|
||||
body
|
||||
{ return random()*(b - a) + a; }
|
||||
|
||||
// Return a random integer between a and b, inclusive.
|
||||
int randInt(int a, int b)
|
||||
out(result)
|
||||
{
|
||||
// Result must be in range m <= result <= M, where m=min(a,b) and M=max(a,b)
|
||||
if(b >= a) assert( (a <= result) && (result <= b) );
|
||||
else if(a > b) assert( (b <= result) && (result <= a) );
|
||||
}
|
||||
body
|
||||
{
|
||||
if(a>b) return cast(int)(rand() % (a-b+1)) + b;
|
||||
else if(b>a) return cast(int)(rand() % (b-a+1)) + a;
|
||||
else return a;
|
||||
}
|
||||
|
||||
// Allow using "function call" syntax:
|
||||
//
|
||||
// Random ran = new Random1;
|
||||
// double d = ran(); // Calls ran.random()
|
||||
// d = ran(a,b); // Calls ran.random(a,b);
|
||||
double opCall() { return random(); }
|
||||
double opCall(double a, double b) { return random(a, b); }
|
||||
|
||||
// Return the seed originally given the object
|
||||
long getSeed() {return origSeed;}
|
||||
|
||||
protected:
|
||||
long origSeed; // Value used to seed the generator
|
||||
}
|
||||
|
||||
// Uses the standard library generator
|
||||
class DRand : Random
|
||||
{
|
||||
// Initialize from current time
|
||||
this() { super(); }
|
||||
|
||||
// Initialize from parameter
|
||||
this(long seed) { super(seed); }
|
||||
|
||||
uint rand() { return std.random.rand(); }
|
||||
|
||||
void initialize(long newSeed)
|
||||
{
|
||||
origSeed = newSeed;
|
||||
rand_seed(cast(uint)newSeed, 0);
|
||||
}
|
||||
|
||||
alias Random.initialize initialize;
|
||||
|
||||
unittest
|
||||
{
|
||||
struct tmp { import std.stdio; }
|
||||
alias tmp.writef writef;
|
||||
alias tmp.writefln writefln;
|
||||
|
||||
writefln("Unittest for class DRand");
|
||||
|
||||
DRand ran = new DRand;
|
||||
writefln("Seed (from time) = ", ran.getSeed());
|
||||
|
||||
// Take a look at some numbers on screen
|
||||
writefln("Some random numbers in [0,1]:");
|
||||
for(int i=0; i<10; i++)
|
||||
writef(" ", ran());
|
||||
|
||||
ran = new DRand(0);
|
||||
writefln("\nNew seed (preset) = ", ran.getSeed());
|
||||
|
||||
// Take a look at some numbers on screen
|
||||
writefln("Some random numbers in [0,1]:");
|
||||
for(int i=0; i<10; i++)
|
||||
writef(" ", ran());
|
||||
|
||||
// Check that all interfaces work (compile time)
|
||||
ran();
|
||||
ran(1,2);
|
||||
ran.random();
|
||||
ran.random(3,4);
|
||||
ran.initialize();
|
||||
ran.initialize(10);
|
||||
ran.randInt(-3,5);
|
||||
|
||||
writefln("\nEnd of unittest for class DRand\n");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue