2
0
Эх сурвалжийг харах

Refactored BRL.Random. Added BRL.RandomDefault.

Brucey 5 жил өмнө
parent
commit
57a00eb7bd

+ 134 - 104
random.mod/random.bmx

@@ -6,12 +6,14 @@ bbdoc: Math/Random numbers
 End Rem
 End Rem
 Module BRL.Random
 Module BRL.Random
 
 
-ModuleInfo "Version: 1.07"
+ModuleInfo "Version: 1.08"
 ModuleInfo "Author: Mark Sibly, Floyd"
 ModuleInfo "Author: Mark Sibly, Floyd"
 ModuleInfo "License: zlib/libpng"
 ModuleInfo "License: zlib/libpng"
 ModuleInfo "Copyright: Blitz Research Ltd"
 ModuleInfo "Copyright: Blitz Research Ltd"
 ModuleInfo "Modserver: BRL"
 ModuleInfo "Modserver: BRL"
 
 
+ModuleInfo "History: 1.08"
+ModuleInfo "History: New API to enable custom random number generators."
 ModuleInfo "History: 1.07"
 ModuleInfo "History: 1.07"
 ModuleInfo "History: Added support for multiple generators"
 ModuleInfo "History: Added support for multiple generators"
 ModuleInfo "History: 1.06"
 ModuleInfo "History: 1.06"
@@ -24,15 +26,76 @@ Import BRL.Threads
 ?
 ?
 
 
 Private
 Private
-Const RND_A:Int=48271,RND_M:Int=2147483647,RND_Q:Int=44488,RND_R:Int=3399
+
+Global GlobalRandom:TRandom
+
+Global random_factories:TRandomFactory
+
+Public
+
+Type TRandomFactory
+	Field _succ:TRandomFactory
+	
+	Method New()
+		If random_factories <> Null Then
+			Throw "Random already installed : " + random_factories.GetName()
+		End If
+		_succ=random_factories
+		random_factories=Self
+	End Method
+	
+	Method Init()
+		GlobalRandom = Create()
+	End Method
+	
+	Method GetName:String() Abstract
+	
+	Method Create:TRandom(seed:Int) Abstract
+
+	Method Create:TRandom() Abstract
+		
+End Type
+
+Private
 Global LastNewMs:Int = MilliSecs()
 Global LastNewMs:Int = MilliSecs()
 Global SimultaneousNewCount:Int = 0
 Global SimultaneousNewCount:Int = 0
 ? Threaded
 ? Threaded
 Global NewRandomMutex:TMutex = TMutex.Create()
 Global NewRandomMutex:TMutex = TMutex.Create()
 ?
 ?
-Global GlobalRandom:TRandom = New TRandom
 Public
 Public
 
 
+Function GenerateSeed:Int()
+	? Threaded
+	NewRandomMutex.Lock
+	?
+	Local currentMs:Int = MilliSecs()
+	Local auxSeed:Int
+	If currentMs = LastNewMs Then
+		SimultaneousNewCount :+ 1
+		auxSeed = SimultaneousNewCount
+	Else
+		LastNewMs = currentMs
+		SimultaneousNewCount = 0
+		auxSeed = 0
+	End If
+	? Threaded
+	NewRandomMutex.Unlock
+	?
+	
+	Function ReverseBits:Int(i:Int)
+		If i = 0 Then Return 0
+		Local r:Int
+		For Local b:Int = 0 Until 8 * SizeOf i
+			r :Shl 1
+			r :| i & 1
+			i :Shr 1
+		Next
+		Return r
+	End Function
+	' left-shift before reversing because SeedRnd ignores the most significant bit
+	Return currentMs ~ ReverseBits(auxSeed Shl 1)
+End Function
+
 Rem
 Rem
 bbdoc: Random number generator
 bbdoc: Random number generator
 about:
 about:
@@ -41,83 +104,17 @@ random number generators can be used in parallel.
 End Rem
 End Rem
 Type TRandom
 Type TRandom
 	
 	
