简体   繁体   中英

How do you get the current seed of Random in C#?

In my game I'm going to use random values to pick the reward the player gets from a chest. The problem is that you can quick save and quick load and that means they can keep reloading to re-randomize until they get what they want. Is there some way that I could get the current seed value of my Random object and possibly return to that same point when they load so that they couldn't abuse the randomization?

This is not possible.

Instead, you can serialize the Random instance using binary serialization.
Random is [Serializable] , and the seed and internal state will persist.

Note, however, that saving the random seed allows your players to predict the future, which is very useful if you allow saving in battle.

Also note that users can still save, open the chest, load, perform an action that generates a random number, then get a different item from the chest.

Not sure on getting the seed, but you could save the value you give to the Random object. Remember, there are two constructors. The second is Random(Int32) , so if you set the seed yourself (an easy enough value is Environment.TickCount ), you could store that value somewhere before you pass it to the constructor. If you haven't read it yet, check out the MSDN documentation at https://docs.microsoft.com/en-us/dotnet/api/system.random .

Indeed, the Seed isn't stored as it is not relevant for the algorithm after initialization. One of its derivatives, mj , is stored in the SeedArray though, you can check that using Reflection to compare both Random instances:

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

So all you have to do is to check the last element (index 55) in the SeedArray . This is the only place Seed is used.

[Moved answer from deleted question How to determine if two Random instances have the same seed? ]

This is only related on a tangent, but in case anyone is wondering why Random doesn't have a property called Seed or a method called GetSeed() , I'm willing to wager that it's likely due to security concerns: Would you want to expose the inner workings of your "random" number generator to the outside world? Absolutely not! Otherwise, some client code could poke around until it got the values you were using and then do nasty and unintended things with them.

I'd probably just use this as per MSDN: http://msdn.microsoft.com/en-us/library/ctssatww.aspx

Random(seed)

where seed is some value I've loaded from storage.

Unfortunately, in the reference implementation from Microsoft, the no arg ctor's seed value is not even saved, let alone exposed for access: http://referencesource.microsoft.com/#mscorlib/system/random.cs,bb77e610694e64ca

However, as you can also see in the reference implementation, the value you can pass in (probably should -- I know I do), just like they do, is: Environment.TickCount

So save that to a variable, then pass that variable in to the ctor that takes an arg and you now know the seed. Not after the fact, but this should be sufficient for whatever your intent is.

You can calculate the random reward as a hash function of:

  1. some seed that is assigned when you begin a new game, and is persisted in saved games; and
  2. some constant property of a chest that is invariant across all games (eg a fixed ID, or its position if it never moves).

This method has the advantage that a given chest will always yield the same reward in a given game, no matter how many times you save and replay, even if chests are opened in different orders, or other 'random' events are triggered in different orders. Also each chest's reward is independent of other chests' rewards, so long as the chest's property used in the hash is independent.

In the following example GetRewardId generates a reward ID as a hash of the game seed XORed with the x coordinate of a chest. It uses Random to perform the hash, by using the hash input as the Random object's seed, and taking the first randomly generated number as the output.

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

If many of your chests are likely to be aligned in sace, you may want to choose a different property, or use additional dimensions, by XORing with the y and/or z coordinates too.

I recommend you to generate a random number and use it as a seed number to your real random number generator. By this method you have a seed number that is actually a random number and you can save your seed number for further using.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM