Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to grab the actual state of System.Random?

Tags:

c#

.net

random

I would like to be able to get the actual state or seed or whatever of System.Random so I can close an app and when the user restarts it, it just "reseeds" it with the stored one and continues like it was never closed.

Is it possible?

Using Jon's idea I came up with this to test it;

static void Main(string[] args)
{
    var obj = new Random();
    IFormatter formatter = new BinaryFormatter();
    Stream stream = new FileStream("c:\\test.txt", FileMode.Create, FileAccess.Write, FileShare.None);
    formatter.Serialize(stream, obj);
    stream.Close();
    for (var i = 0; i < 10; i++)
        Console.WriteLine(obj.Next().ToString());

    Console.WriteLine();

    formatter = new BinaryFormatter();
    stream = new FileStream("c:\\test.txt", FileMode.Open, FileAccess.Read, FileShare.Read);
    obj = (Random)formatter.Deserialize(stream);
    stream.Close();
    for (var i = 0; i < 10; i++)
        Console.WriteLine(obj.Next().ToString());

    Console.Read();
}
like image 560
Fredou Avatar asked Nov 18 '11 20:11

Fredou


2 Answers

It's serializable, so you may find you can just use BinaryFormatter and save the byte array...

Sample code:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class Program
{
    public static void Main(String[] args)
    {
        Random rng = new Random();
        Console.WriteLine("Values before saving...");
        ShowValues(rng);

        BinaryFormatter formatter = new BinaryFormatter(); 
        MemoryStream stream = new MemoryStream();
        formatter.Serialize(stream, rng);

        Console.WriteLine("Values after saving...");
        ShowValues(rng);

        stream.Position = 0; // Rewind ready for reading
        Random restored = (Random) formatter.Deserialize(stream);

        Console.WriteLine("Values after restoring...");
        ShowValues(restored);       
    }

    static void ShowValues(Random rng)
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine(rng.Next(100));
        }
    }
}

Results on a sample run are promising:

Values before saving...
25
73
58
6
33
Values after saving...
71
7
87
3
77
Values after restoring...
71
7
87
3
77

Admittedly I'm not keen on the built-in serialization, but if this is for a reasonably quick and dirty hack, it should be okay...

like image 121
Jon Skeet Avatar answered Oct 31 '22 14:10

Jon Skeet


Here's a dotnet 5.0 or later struct that can save and load the current state of a System.Random instance. This can surely be improved but it works as it should. Also, I'm pretty sure it only works with System.Random instances that have been created with a Seed parameter (System.Random(int Seed)). I noticed while debugging that using the parameterless constructor of System.Random yields an other type of ImplBase named XoshiroImpl that lacks seed support therefore making my code unusable.

This can be serialized with System.TEx

Net5CompatSeedImpl class: https://github.com/dotnet/runtime/blob/f7633f498a8be34bee739b240a0aa9ae6a660cd9/src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs#L283

XoshiroImpl class: https://github.com/dotnet/runtime/blob/4017327955f1d8ddc43980eb1848c52fbb131dfc/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro128StarStarImpl.cs

using System.Reflection;
public struct RandomState
{
    static RandomState()
    {
        ImplInfo = typeof(Random).GetField("_impl", BindingFlags.Instance | BindingFlags.NonPublic)!;
        PrngInfo = Type.GetType(Net5CompatSeedImplName)!.GetField("_prng", BindingFlags.Instance | BindingFlags.NonPublic)!;
        Type compatPrngType = Type.GetType(CompatPrngName)!;
        seedArrayInfo = compatPrngType.GetField(SeedArrayInfoName, BindingFlags.Instance | BindingFlags.NonPublic)!;
        inextInfo = compatPrngType.GetField(InextInfoName, BindingFlags.Instance | BindingFlags.NonPublic )!;
        inextpInfo = compatPrngType.GetField(InextpInfoName, BindingFlags.Instance | BindingFlags.NonPublic)!;
    }
    public const string CompatPrngName = "System.Random+CompatPrng";
    public const string Net5CompatSeedImplName = "System.Random+Net5CompatSeedImpl";
    public const string SeedArrayInfoName = "_seedArray";
    public const string InextInfoName = "_inext";
    public const string InextpInfoName = "_inextp";
    private static FieldInfo ImplInfo;
    private static FieldInfo PrngInfo;
    private static FieldInfo seedArrayInfo;
    private static FieldInfo inextInfo;
    private static FieldInfo inextpInfo;

    public int[] seedState { get; set; }
    public int inext { get; set; }
    public int inextp { get; set; }
    public static RandomState GetState(Random random)
    {
        object o = GetCompatPrng(random);
        RandomState state = new RandomState();
        state.seedState = (int[])seedArrayInfo.GetValue(o)!;
        state.inext = (int)inextInfo.GetValue(o)!;
        state.inextp = (int)inextpInfo.GetValue(o)!;
        return state;

    }
    //Random > Impl > CompatPrng
    public static object GetImpl(Random random) => ImplInfo.GetValueDirect(__makeref(random))!;
    public static object GetCompatPrng(object impl) => PrngInfo.GetValueDirect(__makeref(impl))!;
    public static object GetCompatPrng(Random random)
    {
        object impl = GetImpl(random);
        return PrngInfo.GetValueDirect(__makeref(impl))!;
    }
    public static void SetState(Random random, RandomState state)
    {
        object impl = GetImpl(random);
        TypedReference implref = __makeref(impl);

        object prng = PrngInfo.GetValueDirect(implref)!;

        seedArrayInfo.SetValue(prng, state.seedState);
        inextInfo.SetValue(prng, state.inext);
        inextpInfo.SetValue(prng, state.inextp);

        PrngInfo.SetValueDirect(implref, prng);
        //Testing. can be removed.
        /*object o2 = GetCompatPrng(impl);
        DehFwk.Debug.Log("orig: " + ((int[])seedArrayInfo.GetValue(prng)!).Length + "| new: " + ((int[])seedArrayInfo.GetValue(o2)!).Length + " vs " + state.seedState.Length);
        DehFwk.Debug.Log("orig: " + inextInfo.GetValue(prng)! + " " + "| new: " + inextInfo.GetValue(o2) + " vs " + state.inext);
        DehFwk.Debug.Log("orig: " + inextpInfo.GetValue(prng) + "| new: " + inextpInfo.GetValue(o2) + " vs " + state.inextp);*/

    }
}
like image 1
Pat Avatar answered Oct 31 '22 15:10

Pat