I\'m a web-game developer and I got a problem with random numbers. Let\'s say that a player has 20% chance to get a critical hit with his sword. That means, 1 out of 5 hits
Over such a small number of tests you should expect results like that:
True randomness is only predictable over a huge set size, such that it's quite possible to flip a coin and get heads 3 times in a row first time, however over a few million flips you will end up with apporximately 50-50.
OP,
Pretty much, if you want it to be fair, its not going to be random.
The problem of your game is the actual match length. The longer the match is, the less randomness you are going to see(crits will tend to be 20%) and its going to approach your intended values.
You got two options, pre-calculate the attacks based on previous rolls. Which will you get one crit every 5 attacks(based on yours 20%), but you can make the order it occurs random.
listOfFollowingAttacks = {Hit,Hit,Hit,Miss,Crit};
That's the pattern you want. So make it choose randomly from that list, until its empty, them re-create it.
That's a pattern I created for my game, it works quite well, for what I want it to do.
your second option, would be, increase the chance to crit, you are probably going to see a more even number in the end of all attacks(presuming that your matches ends rather quickly). The less % chance, the more RNG'd you get.
mt_rand() is based on a Mersenne Twister implementation, which means it yields one of the best random distributions you can get.
Apparently what you want is not randomness at all, so you should start out specifying exactly what you want. You'll probably realize you have conflicting expectations - that the results should be truely random and not predictable, yet at the same time they should not exhibit local variations from the stated probability - but then it becomes predictable. If you set a maximum of 10 non-crits in a row, then you've just told players "if you've had 9 non-crits in a row, the next one will be critical with 100% certainty" - you might as well not bother with randomness at all.
If you want a distribution that discourages repeat values, you could use a simple repeat rejection algorithm.
e.g.
int GetRand(int nSize)
{
return 1 + (::rand() % nSize);
}
int GetDice()
{
static int nPrevious=-1;
while (1) {
int nValue = GetRand(6);
// only allow repeat 5% of the time
if (nValue==nPrevious && GetRand(100)<95)
continue;
nPrevious = nValue;
return nValue;
}
}
This code rejects repeat values 95% of the time, making repeats unlikely but not impossible. Statistically it is a bit ugly, but it will probably produce the results you want. Of course, it won't prevent a distribution like "5 4 5 4 5". You could get fancier and reject the second last (say) 60% of the time and third last (say) 30%.
I'm not recommending this as good game design. Simply suggesting how to achieve what you want.
Well, if you are into math a little, you can probably try Exponential distribution
For example, if lambda = 0.5, expected value is 2 (go read that article!), means you will most probably hit/crit/whatever every 2nd turn (like 50%, huh?). But with such probability distribution, you will definetevely miss (or do opposite to whatever) at 0th turn (the one, in which event had already occured and turn_counter had been reseted), have like 40% chance to hit next turn, about 65% chance to do it 2nd (next after next) turn, about 80% to hit 3rd and so on.
The whole purpose of that distribution is if one has 50% hit chance and he misses 3 times in a row, he wil shurely (well, over 80% chance, and it increases every next turn) hit. It leads to more "fair" results, keeping overal 50% chance unchanged.
Taking your 20% chance of crit, you have
Its still about 0.2% (vs those 5%) chance of 3 crits + 2 non-crits in 5 consequent turns. And there is 14% chance of 4 consequent non-crits, 5% of 5, 1.5% for 6, 0.3% for 7, 0.07% for 8 consequent non-crits. I bet its "more fair" than 41%, 32%, 26%,21% and 16%.
Hope you still don't bored to death.
I would propose the following "randomly delayed putback die":
in-array
) initially filled with the values from 0 to n-1, the other (out-array
) emptyin-array
in-array
to out-array
out-array
back into in-array
This has the property that it will "react" more slowly the bigger n is. For example, if you want a 20% chance, setting n to 5 and hitting on a 0 is "less random" than setting n to 10 and hitting on a 0 or 1, and making it 0 to 199 out of 1000 will be almost indistinguishable from true randomness over a small sample. You will have to adjust n to your sample size.