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
Hopefully this article will aid you: http://web.archive.org/web/20090103063439/http://www.gamedev.net:80/reference/design/features/randomness/
This method of generating 'random numbers' is common in rpg/mmorpg games.
The problem it solves is this (extract):
A blade spider is at your throat. It hits and you miss. It hits again and you miss again. And again and again, until there's nothing left of you to hit. You're dead and there's a two-ton arachnid gloating over your corpse. Impossible? No. Improbable? Yes. But given enough players and given enough time, the improbable becomes almost certain. It wasn't that the blade spider was hard, it was just bad luck. How frustrating. It's enough to make a player want to quit.
It's not really clear what you want. It is possible to create a function such that the first 5 times you call it, it returns the numberes 1-5 in a random order.
But that's not really random. The player will know that he's going to get exactly one 5 in the next 5 attacks. It might be what you want though, and in that case, you simply have to code it yourself. (create an array containing the numbers and then shuffle them)
Alternatively, you could keep using your current approach, and assume that your current results are due to a bad random generator. Note that nothing may be wrong with your current numbers. Random values are random. sometimes you get 2, 3 or 8 of the same value in a row. Because they're random. A good random generator just guarantees that on average, all the numbers will be returned equally often.
Of course if you've been using a bad random generator, that might have skewed your results, and if so, simply switching to a better random generator should fix the problem. (Check out the Boost.Random library for better generators)
Alternatively, you could remember the last N values returned by your random function, and weigh the result by those. (a simple example would be, "for each occurrence of the new result, there's a 50% chance we should discard the value and get a new one"
If I had to guess, I'd say sticking with "actual" randomness is your best bet. Make sure you use a good random generator, and then keep going the way you're doing it now.
Pre-calculate a random critical hit for each player.
// OBJECT
//...
// OnAttack()
//...
c_h = c_h -1;
if ( c_h == 0 ) {
// Yes, critical hit!
c_h = random(5) + 1 // for the next time
// ...
}
What about making the chance of critical depend on the last N attacks. One simple scheme is some kind of markov chain: http://en.wikipedia.org/wiki/Markov_chain but the code is very simple anyway.
IF turns_since_last_critical < M THEN
critial = false
turns_since_last_critical++;
ELSE
critial = IsCritical(chance);
IF Critial THEN
turns_since_last_critica = 0;
ELSE
turns_since_last_critica++;
END IF;
END IF;
Of course you must make your maths because the chance of a critical is lower than the chance of a critical once you know that it has been enough turns since the last one
The top few answers are great explanations, so I'll just focus on an algorithm that gives you control over the probability of "bad streaks" while never becoming deterministic. Here's what I think you should do:
Instead of specifying p, the parameter of a Bernoulli distribution, which is your probability of a critical hit, specify a and b, the parameters of the beta distribution, the "conjugate prior" of the Bernoulli distribution. You need to keep track of A and B, the number of critical and non-critical hits so far.
Now, to specify a and b, ensure that a/(a+b) = p, the chance of a critical hit. The neat thing is that (a+b) quantifies how close you want the A/(A+B) to be to p in general.
You do your sampling like this:
let p(x)
be the probability density function of the beta distribution. It is available in many places, but you can find it in the GSL as gsl_ran_beta_pdf.
S = A+B+1
p_1 = p((A+1)/S)
p_2 = p(A/S)
Choose a critical hit by sampling from a bernoulli distribution with probability p_1 / (p_1 + p_2)
If you find that the random numbers have too many "bad streaks", scale up a and b, but in the limit, as a and b go to infinity, you will have the shuffle bag approach previously described.
If you implement this, please let me know how it goes!
Given the behavior you're asking for, I think you're randomizing the wrong variable.
Rather than randomizing whether this hit will be critical, try randomizing the number of turns until the next critical hit occurs. For example, just pick a number between 2 & 9 every time the player gets a critical, and then give them their next critical after that many rounds have passed. You can also use dice methods to get closer to a normal distribution -- for example, you will get your next critical in 2D4 turns.
I believe this technique gets used in RPGs that have random encounters in the overworld as well -- you randomize a step counter, and after that many steps, you get hit again. It feels a lot more fair because you almost never get hit by two encounters in a row -- if that happens even once, the players get irritable.