Prokgen generators for primitives

This commit is contained in:
2022-03-30 14:18:18 -06:00
parent bde4be60f0
commit c6f70b46ea
12 changed files with 1051 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
-lib kiss
-cp src
--main prokgen.Main
--interp

View 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"
}

View 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;
}

View 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 {}

View 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))

View 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;
}
}

View 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): 105110.
*/
static inline var MULTIPLIER:Float = 48271.0;
static inline var MODULUS:Int = ProkMath.MAX_VALUE_INT;
}

View 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]
]);
}
}

View 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]);
}
}

View 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;
}
}
}

View 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
View File

@@ -0,0 +1,3 @@
#! /bin/bash
haxe build.hxml