reckfellENGINE

Random numbers: seed, state and weighted selection

A reproducible field, independent generators, State restoration and weight validation.

On this page

Replay generation

Attach RandomField2D.cs to an Entity2D root in a separate 1.0.0 C# level. Set the window/viewport to 640×420, build and press F6. The 96-marker field repeats with Seed = 42 and the same call order. A different Seed changes it. This chooses points; it does not spawn physics gameplay objects.

A fixed-seed field with three marker-size weights

RandomField2D.cs
C#
using System;
using Reckfell;

public partial class RandomField2D : Entity2D
{
    [Export] public ulong Seed { get; set; } = 42;
    [Export] public int Count { get; set; } = 96;
    public int PointCount => _points.Length;
    public ulong FinalState { get; private set; }
    private Vector2[] _points = [];
    private int[] _kinds = [];
    private Label? _status;

    public Vector2 GetPoint(int index) => _points[index];
    public int GetKind(int index) => _kinds[index];

    public static bool ValidWeights(float[]? weights)
    {
        if (weights is null || weights.Length == 0) return false;
        float sum = 0;
        foreach (float weight in weights)
        {
            if (!float.IsFinite(weight) || weight < 0) return false;
            sum += weight;
        }
        return float.IsFinite(sum) && sum > 0;
    }

    public static int Choose(RandomNumberGenerator rng, float[] weights)
    {
        if (!ValidWeights(weights)) throw new ArgumentException("Weights require a finite positive sum", nameof(weights));
        return checked((int)rng.RandWeighted(weights));
    }

    public Error Generate(ulong seed, int count)
    {
        if (count < 1 || count > 256) return Error.InvalidParameter;
        Vector2[] points = new Vector2[count];
        int[] kinds = new int[count];
        using var rng = new RandomNumberGenerator { Seed = seed };
        float[] weights = [1, 3, 6];
        for (int i = 0; i < count; i++)
        {
            points[i] = new Vector2(rng.RandfRange(40, 600), rng.RandfRange(125, 380));
            kinds[i] = Choose(rng, weights);
        }
        FinalState = rng.State;
        _points = points;
        _kinds = kinds;
        Seed = seed;
        Count = count;
        if (_status is not null) _status.Text = "Seed = " + seed + "; n = " + count;
        QueueRedraw();
        return Error.Ok;
    }

    public override void _Ready()
    {
        bool ru = TranslationServer.GetLocale().StartsWith("ru");
        var title = new Label { Position = new Vector2(24, 20), Text = ru
            ? "Один seed и порядок вызовов — одно поле" : "Same seed and call order, same field" };
        title.AddThemeFontSizeOverride("font_size", 22); AddChild(title);
        _status = new Label { Position = new Vector2(24, 58) }; AddChild(_status);
        AddChild(new Label { Position = new Vector2(24, 85), Text = ru
            ? "Размеры маркеров: веса 1 : 3 : 6; это не точные количества."
            : "Marker sizes use weights 1 : 3 : 6, not exact counts." });
        if (Generate(Seed, Count) != Error.Ok) ReckfellApi.PrintErr("RandomField2D: count must be 1..256");
    }

    public override void _Draw()
    {
        DrawRect(new Rect2(0, 0, 640, 420), new Color(0.035f, 0.04f, 0.05f));
        for (int i = 0; i < _points.Length; i++)
            DrawCircle(_points[i], 2 + 2 * _kinds[i], _kinds[i] == 0 ? Colors.White : new Color(0.25f, 0.46f, 1));
    }
}

Each RandomNumberGenerator owns its state. Its constructor calls Randomize; the example immediately assigns Seed for a defined start. Another instance's calls do not alter this sequence. Separate instances with controlled call order are useful for independent gameplay systems. A global sequence can change because of an unrelated call elsewhere in the project.

Seed and State serve different purposes

Seed restarts a sequence. State resumes it from a saved point. Save State, obtain two numbers, restore State and repeat the same two calls: values should match. The engine fixture checks this case.

If saving both values, restore Seed before State: assigning Seed resets internal state. Do not substitute an arbitrary seed for State. Replay requires the same generator implementation, parameters, call order and branching. A matching seed alone does not establish network synchronization or identical output in future versions.

Ranges and weights

MethodResult
RandiRange(a, b)Integer including both a and b
Randf / RandfRangeFloat in the selected range; not a ready-made array index
Randfn(mean, deviation)Normal distribution, not a bounded interval
RandWeighted(weights)Index of the selected weight, not its value

For a nonempty array index, use RandiRange(0, Length − 1). Randfn can exceed mean ± deviation; clamping changes its distribution. Weights 1 : 3 : 6 give relative probabilities of 10%, 30%, 60%, not exact counts in every hundred draws.

The example's ValidWeights rejects empty input, negative/nonfinite weights, sum overflow and zero total. Native RandWeighted logs an error and returns −1 for empty/negative input; a zero total selects nothing. Do not use −1 as an index. A zero weight in an otherwise valid set excludes that option.

Use this generator for gameplay randomness. Secrets and tokens require a cryptographic generator. Documentation checks establish ranges, replay and selected weight cases; they do not certify statistical quality or bitwise cross-platform determinism.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.