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 开发者_如何学Pythonthat 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://learn.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?]
You can calculate the random reward as a hash function of:
- some seed that is assigned when you begin a new game, and is persisted in saved games; and
- some constant property of a chest that is invariant across all games (e.g. 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'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.
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.
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.
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.
精彩评论