Prokgen generators for primitives
This commit is contained in:
4
projects/prokgen/build.hxml
Normal file
4
projects/prokgen/build.hxml
Normal file
@@ -0,0 +1,4 @@
|
||||
-lib kiss
|
||||
-cp src
|
||||
--main prokgen.Main
|
||||
--interp
|
17
projects/prokgen/haxelib.json
Normal file
17
projects/prokgen/haxelib.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"main": "prokgen.Main",
|
||||
"name": "prokgen",
|
||||
"description": "Generalized genetic generation for Haxe classes",
|
||||
"classPath": "src/",
|
||||
"dependencies": {
|
||||
"kiss": ""
|
||||
},
|
||||
"url": "https://github.com/NQNStudios/kisslang",
|
||||
"contributors": [
|
||||
"NQNStudios"
|
||||
],
|
||||
"version": "0.0.0",
|
||||
"releasenote": "",
|
||||
"tags": [],
|
||||
"license": "LGPL"
|
||||
}
|
7
projects/prokgen/src/prokgen/Generator.hx
Normal file
7
projects/prokgen/src/prokgen/Generator.hx
Normal file
@@ -0,0 +1,7 @@
|
||||
package prokgen;
|
||||
|
||||
interface Generator<T> {
|
||||
function use(s:ProkRandom):Void;
|
||||
function makeRandom():T;
|
||||
function combine(a:T, b:T):T;
|
||||
}
|
9
projects/prokgen/src/prokgen/Main.hx
Normal file
9
projects/prokgen/src/prokgen/Main.hx
Normal file
@@ -0,0 +1,9 @@
|
||||
package prokgen;
|
||||
|
||||
import kiss.Kiss;
|
||||
import kiss.Prelude;
|
||||
import prokgen.ProkRandom;
|
||||
import prokgen.generators.*;
|
||||
|
||||
@:build(kiss.Kiss.build())
|
||||
class Main {}
|
13
projects/prokgen/src/prokgen/Main.kiss
Normal file
13
projects/prokgen/src/prokgen/Main.kiss
Normal file
@@ -0,0 +1,13 @@
|
||||
(let [r (new ProkRandom)
|
||||
:Array<Dynamic> generators []]
|
||||
(generators.push (new IntGen))
|
||||
(generators.push (new FloatGen))
|
||||
(generators.push (new BoolGen))
|
||||
(generators.push (new ArrayGen<Int> (new IntGen) 5 10))
|
||||
(doFor g generators (g.use r))
|
||||
(let [:Array<Dynamic> aList (for g generators (g.makeRandom))
|
||||
:Array<Dynamic> bList (for g generators (g.makeRandom))
|
||||
:Array<Dynamic> cList (for i (range generators.length) (.combine (nth generators i) (nth aList i) (nth bList i)))]
|
||||
~aList
|
||||
~bList
|
||||
~cList))
|
366
projects/prokgen/src/prokgen/ProkMath.hx
Normal file
366
projects/prokgen/src/prokgen/ProkMath.hx
Normal file
@@ -0,0 +1,366 @@
|
||||
package prokgen;
|
||||
|
||||
/**
|
||||
* A class containing a set of math-related functions.
|
||||
* Based on HaxeFlixel's FlxMath, with permission.
|
||||
*/
|
||||
class ProkMath
|
||||
{
|
||||
#if (flash || js || ios || blackberry)
|
||||
/**
|
||||
* Minimum value of a floating point number.
|
||||
*/
|
||||
public static inline var MIN_VALUE_FLOAT:Float = 0.0000000000000001;
|
||||
#else
|
||||
|
||||
/**
|
||||
* Minimum value of a floating point number.
|
||||
*/
|
||||
public static inline var MIN_VALUE_FLOAT:Float = 5e-324;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Maximum value of a floating point number.
|
||||
*/
|
||||
public static inline var MAX_VALUE_FLOAT:Float = 1.79e+308;
|
||||
|
||||
/**
|
||||
* Minimum value of an integer.
|
||||
*/
|
||||
public static inline var MIN_VALUE_INT:Int = -MAX_VALUE_INT;
|
||||
|
||||
/**
|
||||
* Maximum value of an integer.
|
||||
*/
|
||||
public static inline var MAX_VALUE_INT:Int = 0x7FFFFFFF;
|
||||
|
||||
/**
|
||||
* Approximation of `Math.sqrt(2)`.
|
||||
*/
|
||||
public static inline var SQUARE_ROOT_OF_TWO:Float = 1.41421356237;
|
||||
|
||||
/**
|
||||
* Used to account for floating-point inaccuracies.
|
||||
*/
|
||||
public static inline var EPSILON:Float = 0.0000001;
|
||||
|
||||
/**
|
||||
* Round a decimal number to have reduced precision (less decimal numbers).
|
||||
*
|
||||
* ```haxe
|
||||
* roundDecimal(1.2485, 2) = 1.25
|
||||
* ```
|
||||
*
|
||||
* @param Value Any number.
|
||||
* @param Precision Number of decimals the result should have.
|
||||
* @return The rounded value of that number.
|
||||
*/
|
||||
public static function roundDecimal(Value:Float, Precision:Int):Float
|
||||
{
|
||||
var mult:Float = 1;
|
||||
for (i in 0...Precision)
|
||||
{
|
||||
mult *= 10;
|
||||
}
|
||||
return Math.fround(Value * mult) / mult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bound a number by a minimum and maximum. Ensures that this number is
|
||||
* no smaller than the minimum, and no larger than the maximum.
|
||||
* Leaving a bound `null` means that side is unbounded.
|
||||
*
|
||||
* @param Value Any number.
|
||||
* @param Min Any number.
|
||||
* @param Max Any number.
|
||||
* @return The bounded value of the number.
|
||||
*/
|
||||
public static inline function bound(Value:Float, ?Min:Float, ?Max:Float):Float
|
||||
{
|
||||
var lowerBound:Float = (Min != null && Value < Min) ? Min : Value;
|
||||
return (Max != null && lowerBound > Max) ? Max : lowerBound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the linear interpolation of two numbers if `ratio`
|
||||
* is between 0 and 1, and the linear extrapolation otherwise.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```haxe
|
||||
* lerp(a, b, 0) = a
|
||||
* lerp(a, b, 1) = b
|
||||
* lerp(5, 15, 0.5) = 10
|
||||
* lerp(5, 15, -1) = -5
|
||||
* ```
|
||||
*/
|
||||
public static inline function lerp(a:Float, b:Float, ratio:Float):Float
|
||||
{
|
||||
return a + ratio * (b - a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if number is in defined range. A null bound means that side is unbounded.
|
||||
*
|
||||
* @param Value Number to check.
|
||||
* @param Min Lower bound of range.
|
||||
* @param Max Higher bound of range.
|
||||
* @return Returns true if Value is in range.
|
||||
*/
|
||||
public static inline function inBounds(Value:Float, Min:Null<Float>, Max:Null<Float>):Bool
|
||||
{
|
||||
return (Min == null || Value >= Min) && (Max == null || Value <= Max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the given number is odd.
|
||||
*/
|
||||
public static inline function isOdd(n:Float):Bool
|
||||
{
|
||||
return (Std.int(n) & 1) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the given number is even.
|
||||
*/
|
||||
public static inline function isEven(n:Float):Bool
|
||||
{
|
||||
return (Std.int(n) & 1) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `-1` if `a` is smaller, `1` if `b` is bigger and `0` if both numbers are equal.
|
||||
*/
|
||||
public static function numericComparison(a:Float, b:Float):Int
|
||||
{
|
||||
if (b > a)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (a > b)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given x/y coordinate is within the given rectangular block
|
||||
*
|
||||
* @param pointX The X value to test
|
||||
* @param pointY The Y value to test
|
||||
* @param rectX The X value of the region to test within
|
||||
* @param rectY The Y value of the region to test within
|
||||
* @param rectWidth The width of the region to test within
|
||||
* @param rectHeight The height of the region to test within
|
||||
*
|
||||
* @return true if pointX/pointY is within the region, otherwise false
|
||||
*/
|
||||
public static function pointInCoordinates(pointX:Float, pointY:Float, rectX:Float, rectY:Float, rectWidth:Float, rectHeight:Float):Bool
|
||||
{
|
||||
if (pointX >= rectX && pointX <= (rectX + rectWidth))
|
||||
{
|
||||
if (pointY >= rectY && pointY <= (rectY + rectHeight))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given amount to the value, but never lets the value
|
||||
* go over the specified maximum or under the specified minimum.
|
||||
*
|
||||
* @param value The value to add the amount to
|
||||
* @param amount The amount to add to the value
|
||||
* @param max The maximum the value is allowed to be
|
||||
* @param min The minimum the value is allowed to be
|
||||
* @return The new value
|
||||
*/
|
||||
public static function maxAdd(value:Int, amount:Int, max:Int, min:Int = 0):Int
|
||||
{
|
||||
value += amount;
|
||||
|
||||
if (value > max)
|
||||
{
|
||||
value = max;
|
||||
}
|
||||
else if (value <= min)
|
||||
{
|
||||
value = min;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that value always stays between 0 and max,
|
||||
* by wrapping the value around.
|
||||
*
|
||||
* @param value The value to wrap around
|
||||
* @param min The minimum the value is allowed to be
|
||||
* @param max The maximum the value is allowed to be
|
||||
* @return The wrapped value
|
||||
*/
|
||||
public static function wrap(value:Int, min:Int, max:Int):Int
|
||||
{
|
||||
var range:Int = max - min + 1;
|
||||
|
||||
if (value < min)
|
||||
value += range * Std.int((min - value) / range + 1);
|
||||
|
||||
return min + (value - min) % range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remaps a number from one range to another.
|
||||
*
|
||||
* @param value The incoming value to be converted
|
||||
* @param start1 Lower bound of the value's current range
|
||||
* @param stop1 Upper bound of the value's current range
|
||||
* @param start2 Lower bound of the value's target range
|
||||
* @param stop2 Upper bound of the value's target range
|
||||
* @return The remapped value
|
||||
*/
|
||||
public static function remapToRange(value:Float, start1:Float, stop1:Float, start2:Float, stop2:Float):Float
|
||||
{
|
||||
return start2 + (value - start1) * ((stop2 - start2) / (stop1 - start1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the dot product value of two vectors
|
||||
*
|
||||
* @param ax Vector X
|
||||
* @param ay Vector Y
|
||||
* @param bx Vector X
|
||||
* @param by Vector Y
|
||||
*
|
||||
* @return Result of the dot product
|
||||
*/
|
||||
public static inline function dotProduct(ax:Float, ay:Float, bx:Float, by:Float):Float
|
||||
{
|
||||
return ax * bx + ay * by;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the given vector.
|
||||
*/
|
||||
public static inline function vectorLength(dx:Float, dy:Float):Float
|
||||
{
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of decimals a `Float` has.
|
||||
*/
|
||||
public static function getDecimals(n:Float):Int
|
||||
{
|
||||
var helperArray:Array<String> = Std.string(n).split(".");
|
||||
var decimals:Int = 0;
|
||||
|
||||
if (helperArray.length > 1)
|
||||
{
|
||||
decimals = helperArray[1].length;
|
||||
}
|
||||
|
||||
return decimals;
|
||||
}
|
||||
|
||||
public static inline function equal(aValueA:Float, aValueB:Float, aDiff:Float = EPSILON):Bool
|
||||
{
|
||||
return Math.abs(aValueA - aValueB) <= aDiff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `-1` if the number is smaller than `0` and `1` otherwise
|
||||
*/
|
||||
public static inline function signOf(n:Float):Int
|
||||
{
|
||||
return (n < 0) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two numbers have the same sign (using `FlxMath.signOf()`).
|
||||
*/
|
||||
public static inline function sameSign(a:Float, b:Float):Bool
|
||||
{
|
||||
return signOf(a) == signOf(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* A faster but slightly less accurate version of `Math.sin()`.
|
||||
* About 2-6 times faster with < 0.05% average error.
|
||||
*
|
||||
* @param n The angle in radians.
|
||||
* @return An approximated sine of `n`.
|
||||
*/
|
||||
public static inline function fastSin(n:Float):Float
|
||||
{
|
||||
n *= 0.3183098862; // divide by pi to normalize
|
||||
|
||||
// bound between -1 and 1
|
||||
if (n > 1)
|
||||
{
|
||||
n -= (Math.ceil(n) >> 1) << 1;
|
||||
}
|
||||
else if (n < -1)
|
||||
{
|
||||
n += (Math.ceil(-n) >> 1) << 1;
|
||||
}
|
||||
|
||||
// this approx only works for -pi <= rads <= pi, but it's quite accurate in this region
|
||||
if (n > 0)
|
||||
{
|
||||
return n * (3.1 + n * (0.5 + n * (-7.2 + n * 3.6)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return n * (3.1 - n * (0.5 + n * (7.2 + n * 3.6)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A faster, but less accurate version of `Math.cos()`.
|
||||
* About 2-6 times faster with < 0.05% average error.
|
||||
*
|
||||
* @param n The angle in radians.
|
||||
* @return An approximated cosine of `n`.
|
||||
*/
|
||||
public static inline function fastCos(n:Float):Float
|
||||
{
|
||||
return fastSin(n + 1.570796327); // sin and cos are the same, offset by pi/2
|
||||
}
|
||||
|
||||
/**
|
||||
* Hyperbolic sine.
|
||||
*/
|
||||
public static inline function sinh(n:Float):Float
|
||||
{
|
||||
return (Math.exp(n) - Math.exp(-n)) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bigger argument.
|
||||
*/
|
||||
public static inline function maxInt(a:Int, b:Int):Int
|
||||
{
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smaller argument.
|
||||
*/
|
||||
public static inline function minInt(a:Int, b:Int):Int
|
||||
{
|
||||
return (a > b) ? b : a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute integer value.
|
||||
*/
|
||||
public static inline function absInt(n:Int):Int
|
||||
{
|
||||
return (n > 0) ? n : -n;
|
||||
}
|
||||
}
|
469
projects/prokgen/src/prokgen/ProkRandom.hx
Normal file
469
projects/prokgen/src/prokgen/ProkRandom.hx
Normal file
@@ -0,0 +1,469 @@
|
||||
package prokgen;
|
||||
|
||||
import prokgen.ProkMath;
|
||||
|
||||
#if flixel
|
||||
import flixel.util.FlxColor;
|
||||
#end
|
||||
|
||||
/**
|
||||
* A class containing a set of functions for random generation.
|
||||
* Based on HaxeFlixel's FlxRandom, with permission.
|
||||
*/
|
||||
class ProkRandom
|
||||
{
|
||||
/**
|
||||
* The global base random number generator seed (for deterministic behavior in recordings and saves).
|
||||
* If you want, you can set the seed with an integer between 1 and 2,147,483,647 inclusive.
|
||||
* Altering this yourself may break recording functionality!
|
||||
*/
|
||||
public var initialSeed(default, set):Int = 1;
|
||||
|
||||
/**
|
||||
* Current seed used to generate new random numbers. You can retrieve this value if,
|
||||
* for example, you want to store the seed that was used to randomly generate a level.
|
||||
*/
|
||||
public var currentSeed(get, set):Int;
|
||||
|
||||
/**
|
||||
* Create a new ProkRandom object.
|
||||
*
|
||||
* @param InitialSeed The first seed of this ProkRandom object. If ignored, a random seed will be generated.
|
||||
*/
|
||||
public function new(?InitialSeed:Int)
|
||||
{
|
||||
if (InitialSeed != null)
|
||||
{
|
||||
initialSeed = InitialSeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetInitialSeed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to easily set the global seed to a new random number.
|
||||
* Please note that this function is not deterministic!
|
||||
* If you call it in your game, recording may not function as expected.
|
||||
*
|
||||
* @return The new initial seed.
|
||||
*/
|
||||
public inline function resetInitialSeed():Int
|
||||
{
|
||||
return initialSeed = rangeBound(Std.int(Math.random() * ProkMath.MAX_VALUE_INT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom integer between Min and Max, inclusive.
|
||||
* Will not return a number in the Excludes array, if provided.
|
||||
* Please note that large Excludes arrays can slow calculations.
|
||||
*
|
||||
* @param Min The minimum value that should be returned. 0 by default.
|
||||
* @param Max The maximum value that should be returned. 2,147,483,647 by default.
|
||||
* @param Excludes Optional array of values that should not be returned.
|
||||
*/
|
||||
public function int(Min:Int = 0, Max:Int = ProkMath.MAX_VALUE_INT, ?Excludes:Array<Int>):Int
|
||||
{
|
||||
if (Min == 0 && Max == ProkMath.MAX_VALUE_INT && Excludes == null)
|
||||
{
|
||||
return Std.int(generate());
|
||||
}
|
||||
else if (Min == Max)
|
||||
{
|
||||
return Min;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Swap values if reversed
|
||||
if (Min > Max)
|
||||
{
|
||||
Min = Min + Max;
|
||||
Max = Min - Max;
|
||||
Min = Min - Max;
|
||||
}
|
||||
|
||||
if (Excludes == null)
|
||||
{
|
||||
return Math.floor(Min + generate() / MODULUS * (Max - Min + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
var result:Int = 0;
|
||||
|
||||
do
|
||||
{
|
||||
result = Math.floor(Min + generate() / MODULUS * (Max - Min + 1));
|
||||
}
|
||||
while (Excludes.indexOf(result) >= 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom float value between Min (inclusive) and Max (exclusive).
|
||||
* Will not return a number in the Excludes array, if provided.
|
||||
* Please note that large Excludes arrays can slow calculations.
|
||||
*
|
||||
* @param Min The minimum value that should be returned. 0 by default.
|
||||
* @param Max The maximum value that should be returned. 1 by default.
|
||||
* @param Excludes Optional array of values that should not be returned.
|
||||
*/
|
||||
public function float(Min:Float = 0, Max:Float = 1, ?Excludes:Array<Float>):Float
|
||||
{
|
||||
var result:Float = 0;
|
||||
|
||||
if (Min == 0 && Max == 1 && Excludes == null)
|
||||
{
|
||||
return generate() / MODULUS;
|
||||
}
|
||||
else if (Min == Max)
|
||||
{
|
||||
result = Min;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Swap values if reversed.
|
||||
if (Min > Max)
|
||||
{
|
||||
Min = Min + Max;
|
||||
Max = Min - Max;
|
||||
Min = Min - Max;
|
||||
}
|
||||
|
||||
if (Excludes == null)
|
||||
{
|
||||
result = Min + (generate() / MODULUS) * (Max - Min);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
result = Min + (generate() / MODULUS) * (Max - Min);
|
||||
}
|
||||
while (Excludes.indexOf(result) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// helper variables for floatNormal -- it produces TWO random values with each call so we have to store some state outside the function
|
||||
var _hasFloatNormalSpare:Bool = false;
|
||||
var _floatNormalRand1:Float = 0;
|
||||
var _floatNormalRand2:Float = 0;
|
||||
var _twoPI:Float = Math.PI * 2;
|
||||
var _floatNormalRho:Float = 0;
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom float value in a statistical normal distribution centered on Mean with a standard deviation size of StdDev.
|
||||
* (This uses the Box-Muller transform algorithm for gaussian pseudorandom numbers)
|
||||
*
|
||||
* Normal distribution: 68% values are within 1 standard deviation, 95% are in 2 StdDevs, 99% in 3 StdDevs.
|
||||
* See this image: https://github.com/HaxeFlixel/flixel-demos/blob/dev/Performance/FlxRandom/normaldistribution.png
|
||||
*
|
||||
* @param Mean The Mean around which the normal distribution is centered
|
||||
* @param StdDev Size of the standard deviation
|
||||
*/
|
||||
public function floatNormal(Mean:Float = 0, StdDev:Float = 1):Float
|
||||
{
|
||||
if (_hasFloatNormalSpare)
|
||||
{
|
||||
_hasFloatNormalSpare = false;
|
||||
var scale:Float = StdDev * _floatNormalRho;
|
||||
return Mean + scale * _floatNormalRand2;
|
||||
}
|
||||
|
||||
_hasFloatNormalSpare = true;
|
||||
|
||||
var theta:Float = _twoPI * (generate() / MODULUS);
|
||||
_floatNormalRho = Math.sqrt(-2 * Math.log(1 - (generate() / MODULUS)));
|
||||
var scale:Float = StdDev * _floatNormalRho;
|
||||
|
||||
_floatNormalRand1 = Math.cos(theta);
|
||||
_floatNormalRand2 = Math.sin(theta);
|
||||
|
||||
return Mean + scale * _floatNormalRand1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true or false based on the chance value (default 50%).
|
||||
* For example if you wanted a player to have a 30.5% chance of getting a bonus,
|
||||
* call bool(30.5) - true means the chance passed, false means it failed.
|
||||
*
|
||||
* @param Chance The chance of receiving the value.
|
||||
* Should be given as a number between 0 and 100 (effectively 0% to 100%)
|
||||
* @return Whether the roll passed or not.
|
||||
*/
|
||||
public inline function bool(Chance:Float = 50):Bool
|
||||
{
|
||||
return float(0, 100) < Chance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either a 1 or -1.
|
||||
*
|
||||
* @param Chance The chance of receiving a positive value.
|
||||
* Should be given as a number between 0 and 100 (effectively 0% to 100%)
|
||||
* @return 1 or -1
|
||||
*/
|
||||
public inline function sign(Chance:Float = 50):Int
|
||||
{
|
||||
return bool(Chance) ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pseudorandomly select from an array of weighted options. For example, if you passed in an array of [50, 30, 20]
|
||||
* there would be a 50% chance of returning a 0, a 30% chance of returning a 1, and a 20% chance of returning a 2.
|
||||
* Note that the values in the array do not have to add to 100 or any other number.
|
||||
* The percent chance will be equal to a given value in the array divided by the total of all values in the array.
|
||||
*
|
||||
* @param WeightsArray An array of weights.
|
||||
* @return A value between 0 and (SelectionArray.length - 1), with a probability equivalent to the values in SelectionArray.
|
||||
*/
|
||||
public function weightedPick(WeightsArray:Array<Float>):Int
|
||||
{
|
||||
var totalWeight:Float = 0;
|
||||
var pick:Int = 0;
|
||||
|
||||
for (i in WeightsArray)
|
||||
{
|
||||
totalWeight += i;
|
||||
}
|
||||
|
||||
totalWeight = float(0, totalWeight);
|
||||
|
||||
for (i in 0...WeightsArray.length)
|
||||
{
|
||||
if (totalWeight < WeightsArray[i])
|
||||
{
|
||||
pick = i;
|
||||
break;
|
||||
}
|
||||
|
||||
totalWeight -= WeightsArray[i];
|
||||
}
|
||||
|
||||
return pick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random object from an array.
|
||||
*
|
||||
* @param Objects An array from which to return an object.
|
||||
* @param WeightsArray Optional array of weights which will determine the likelihood of returning a given value from Objects.
|
||||
* If none is passed, all objects in the Objects array will have an equal likelihood of being returned.
|
||||
* Values in WeightsArray will correspond to objects in Objects exactly.
|
||||
* @param StartIndex Optional index from which to restrict selection. Default value is 0, or the beginning of the Objects array.
|
||||
* @param EndIndex Optional index at which to restrict selection. Ignored if 0, which is the default value.
|
||||
* @return A pseudorandomly chosen object from Objects.
|
||||
*/
|
||||
@:generic
|
||||
public function getObject<T>(Objects:Array<T>, ?WeightsArray:Array<Float>, StartIndex:Int = 0, ?EndIndex:Null<Int>):T
|
||||
{
|
||||
var selected:Null<T> = null;
|
||||
|
||||
if (Objects.length != 0)
|
||||
{
|
||||
if (WeightsArray == null)
|
||||
{
|
||||
WeightsArray = [for (i in 0...Objects.length) 1];
|
||||
}
|
||||
|
||||
if (EndIndex == null)
|
||||
{
|
||||
EndIndex = Objects.length - 1;
|
||||
}
|
||||
|
||||
StartIndex = Std.int(ProkMath.bound(StartIndex, 0, Objects.length - 1));
|
||||
EndIndex = Std.int(ProkMath.bound(EndIndex, 0, Objects.length - 1));
|
||||
|
||||
// Swap values if reversed
|
||||
if (EndIndex < StartIndex)
|
||||
{
|
||||
StartIndex = StartIndex + EndIndex;
|
||||
EndIndex = StartIndex - EndIndex;
|
||||
StartIndex = StartIndex - EndIndex;
|
||||
}
|
||||
|
||||
if (EndIndex > WeightsArray.length - 1)
|
||||
{
|
||||
EndIndex = WeightsArray.length - 1;
|
||||
}
|
||||
|
||||
_arrayFloatHelper = [for (i in StartIndex...EndIndex + 1) WeightsArray[i]];
|
||||
selected = Objects[StartIndex + weightedPick(_arrayFloatHelper)];
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles the entries in an array into a new pseudorandom order.
|
||||
*
|
||||
* @param Objects An array to shuffle.
|
||||
* @param HowManyTimes How many swaps to perform during the shuffle operation.
|
||||
* A good rule of thumb is 2-4 times the number of objects in the list.
|
||||
* @return The newly shuffled array.
|
||||
*/
|
||||
@:generic
|
||||
@:deprecated("Unless you rely on reproducing the exact output of shuffleArray(), you should use shuffle() instead, which is both faster and higher quality.")
|
||||
public function shuffleArray<T>(Objects:Array<T>, HowManyTimes:Int):Array<T>
|
||||
{
|
||||
HowManyTimes = Std.int(Math.max(HowManyTimes, 0));
|
||||
|
||||
var tempObject:Null<T> = null;
|
||||
|
||||
for (i in 0...HowManyTimes)
|
||||
{
|
||||
var pick1:Int = int(0, Objects.length - 1);
|
||||
var pick2:Int = int(0, Objects.length - 1);
|
||||
|
||||
tempObject = Objects[pick1];
|
||||
Objects[pick1] = Objects[pick2];
|
||||
Objects[pick2] = tempObject;
|
||||
}
|
||||
|
||||
return Objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles the entries in an array in-place into a new pseudorandom order,
|
||||
* using the standard Fisher-Yates shuffle algorithm.
|
||||
*
|
||||
* @param array The array to shuffle.
|
||||
* @since 4.2.0
|
||||
*/
|
||||
@:generic
|
||||
public function shuffle<T>(array:Array<T>):Void
|
||||
{
|
||||
var maxValidIndex = array.length - 1;
|
||||
for (i in 0...maxValidIndex)
|
||||
{
|
||||
var j = int(i, maxValidIndex);
|
||||
var tmp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
#if flixel
|
||||
/**
|
||||
* Returns a random color.
|
||||
*
|
||||
* @param Min An optional FlxColor representing the lower bounds for the generated color.
|
||||
* @param Max An optional FlxColor representing the upper bounds for the generated color.
|
||||
* @param Alpha An optional value for the alpha channel of the generated color.
|
||||
* @param GreyScale Whether or not to create a color that is strictly a shade of grey. False by default.
|
||||
* @return A color value as a FlxColor.
|
||||
*/
|
||||
public function color(?Min:FlxColor, ?Max:FlxColor, ?Alpha:Int, GreyScale:Bool = false):FlxColor
|
||||
{
|
||||
var red:Int;
|
||||
var green:Int;
|
||||
var blue:Int;
|
||||
var alpha:Int;
|
||||
|
||||
if (Min == null && Max == null)
|
||||
{
|
||||
red = int(0, 255);
|
||||
green = int(0, 255);
|
||||
blue = int(0, 255);
|
||||
alpha = Alpha == null ? int(0, 255) : Alpha;
|
||||
}
|
||||
else if (Max == null)
|
||||
{
|
||||
red = int(Min.red, 255);
|
||||
green = GreyScale ? red : int(Min.green, 255);
|
||||
blue = GreyScale ? red : int(Min.blue, 255);
|
||||
alpha = Alpha == null ? int(Min.alpha, 255) : Alpha;
|
||||
}
|
||||
else if (Min == null)
|
||||
{
|
||||
red = int(0, Max.red);
|
||||
green = GreyScale ? red : int(0, Max.green);
|
||||
blue = GreyScale ? red : int(0, Max.blue);
|
||||
alpha = Alpha == null ? int(0, Max.alpha) : Alpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
red = int(Min.red, Max.red);
|
||||
green = GreyScale ? red : int(Min.green, Max.green);
|
||||
blue = GreyScale ? red : int(Min.blue, Max.blue);
|
||||
alpha = Alpha == null ? int(Min.alpha, Max.alpha) : Alpha;
|
||||
}
|
||||
|
||||
return FlxColor.fromRGB(red, green, blue, alpha);
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Internal method to quickly generate a pseudorandom number. Used only by other functions of this class.
|
||||
* Also updates the internal seed, which will then be used to generate the next pseudorandom number.
|
||||
*
|
||||
* @return A new pseudorandom number.
|
||||
*/
|
||||
inline function generate():Float
|
||||
{
|
||||
return internalSeed = (internalSeed * MULTIPLIER) % MODULUS;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual internal seed. Stored as a Float value to prevent inaccuracies due to
|
||||
* integer overflow in the generate() equation.
|
||||
*/
|
||||
var internalSeed:Float = 1;
|
||||
|
||||
/**
|
||||
* Internal function to update the current seed whenever the initial seed is reset,
|
||||
* and keep the initial seed's value in range.
|
||||
*/
|
||||
inline function set_initialSeed(NewSeed:Int):Int
|
||||
{
|
||||
return initialSeed = currentSeed = rangeBound(NewSeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal seed as an integer.
|
||||
*/
|
||||
inline function get_currentSeed():Int
|
||||
{
|
||||
return Std.int(internalSeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal seed to an integer value.
|
||||
*/
|
||||
inline function set_currentSeed(NewSeed:Int):Int
|
||||
{
|
||||
return Std.int(internalSeed = rangeBound(NewSeed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal shared function to ensure an arbitrary value is in the valid range of seed values.
|
||||
*/
|
||||
static inline function rangeBound(Value:Int):Int
|
||||
{
|
||||
return Std.int(ProkMath.bound(Value, 1, MODULUS - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal shared helper variable. Used by getObject().
|
||||
*/
|
||||
static var _arrayFloatHelper:Array<Float> = null;
|
||||
|
||||
/**
|
||||
* Constants used in the pseudorandom number generation equation.
|
||||
* These are the constants suggested by the revised MINSTD pseudorandom number generator,
|
||||
* and they use the full range of possible integer values.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||
* @see Stephen K. Park and Keith W. Miller and Paul K. Stockmeyer (1988).
|
||||
* "Technical Correspondence". Communications of the ACM 36 (7): 105–110.
|
||||
*/
|
||||
static inline var MULTIPLIER:Float = 48271.0;
|
||||
|
||||
static inline var MODULUS:Int = ProkMath.MAX_VALUE_INT;
|
||||
}
|
51
projects/prokgen/src/prokgen/generators/ArrayGen.hx
Normal file
51
projects/prokgen/src/prokgen/generators/ArrayGen.hx
Normal file
@@ -0,0 +1,51 @@
|
||||
package prokgen.generators;
|
||||
|
||||
enum ArrayCombineBehavior {
|
||||
ShortestLength;
|
||||
LongestLength;
|
||||
}
|
||||
|
||||
class ArrayGen<T> implements Generator<Array<T>> {
|
||||
var elementGen:Generator<T>;
|
||||
var minLength:Int;
|
||||
var maxLength:Int;
|
||||
var r:ProkRandom;
|
||||
var behavior:ArrayCombineBehavior;
|
||||
|
||||
public function new(elementGen:Generator<T>, minLength:Int, maxLength:Int, behavior:ArrayCombineBehavior = ShortestLength) {
|
||||
this.elementGen = elementGen;
|
||||
this.minLength = minLength;
|
||||
this.maxLength = maxLength;
|
||||
this.behavior = behavior;
|
||||
}
|
||||
|
||||
public function use(r:ProkRandom) {
|
||||
this.r = r;
|
||||
this.elementGen.use(r);
|
||||
}
|
||||
|
||||
public function makeRandom() {
|
||||
var length = r.int(minLength, maxLength);
|
||||
return [for (_ in 0... length) elementGen.makeRandom()];
|
||||
}
|
||||
|
||||
public function combine(a:Array<T>, b:Array<T>) {
|
||||
var longest = if (a.length > b.length) a else b;
|
||||
var sharedLength = Math.floor(Math.min(a.length, b.length));
|
||||
var longestLength = switch (behavior) {
|
||||
case LongestLength:
|
||||
longest.length;
|
||||
case ShortestLength:
|
||||
sharedLength;
|
||||
};
|
||||
|
||||
return [
|
||||
for (i in 0...sharedLength)
|
||||
elementGen.combine(a[i], b[i])
|
||||
].concat(
|
||||
[
|
||||
for (i in sharedLength... longestLength)
|
||||
longest[i]
|
||||
]);
|
||||
}
|
||||
}
|
21
projects/prokgen/src/prokgen/generators/BoolGen.hx
Normal file
21
projects/prokgen/src/prokgen/generators/BoolGen.hx
Normal file
@@ -0,0 +1,21 @@
|
||||
package prokgen.generators;
|
||||
|
||||
import prokgen.ProkRandom;
|
||||
|
||||
class BoolGen implements Generator<Bool> {
|
||||
var r:ProkRandom;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function use(r:ProkRandom) {
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
public function makeRandom() {
|
||||
return r.bool();
|
||||
}
|
||||
|
||||
public function combine(a:Bool, b:Bool) {
|
||||
return r.getObject([a, b]);
|
||||
}
|
||||
}
|
46
projects/prokgen/src/prokgen/generators/FloatGen.hx
Normal file
46
projects/prokgen/src/prokgen/generators/FloatGen.hx
Normal file
@@ -0,0 +1,46 @@
|
||||
package prokgen.generators;
|
||||
|
||||
import prokgen.ProkMath;
|
||||
import prokgen.ProkRandom;
|
||||
|
||||
class FloatGen implements Generator<Float> {
|
||||
private var min:Float;
|
||||
private var max:Float;
|
||||
private var r:ProkRandom;
|
||||
|
||||
public function new(min:Float = -ProkMath.MAX_VALUE_FLOAT, max:Float = ProkMath.MAX_VALUE_FLOAT) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public function use(r:ProkRandom) {
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
public function makeRandom() {
|
||||
var minSign = min / Math.abs(min);
|
||||
var maxSign = max / Math.abs(max);
|
||||
|
||||
return if (minSign != maxSign) {
|
||||
var sign = r.getObject([minSign, maxSign], [Math.abs(min)/Math.abs(max), 1]);
|
||||
var abs = r.float(0, Math.abs(if (sign == minSign) min else max));
|
||||
sign * abs;
|
||||
} else {
|
||||
r.float(min, max);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public function combine(a:Float, b:Float) {
|
||||
var minSign = min / Math.abs(min);
|
||||
var maxSign = max / Math.abs(max);
|
||||
|
||||
// Avoid overflow when taking the mean:
|
||||
return if (minSign == maxSign) {
|
||||
var halfDiff = Math.abs(a - b) / 2;
|
||||
Math.min(a, b) + halfDiff;
|
||||
} else {
|
||||
(a + b) / 2;
|
||||
}
|
||||
}
|
||||
}
|
45
projects/prokgen/src/prokgen/generators/IntGen.hx
Normal file
45
projects/prokgen/src/prokgen/generators/IntGen.hx
Normal file
@@ -0,0 +1,45 @@
|
||||
package prokgen.generators;
|
||||
|
||||
import prokgen.ProkMath;
|
||||
import prokgen.ProkRandom;
|
||||
|
||||
class IntGen implements Generator<Int> {
|
||||
private var min:Int;
|
||||
private var max:Int;
|
||||
private var r:ProkRandom;
|
||||
|
||||
public function new(min:Int = ProkMath.MIN_VALUE_INT, max:Int = ProkMath.MAX_VALUE_INT) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public function use(r:ProkRandom) {
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
public function makeRandom() {
|
||||
var minSign = min / Math.abs(min);
|
||||
var maxSign = max / Math.abs(max);
|
||||
|
||||
return if (minSign != maxSign) {
|
||||
var sign = r.getObject([minSign, maxSign], [Math.abs(min)/Math.abs(max), 1]);
|
||||
var abs = r.int(0, Std.int(Math.abs(if (sign == minSign) min else max)));
|
||||
Std.int(sign * abs);
|
||||
} else {
|
||||
r.int(min, max);
|
||||
};
|
||||
}
|
||||
|
||||
public function combine(a:Int, b:Int) {
|
||||
var minSign = min / Math.abs(min);
|
||||
var maxSign = max / Math.abs(max);
|
||||
|
||||
// Avoid overflow when taking the mean:
|
||||
return if (minSign == maxSign) {
|
||||
var halfDiff = Math.abs(a - b) / 2;
|
||||
Math.round(Math.min(a, b) + halfDiff);
|
||||
} else {
|
||||
Math.round((a + b) / 2);
|
||||
}
|
||||
}
|
||||
}
|
3
projects/prokgen/test.sh
Normal file
3
projects/prokgen/test.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#! /bin/bash
|
||||
|
||||
haxe build.hxml
|
Reference in New Issue
Block a user