reckfellENGINE

Случайные числа: seed, состояние и взвешенный выбор

Воспроизводимое поле, независимые генераторы, восстановление State и проверка весов.

На этой странице

Повторить генерацию

Добавьте RandomField2D.cs к корню Entity2D отдельного C#-уровня 1.0.0. Задайте окно/viewport 640×420, соберите и нажмите F6. Поле из 96 маркеров повторяется при Seed = 42 и том же порядке вызовов. Смена Seed меняет поле. Это выбор точек, не создание физических игровых объектов.

Поле с фиксированным seed и тремя весами размеров

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

У каждого RandomNumberGenerator своё состояние. Конструктор вызывает Randomize; пример сразу присваивает Seed, чтобы старт был определённым. Вызовы чужого экземпляра не меняют последовательность этого генератора. Для независимых игровых систем удобно иметь отдельные экземпляры и контролировать порядок их использования. Глобальная случайная последовательность может измениться от случайного вызова в другом месте проекта.

Seed и State решают разные задачи

Seed запускает последовательность с начала. State продолжает её с сохранённого места. Сохраните State после нужного вызова, получите два числа, верните State и повторите эти же два вызова — значения должны совпасть. Этот сценарий проверен в движке.

Если сохраняете оба значения, при восстановлении сначала установите Seed, затем State: присваивание Seed сбрасывает внутреннее состояние. Не подменяйте State произвольным seed. Для повторения нужны та же реализация генератора, параметры, порядок вызовов и ветвления. Совпавший seed сам по себе не доказывает синхронность сетевой игры или одинаковую последовательность на будущих версиях.

Диапазоны и веса

МетодЗначение
RandiRange(a, b)Целое с включёнными границами a и b
Randf / RandfRangefloat в заданном диапазоне; не используйте как готовый индекс массива
Randfn(mean, deviation)Нормальное распределение, не ограниченный диапазон
RandWeighted(weights)Индекс выбранного веса, не сам вес

Для случайного индекса непустого массива используйте RandiRange(0, Length − 1). Randfn может выйти за mean ± deviation; ограничение Clamp меняет распределение. Для весов 1 : 3 : 6 относительные вероятности — 10%, 30%, 60%, а не точные количества в каждой сотне.

ValidWeights примера отвергает пустой массив, отрицательные/нефинитные веса, переполнение суммы и сумму 0. Нативный RandWeighted при пустом/отрицательном массиве сообщает ошибку и возвращает −1; при нулевой сумме выбора нет. Не используйте −1 как индекс. Нулевой вес в валидном наборе исключает этот вариант.

Генератор предназначен для игровой случайности. Для секретов и токенов нужен криптографический генератор. Проверки документации подтверждают диапазоны, повторение и отдельные случаи весов; они не являются сертификацией статистического качества или гарантией межплатформенного совпадения битов.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.