-	Private
-	
-	Field rnd_state:Int=$1234
-	
-	Public
-	
-	Rem
-	bbdoc: Create a new random number generator
-	End Rem
-	Method New()
-		? Threaded
-		NewRandomMutex.Lock
-		?
-		Local currentMs:Int = MilliSecs()
-		Local auxSeed:Int
-		If currentMs = LastNewMs Then
-			SimultaneousNewCount :+ 1
-			auxSeed = SimultaneousNewCount
-		Else
-			LastNewMs = currentMs
-			SimultaneousNewCount = 0
-			auxSeed = 0
-		End If
-		? Threaded
-		NewRandomMutex.Unlock
-		?
-		
-		Function ReverseBits:Int(i:Int)
-			If i = 0 Then Return 0
-			Local r:Int
-			For Local b:Int = 0 Until 8 * SizeOf i
-				r :Shl 1
-				r :| i & 1
-				i :Shr 1
-			Next
-			Return r
-		End Function
-		' left-shift before reversing because SeedRnd ignores the most significant bit
-		Local seed:Int = currentMs ~ ReverseBits(auxSeed Shl 1)
-		SeedRnd seed
-	End Method
-	
-	Rem
-	bbdoc: Create a new random number generator with the specified seed
-	End Rem
-	Method New(seed:Int)
-		SeedRnd seed
-	End Method
-	
 	Rem
 	Rem
 	bbdoc: Generate random float
 	bbdoc: Generate random float
 	returns: A random float in the range 0 (inclusive) to 1 (exclusive)
 	returns: A random float in the range 0 (inclusive) to 1 (exclusive)
 	End Rem
 	End Rem
-	Method RndFloat:Float()
-		rnd_state=RND_A*(rnd_state Mod RND_Q)-RND_R*(rnd_state/RND_Q)
-		If rnd_state<0 rnd_state=rnd_state+RND_M
-		Return (rnd_state & $ffffff0) / 268435456#  'divide by 2^28
-	End Method
+	Method RndFloat:Float() Abstract
 	
 	
 	Rem
 	Rem
 	bbdoc: Generate random double
 	bbdoc: Generate random double
 	returns: A random double in the range 0 (inclusive) to 1 (exclusive)
 	returns: A random double in the range 0 (inclusive) to 1 (exclusive)
 	End Rem
 	End Rem
-	Method RndDouble:Double()
-		Const TWO27! = 134217728.0		'2 ^ 27
-		Const TWO29! = 536870912.0		'2 ^ 29
-	
-		rnd_state=RND_A*(rnd_state Mod RND_Q)-RND_R*(rnd_state/RND_Q)
-		If rnd_state<0 rnd_state=rnd_state+RND_M
-		Local r_hi! = rnd_state & $1ffffffc
-	
-		rnd_state=RND_A*(rnd_state Mod RND_Q)-RND_R*(rnd_state/RND_Q)
-		If rnd_state<0 rnd_state=rnd_state+RND_M
-		Local r_lo! = rnd_state & $1ffffff8
-	
-		Return (r_hi + r_lo/TWO27)/TWO29
-	End Method
+	Method RndDouble:Double() Abstract
 	
 	
 	Rem
 	Rem
 	bbdoc: Generate random double
 	bbdoc: Generate random double
@@ -126,15 +123,12 @@ Type TRandom
 	The optional parameters allow you to use Rnd in 3 ways:
 	The optional parameters allow you to use Rnd in 3 ways:
 	
 	
 	[ @Format | @Result
 	[ @Format | @Result
-	* &Rnd() | Random double in the range 0 (inclusive) to 1 (exclusive)
-	* &Rnd(_x_) | Random double in the range 0 (inclusive) to n (exclusive)
-	* &Rnd(_x,y_) | Random double in the range x (inclusive) to y (exclusive)
+	* `Rnd()` | Random double in the range 0 (inclusive) to 1 (exclusive)
+	* `Rnd(x)` | Random double in the range 0 (inclusive) to n (exclusive)
+	* `Rnd(x,y)` | Random double in the range x (inclusive) to y (exclusive)
 	]
 	]
 	End Rem
 	End Rem
