【问题标题】:How do you get the current seed of Random in C#?你如何在 C# 中获得 Random 的当前种子?
【发布时间】:2011-05-13 00:31:55
【问题描述】:

在我的游戏中,我将使用随机值来选择玩家从箱子中获得的奖励。问题是你可以快速保存和快速加载,这意味着他们可以继续重新加载以重新随机化,直到他们得到他们想要的。有什么方法可以让我获得Random 对象的当前种子值,并在它们加载时返回到同一点,这样它们就不会滥用随机化?

【问题讨论】:

    标签: c# random


    【解决方案1】:

    不确定获取种子,但您可以保存您给Random 对象的值。请记住,有两个构造函数。第二个是Random(Int32),所以如果你自己设置种子(一个足够简单的值是Environment.TickCount),你可以在将它传递给构造函数之前将该值存储在某个地方。如果您还没有阅读它,请查看位于https://docs.microsoft.com/en-us/dotnet/api/system.random 的 MSDN 文档。

    【讨论】:

    • 我现在用它来保存种子,效果很好,谢谢!
    • 我建议您考虑一下您已经取得的成就 - 基本上,用户现在可以保证将下一个随机事件绑定到此 Random 对象,以获得仅取决于初始配置的特定结果。可能适合您的游戏。
    • 请注意,如果您也将随机对象用于其他任何事情,则可能无法保证每次都能获得相同的奖励。例如,如果有两个不同的游戏对象具有使用相同随机数生成器的随机事件触发器,您可以保存、触发一个然后另一个、重新加载并以不同的顺序再次触发以获得不同的结果。如果两个对象都是箱子,并且它们每个都有相同的奖励集可供选择,那么您仍然会获得相同的奖励,只是交换了,但如果对象类型或奖励集不同,您将无法获得所需的一致性。
    • 如果您查看source code of Random,您会发现以下内容:public Random() : this(Environment.TickCount) { } 所以您所要做的就是将Environment.TickCount 存储在类字段中并将其提供给构造函数。 你去,你已经保存了种子。 :)
    • 请注意,System.DateTime.Now.Millisecond 仅从 0 变为 1000,这不是很好。更好地使用Environemt.TickCount(正如@Shakaron 指出的那样)(每 ± 50 天滚动一次,精度为 15 毫秒)
    【解决方案2】:

    这是不可能的。

    相反,您可以使用二进制序列化来序列化 Random 实例。
    Random[Serializable],种子和内部状态将保持不变。

    但是请注意,保存随机种子可以让您的玩家预测未来,如果您允许在战斗中保存,这将非常有用。

    另外请注意,用户仍然可以保存、打开宝箱、加载、执行生成随机数的操作,然后从宝箱中获取不同的物品。

    【讨论】:

    • 感谢您的洞察力。我也在考虑他们仍然可以去另一个箱子上用完那个数字然后回来。我最想避免的是不断地重新加载 50 多次以尝试获得稀有物品,例如,如果他们在其他地方使用该随机物品,这不会太困扰我。
    • 您可以在加载关卡时预先生成所有箱子(和类似物品)的内容。为关卡加载创建一个新的随机实例,并确保以某种明确定义的顺序初始化箱子。加载后扔掉实例,但记住保存游戏的初始种子。
    • 这是另一种选择,我可能只使用 Gnafoo,它全部由地图数据生成,然后就不用担心了。
    • +1。您还可以考虑为不同类型的“随机”事件使用多个 Random 对象。 IE。眼睛糖果的东西在一个随机对象上,而战斗结果在另一个对象上并在另一个对象上掠夺。
    • This is not possible. 这正是我要寻找的。​​span>
    【解决方案3】:

    您可以将随机奖励计算为以下哈希函数:

    1. 在您开始游戏时分配的一些种子,并在已保存游戏中持续存在;和
    2. 宝箱的某些常量属性在所有游戏中都是不变的(例如,固定 ID,或者如果它从不移动,则它的位置)。

    此方法的优点是,无论您保存和重玩多少次,即使箱子以不同的顺序打开或触发了其他“随机”事件,给定的箱子在给定的游戏中总是会产生相同的奖励以不同的顺序。此外,每个箱子的奖励与其他箱子的奖励无关,只要在哈希中使用的箱子的属性是独立的。

    在以下示例中,GetRewardId 生成奖励 ID,作为游戏种子的哈希值与箱子的 x 坐标进行异或运算。它使用Random 执行哈希,将哈希输入作为Random 对象的种子,并将第一个随机生成的数字作为输出。

    private static int GetRewardId(int seed, float coord, int numRewards)
    {
        int tempSeed = BitConverter.ToInt32(BitConverter.GetBytes(coord), 0) ^ seed;
        return new Random(tempSeed).Next(numRewards);
    }
    
    int seed  = new Random().Next();
    int numDifferentRewards = 5;
    float xCoordinate = chest.Position.X;
    int rewardId = GetRewardId(seed, xCoordinate, numDifferentRewards);
    

    如果您的许多胸部可能在 sace 中对齐,您可能希望通过与 y 和/或 z 坐标进行异或来选择不同的属性或使用其他尺寸。

    【讨论】:

      【解决方案4】:

      确实,Seedisn't stored 与初始化后的算法无关。它的派生词之一 mj 存储在 SeedArray 中,但您可以检查使用反射来比较两个 Random 实例:

      int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
      mj = MSEED - subtraction;
      SeedArray[55]=mj;
      

      所以您所要做的就是检查SeedArray 中的最后一个元素(索引55)。这是唯一使用Seed 的地方。

      [已删除问题How to determine if two Random instances have the same seed?的移动答案]

      【讨论】:

      • 我不确定它是否可靠。比较两个 Random 实例的整个 SeedArray 似乎更好,以确定它们将产生相同的序列,而不仅仅是最后一项。因为这个数组在Next 调用期间被修改(包括最后一个元素),所以最后一个元素可能相同,但其余元素不同。
      • 这是一个很好的观点。我没有通过整个算法来检查,但看来你是对的@Evk
      • 感谢您在这里回答我的(因为重复而删除)问题 :-)
      • 我想最好把它放在这里:) @TimSchmelter
      • 我认为你也需要考虑inext。 SeedArray + inext 似乎完全确定了输出,因此它们在两个 Random 实例中相等应该证明它们将产生相同的输出。
      【解决方案5】:

      不幸的是,在 Microsoft 的参考实现中,甚至没有保存 no arg ctor 的种子值,更不用说公开访问了: http://referencesource.microsoft.com/#mscorlib/system/random.cs,bb77e610694e64ca

      但是,正如您在参考实现中看到的那样,您可以传入的值(可能应该 -- 我知道我知道)是:Environment.TickCount

      所以将它保存到一个变量中,然后将该变量传递给接受 arg 的 ctor,您现在就知道了种子。不是事后,但这应该足以满足您的意图。

      【讨论】:

        【解决方案6】:

        我建议您生成一个随机数并将其用作真实随机数生成器的种子数。通过这种方法,您有一个实际上是随机数的种子编号,您可以保存您的种子编号以供进一步使用。

        【讨论】:

        • 用伪随机数播种伪随机数生成器仍然会给您一个伪随机数。这也不能真正回答问题。
        【解决方案7】:

        这仅与切线相关,但如果有人想知道为什么 Random 没有名为 Seed 的属性或名为 GetSeed() 的方法,我愿意打赌这可能是由于安全问题:您想将“随机”数字生成器的内部工作原理暴露给外界吗?绝对不!否则,一些客户端代码可能会四处乱窜,直到它获得您正在使用的值,然后对它们进行令人讨厌和意想不到的事情。

        【讨论】:

        【解决方案8】:

        我可能只是按照 MSDN 使用这个:http://msdn.microsoft.com/en-us/library/ctssatww.aspx

        Random(seed)
        

        其中种子是我从存储中加载的一些值。

        【讨论】:

        • -1 - 问题正好相反 - 如何获取当前种子,以便稍后在重新加载后用于 Random(seed) 调用。
        • @Alexei:如果您将种子保存在某个地方,那么您可以在以后的某个时间点获取种子。它不一定来自 Random 对象,据我所知也不一定。
        • 问题是如果你用说 5 播种 Random,并调用 Next 几次(即得到 3,7,6,1)并期望下一个值是 13,而是用原始值重新播种5 由于加载下一个 Random 将是 3 而不是 13。您需要当前的种子值(Random 对象的内部数据),而不是您可以轻松记住的原始值。
        • 查看已接受的答案,这正是 OP 想要的...编辑您的答案,这样我就可以给您 1 回馈给您,因为您拥有超棒的精神力量 :)
        猜你喜欢
        • 1970-01-01
        • 2012-04-29
        • 2021-07-18
        • 2021-10-17
        • 2011-08-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-02
        相关资源
        最近更新 更多