BeeneticToolkit.Random
0.12.1
dotnet add package BeeneticToolkit.Random --version 0.12.1
NuGet\Install-Package BeeneticToolkit.Random -Version 0.12.1
<PackageReference Include="BeeneticToolkit.Random" Version="0.12.1" />
<PackageVersion Include="BeeneticToolkit.Random" Version="0.12.1" />
<PackageReference Include="BeeneticToolkit.Random" />
paket add BeeneticToolkit.Random --version 0.12.1
#r "nuget: BeeneticToolkit.Random, 0.12.1"
#:package BeeneticToolkit.Random@0.12.1
#addin nuget:?package=BeeneticToolkit.Random&version=0.12.1
#tool nuget:?package=BeeneticToolkit.Random&version=0.12.1
BeeneticToolkit.Random
Deterministic, seedable pseudo-random generation for games and simulations.
System.Random gives you one algorithm and no reproducibility guarantees. BeeneticToolkit.Random adds:
- Seedable, swappable algorithms — xoshiro256** (default), Xorshift, Combined LCG, Middle-Square-Weyl.
- Reproducible environments — group named generators under a single root seed so an entire run replays from one number.
- Rich selection helpers — weighted choice, random subsets, exclusions, shuffles, and random enum values.
Not cryptographically secure — designed for game logic, procedural generation, and simulations. Targets
netstandard2.1(modern .NET and Unity).
Install
dotnet add package BeeneticToolkit.Random
Quick start
Generators come from environments — the seedable unit. Create one, register a generator on it, and draw:
using BeeneticToolkit.Random;
var env = new RandomEnvironment("game", rootSeed: 12345);
var rng = env.CreateAndRegister("main");
int roll = rng.NextInt(1, 7); // [1, 7)
double unit = rng.NextDouble(); // [0, 1)
bool crit = rng.NextBool(0.05f); // 5% chance of true
var dir = rng.NextEnum<Direction>(); // a random value of your enum
Guid id = rng.NextGuid();
Just need a number and don't care about reproducibility? Use the shared scratch generator:
int n = RandomManager.Scratch.NextInt(100); // entropy-seeded, not reproducible
Reproducible environments
Group related generators under one root seed. Each generator's seed is derived from the root and its key, so every stream is independent yet the whole environment replays from a single number:
var world = new RandomEnvironment("world", rootSeed: 2024);
var terrain = world.CreateAndRegister("terrain");
var loot = world.CreateAndRegister("loot"); // distinct stream, also reproducible
An environment always has a RootSeed. If you don't supply one, a high-entropy seed is generated
and recorded — so even an unseeded run can be reproduced after the fact:
var run = new RandomEnvironment("run"); // auto-seeded
long seed = run.RootSeed; // capture it to replay this exact run later
Use RandomKey constants instead of raw strings for compile-time safety against typos. Pick a default
algorithm per environment, or override per generator:
var env = new RandomEnvironment("sim", rootSeed: 7, algorithm: RandomAlgorithm.CombinedLCG);
var fast = env.CreateAndRegister("fast", algorithm: RandomAlgorithm.Xorshift);
Process-wide access
The global RandomManager is a registry of named environments:
var enemies = RandomManager.CreateEnvironment("enemies", rootSeed: 99);
enemies.CreateAndRegister("spawns");
var sameEnv = RandomManager.GetEnvironment("enemies");
var spawnRng = sameEnv.Get("spawns");
Selection helpers
The selection and shuffle helpers are extension methods on the generator, so the source of randomness is always explicit:
var loot = new[] { "common", "rare", "epic" };
string pick = rng.RandomChoice(loot);
// Weighted — "common" is chosen ~90% of the time.
string weighted = rng.RandomWeightedChoice(
new[] { ("common", 9.0), ("rare", 1.0) });
// Shuffle returns a new list; ShuffleInPlace mutates in place.
var deck = Enumerable.Range(1, 52).ToList();
List<int> shuffled = rng.Shuffle(deck);
rng.ShuffleInPlace(deck);
// Non-throwing Try* variants are available for all selectors.
if (rng.TryRandomChoice(loot, out string chosen)) { /* ... */ }
Coherent noise
Deterministic, cross-platform value and Perlin noise (2D & 3D) with a fractal/fBm wrapper, in
the BeeneticToolkit.Random.Noise namespace. Unlike Unity's Mathf.PerlinNoise, it is seedable and
bit-identical across platforms, so procedural content reproduces exactly. Allocation-free scalar sampling.
using BeeneticToolkit.Random.Noise;
INoise perlin = NoiseFactory.Create(NoiseAlgorithm.Perlin, seed: 1337);
float v = perlin.Sample(x, y); // [-1, 1]
float v01 = perlin.Sample01(x, y); // [0, 1]
float cave = perlin.Sample(x, y, z); // 3D
// Fractal Brownian motion — natural-looking terrain/textures:
var terrain = new FractalNoise(perlin, octaves: 5, frequency: 0.01f);
float height = terrain.Sample(worldX, worldY);
Seed it from an environment's RootSeed to keep noise reproducible alongside the rest of a run:
INoise noise = NoiseFactory.Create(NoiseAlgorithm.Perlin, world.RootSeed);
Poisson-disk point sampling
Blue-noise point distributions (BeeneticToolkit.Random.Sampling) — random points kept at least a
minimum distance apart, so they scatter evenly with no clumps or gaps. Ideal for placing vegetation,
props, spawns, or decoration; deterministic for a seeded generator.
using BeeneticToolkit.Random.Sampling;
// Points at least 4 units apart, filling a 100x100 region:
IReadOnlyList<(float X, float Y)> points =
PoissonDisk.Sample(rng, width: 100f, height: 100f, minDistance: 4f);
foreach (var (x, y) in points)
PlaceTree(x, y);
Spatial point sampling
Single random directions and points inside 2D/3D shapes, as extension methods on the generator
(BeeneticToolkit.Random.Sampling). Points inside areas and volumes are uniformly distributed
(no center-crowding), and results are returned as value tuples — no allocation, no engine-specific
vector type to convert:
using BeeneticToolkit.Random.Sampling;
float angle = rng.NextAngle(); // [0, 2π)
(float X, float Y) dir = rng.NextUnitVector2(); // point on the unit circle
var dir3 = rng.NextUnitVector3(); // point on the unit sphere
var spawn = rng.NextPointInCircle(radius: 5f); // uniform inside a disk
var ring = rng.NextPointInAnnulus(inner: 3f, outer: 5f); // uniform inside a ring
var inBlast = rng.NextPointInSphere(radius: 2f); // uniform inside a ball
Probability & loot
Game-ready probability building blocks in the BeeneticToolkit.Random.Probability namespace. Each takes a
generator at draw time, so everything is reproducible from a seed:
using BeeneticToolkit.Random.Probability;
// Weighted bag — draw with replacement, or without (the bag depletes).
var bag = new WeightedBag<string>().Add("common", 9).Add("rare", 1);
string drop = bag.Draw(rng); // bag unchanged
string unique = bag.DrawWithoutReplacement(rng); // removed from the bag
// Loot table — weighted entries, nestable into tiers.
var rares = new LootTable<string>().Add("epic", 3).Add("legendary", 1);
var chest = new LootTable<string>()
.Add("gold", 80)
.AddTable(rares, 20); // 20% chance to roll the rare sub-table
string loot = chest.Roll(rng);
List<string> haul = chest.Roll(rng, count: 5);
// Dice notation — "NdS±M", parse once or roll inline.
int damage = rng.Roll("2d6+1");
var attack = DiceRoll.Parse("1d20+5");
int hit = attack.Roll(rng); // attack.Min .. attack.Max
// Gacha pity — base rate, soft-pity ramp, guaranteed at hard pity.
var pity = new PityCounter(baseChance: 0.006, hardPity: 90, softPityStart: 73, softPityIncrement: 0.06);
bool fiveStar = pity.Roll(rng); // resets the streak on success
Testing & dependency injection
Every helper here — dice, loot, weighted bags, selectors, shuffles, spatial sampling — is keyed to the
IRandomGenerator interface, not the concrete generator. So your game logic can depend on the
abstraction and take a mock or fixed-sequence fake in tests, while production passes a real seeded
generator:
// Logic depends only on the interface…
int AttackRoll(IRandomGenerator rng) => rng.Roll("1d20+5");
// …a real seeded generator in production…
IRandomGenerator rng = new RandomEnvironment("combat", rootSeed: 42).CreateAndRegister("attacks");
int hit = AttackRoll(rng);
// …a mock/fake in a unit test.
int forced = AttackRoll(fakeRng);
Determinism is a property of the seeded instance, not the reference type — passing a generator as
IRandomGenerator never changes its stream. (Environment registration still uses the concrete
RandomGenerator, since it owns the seeding/reproducibility contract the interface doesn't express.)
Thread safety
Generators are not thread-safe — each draw advances mutable state. For concurrent work, give each
thread its own generator from an environment (env.CreateAndRegister(perThreadKey)); because their seeds
derive from the shared root, that is both thread-safe and reproducible. RandomManager.Scratch is
likewise single-threaded by contract.
License
Licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on BeeneticToolkit.Random:
| Package | Downloads |
|---|---|
|
BeeneticToolkit
Meta-package that references the full BeeneticToolkit family: BeeneticToolkit.Random, BeeneticToolkit.Collections, BeeneticToolkit.Numerics, BeeneticToolkit.Logging, BeeneticToolkit.Spatial, and BeeneticToolkit.BigMath. Install this to get everything, or install the individual packages to take only what you need. |
|
|
BeeneticToolkit.Godot
Thin Godot shell helpers for the Beenetic game-dev standard: composition-root bootstrap, a Godot logging sink for BeeneticToolkit.Logging, save/offline store, node pooling, and a fixed-step tick driver. Consumes BeeneticToolkit; the pure game core never references this. |
GitHub repositories
This package is not used by any popular GitHub repositories.
See the changelog: https://github.com/Beenetic-Studios/BeeneticToolkit/blob/master/CHANGELOG.md