-	Method Rnd:Double(minValue:Double = 1, maxValue:Double = 0)
-		If maxValue>minValue Return RndDouble()*(maxValue-minValue)+minValue
-		Return RndDouble()*(minValue-maxValue)+maxValue
-	End Method
+	Method Rnd:Double(minValue:Double = 1, maxValue:Double = 0) Abstract
 	
 	
 	Rem
 	Rem
 	bbdoc: Generate random integer
 	bbdoc: Generate random integer
@@ -143,23 +137,16 @@ Type TRandom
 	The optional parameter allows you to use #Rand in 2 ways:
 	The optional parameter allows you to use #Rand in 2 ways:
 	
 	
 	[ @Format | @Result
 	[ @Format | @Result
-	* &Rand(x) | Random integer in the range 1 to x (inclusive)
-	* &Rand(x,y) | Random integer in the range x to y (inclusive)
+	* `Rand(x)` | Random integer in the range 1 to x (inclusive)
+	* `Rand(x,y)` | Random integer in the range x to y (inclusive)
 	]
 	]
 	End Rem
 	End Rem
-	Method Rand:Int(minValue:Int, maxValue:Int = 1)
-		Local Range:Int=maxValue-minValue
-		If Range>0 Return Int( RndDouble()*(1+Range) )+minValue
-		Return Int( RndDouble()*(1-Range) )+maxValue
-	End Method
+	Method Rand:Int(minValue:Int, maxValue:Int = 1) Abstract
 	
 	
 	Rem
 	Rem
 	bbdoc: Set random number generator seed
 	bbdoc: Set random number generator seed
 	End Rem
 	End Rem
-	Method SeedRnd(seed:Int)
-		rnd_state=seed & $7fffffff             				'enforces rnd_state >= 0
-		If rnd_state=0 Or rnd_state=RND_M rnd_state=$1234	'disallow 0 and M
-	End Method
+	Method SeedRnd(seed:Int) Abstract
 	
 	
 	Rem
 	Rem
 	bbdoc: Get random number generator seed
 	bbdoc: Get random number generator seed
@@ -167,18 +154,42 @@ Type TRandom
 	about: Used in conjunction with SeedRnd, RndSeed allows you to reproduce sequences of random
 	about: Used in conjunction with SeedRnd, RndSeed allows you to reproduce sequences of random
 	numbers.
 	numbers.
 	End Rem
 	End Rem
-	Method RndSeed:Int()
-		Return rnd_state
-	End Method
+	Method RndSeed:Int() Abstract
 
 
 End Type
 End Type
 
 
+Rem
+bbdoc: Creates a new TRandom instance.
+End Rem
+Function CreateRandom:TRandom()
+	If GlobalRandom Then
+		Return random_factories.Create()
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
+End Function
+
+Rem
+bbdoc: Creates a new TRandom instance with the given @seed.
+End Rem
+Function CreateRandom:TRandom(seed:Int)
+	If GlobalRandom Then
+		Return random_factories.Create(seed)
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
+End Function
+
 Rem
 Rem
 bbdoc: Generate random float
 bbdoc: Generate random float
 returns: A random float in the range 0 (inclusive) to 1 (exclusive)
 returns: A random float in the range 0 (inclusive) to 1 (exclusive)
 End Rem
 End Rem
 Function RndFloat:Float()
 Function RndFloat:Float()
-	Return GlobalRandom.RndFloat()
+	If GlobalRandom Then
+		Return GlobalRandom.RndFloat()
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -186,7 +197,11 @@ bbdoc: Generate random double
 returns: A random double in the range 0 (inclusive) to 1 (exclusive)
 returns: A random double in the range 0 (inclusive) to 1 (exclusive)
 End Rem
 End Rem
 Function RndDouble:Double()
 Function RndDouble:Double()
-	Return GlobalRandom.RndDouble()
+	If GlobalRandom Then
+		Return GlobalRandom.RndDouble()
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -196,13 +211,17 @@ about:
 The optional parameters allow you to use Rnd in 3 ways:
 The optional parameters allow you to use Rnd in 3 ways:
 
 
 [ @Format | @Result
 [ @Format | @Result
-* &Rnd() | Random double in the range 0 (inclusive) to 1 (exclusive)
-* &Rnd(_x_) | Random double in the range 0 (inclusive) to n (exclusive)
-* &Rnd(_x,y_) | Random double in the range x (inclusive) to y (exclusive)
+* `Rnd()` | Random double in the range 0 (inclusive) to 1 (exclusive)
+* `Rnd(x)` | Random double in the range 0 (inclusive) to n (exclusive)
+* `Rnd(x,y)` | Random double in the range x (inclusive) to y (exclusive)
 ]
 ]
 End Rem
 End Rem
 Function Rnd:Double(minValue:Double = 1, maxValue:Double = 0)
 Function Rnd:Double(minValue:Double = 1, maxValue:Double = 0)
-	Return GlobalRandom.Rnd(minValue, maxValue)
+	If GlobalRandom Then
+		Return GlobalRandom.Rnd(minValue, maxValue)
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -212,19 +231,27 @@ about:
 The optional parameter allows you to use #Rand in 2 ways:
 The optional parameter allows you to use #Rand in 2 ways:
 
 
 [ @Format | @Result
 [ @Format | @Result
-* &Rand(x) | Random integer in the range 1 to x (inclusive)
-* &Rand(x,y) | Random integer in the range x to y (inclusive)
+* `Rand(x)` | Random integer in the range 1 to x (inclusive)
+* `Rand(x,y)` | Random integer in the range x to y (inclusive)
 ]
 ]
 End Rem
 End Rem
 Function Rand:Int(minValue:Int, maxValue:Int = 1)
 Function Rand:Int(minValue:Int, maxValue:Int = 1)
-	Return GlobalRandom.Rand(minValue, maxValue)
+	If GlobalRandom Then
+		Return GlobalRandom.Rand(minValue, maxValue)
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
 bbdoc: Set random number generator seed
 bbdoc: Set random number generator seed
 End Rem
 End Rem
 Function SeedRnd(seed:Int)
 Function SeedRnd(seed:Int)
-	GlobalRandom.SeedRnd seed
+	If GlobalRandom Then
+		GlobalRandom.SeedRnd seed
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -234,6 +261,9 @@ about: Used in conjunction with SeedRnd, RndSeed allows you to reproduce sequenc
 numbers.
 numbers.
 End Rem
 End Rem
 Function RndSeed:Int()
 Function RndSeed:Int()
-	Return GlobalRandom.RndSeed()
+	If GlobalRandom Then
+		Return GlobalRandom.RndSeed()
+	Else
+		Throw "No Random installed. Maybe Import BRL.RandomDefault ?"
+	End If
 End Function
 End Function
-

+ 163 - 0
randomdefault.mod/randomdefault.bmx

@@ -0,0 +1,163 @@
+
+SuperStrict
+
+Rem
+bbdoc: Random numbers - Default
+End Rem
+Module BRL.RandomDefault
+
+ModuleInfo "Version: 1.08"
+ModuleInfo "Author: Mark Sibly, Floyd"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Blitz Research Ltd"
+ModuleInfo "Modserver: BRL"
+
+ModuleInfo "History: 1.08"
+ModuleInfo "History: Changed to default random number generator."
+ModuleInfo "History: 1.07"
+ModuleInfo "History: Added support for multiple generators"
+ModuleInfo "History: 1.06"
+ModuleInfo "History: Module is now SuperStrict"
+ModuleInfo "History: 1.05 Release"
+ModuleInfo "History: Fixed Rand() with negative min value bug"
+
+Import BRL.Random
+
+Private
+Const RND_A:Int=48271,RND_M:Int=2147483647,RND_Q:Int=44488,RND_R:Int=3399
+Public
+
+Rem
+bbdoc: Random number generator
+about:
+By creating multiple TRandom objects, multiple independent
+random number generators can be used in parallel.
+End Rem
+Type TRandomDefault Extends TRandom
+	
+	Private
+	
+	Field rnd_state:Int=$1234
+	
+	Public
+	
+	Rem
+	bbdoc: Create a new random number generator
+	End Rem
+	Method New()
+		SeedRnd(GenerateSeed())
+	End Method
+	
+	Rem
+	bbdoc: Create a new random number generator with the specified seed
+	End Rem
+	Method New(seed:Int)
+		SeedRnd seed
+	End Method
+	
+	Rem
+	bbdoc: Generate random float
+	returns: A random float in the range 0 (inclusive) to 1 (exclusive)
+	End Rem
+	Method RndFloat:Float()
+		rnd_state=RND_A*(rnd_state Mod RND_Q)-RND_R*(rnd_state/RND_Q)
+		If rnd_state<0 rnd_state=rnd_state+RND_M
+		Return (rnd_state & $ffffff0) / 268435456#  'divide by 2^28
+	End Method
+	
+	Rem
+	bbdoc: Generate random double
+	returns: A random double in the range 0 (inclusive) to 1 (exclusive)
+	End Rem
+	Method RndDouble:Double()
+		Const TWO27! = 134217728.0		'2 ^ 27
+		Const TWO29! = 536870912.0		'2 ^ 29
+	
+		rnd_state=RND_A*(rnd_state Mod RND_Q)-RND_R*(rnd_state/RND_Q)
+		If rnd_state<0 rnd_state=rnd_state+RND_M
+		Local r_hi! = rnd_state & $1ffffffc
+	
+		rnd_state=RND_A*(rnd_state Mod RND_Q)-RND_R*(rnd_state/RND_Q)
+		If rnd_state<0 rnd_state=rnd_state+RND_M
+		Local r_lo! = rnd_state & $1ffffff8
+	
+		Return (r_hi + r_lo/TWO27)/TWO29
+	End Method
+	
+	Rem
+	bbdoc: Generate random double
+	returns: A random double in the range min (inclusive) to max (exclusive)
+	about: 
+	The optional parameters allow you to use Rnd in 3 ways:
+	
+	[ @Format | @Result
+	* `Rnd()` | Random double in the range 0 (inclusive) to 1 (exclusive)
+	* `Rnd(x)` | Random double in the range 0 (inclusive) to n (exclusive)
+	* `Rnd(x,y)` | Random double in the range x (inclusive) to y (exclusive)
+	]
+	End Rem
+	Method Rnd:Double(minValue:Double = 1, maxValue:Double = 0)
+		If maxValue>minValue Return RndDouble()*(maxValue-minValue)+minValue
+		Return RndDouble()*(minValue-maxValue)+maxValue
+	End Method
+	
+	Rem
+	bbdoc: Generate random integer
+	returns: A random integer in the range min (inclusive) to max (inclusive)
+	about:
+	The optional parameter allows you to use #Rand in 2 ways:
+	
+	[ @Format | @Result
+	* `Rand(x)` | Random integer in the range 1 to x (inclusive)
+	* `Rand(x,y)` | Random integer in the range x to y (inclusive)
+	]
+	End Rem
+	Method Rand:Int(minValue:Int, maxValue:Int = 1)
+		Local Range:Int=maxValue-minValue
+		If Range>0 Return Int( RndDouble()*(1+Range) )+minValue
+		Return Int( RndDouble()*(1-Range) )+maxValue
+	End Method
+	
+	Rem
+	bbdoc: Set random number generator seed
+	End Rem
+	Method SeedRnd(seed:Int)
+		rnd_state=seed & $7fffffff             				'enforces rnd_state >= 0
+		If rnd_state=0 Or rnd_state=RND_M rnd_state=$1234	'disallow 0 and M
+	End Method
+	
+	Rem
+	bbdoc: Get random number generator seed
+	returns: The current random number generator seed
+	about: Used in conjunction with SeedRnd, RndSeed allows you to reproduce sequences of random
+	numbers.
+	End Rem
+	Method RndSeed:Int()
+		Return rnd_state
+	End Method
+
+End Type
+
+Private
+Type TRandomDefaultFactory Extends TRandomFactory
+	
+	Method New()
+		Super.New()
+		Init()
+	End Method
+	
+	Method GetName:String()
+		Return "RandomDefault"
+	End Method
+	
+	Method Create:TRandom(seed:Int)
+		Return New TRandomDefault(seed)
+	End Method
+
+	Method Create:TRandom()
+		Return New TRandomDefault()
+	End Method
+		
+End Type
+
+New TRandomDefaultFactory