Since Hurricane mentioned in another thread what the basic pseudo-random number generator used by civ3 was, I decided to test it for 'streakiness'.
First off, the exact algorithm wasn't revealed, but linear congruent generators all work in the same way AFAIK. My implementation was as follows (in C):
unsigned int seed; /* global variable */
unsigned int prng(void)
{
unsigned long long dummy;
unsigned int tmp;
dummy = ((unsigned long long) seed * 1103515245) + 12345;
seed = dummy & 0xffffffff;
tmp = seed & 0xffc00000;
return(seed >> 22);
}
(on my machine, int is 32 bits, long long is 64 bits).
The bottom 32 bits of 'dummy' are kept as the next seed, and the higest 10 bits of seed are returned are to generate a random number between 0 and 1023 inclusive. As I understand it, this is how the civ3 PRNG works. If any of the programmers wish to correct me...
This PRNG generates a nice flat distribution, with all values being equally likely. The question for combat in civ3 is whether the generator is streaky - whether it has a tendency to produce say 3 or 4 low numbers in a row. Of course there are various tests that can be done on this, but I set it up to simulate 100,000,000 combats in civ3 between a 4 hp, 6.0 attack unit attacking a 4 hp 2.75 defense unit (i.e. an infantry attacking a fortified spearman across a river).
The test is to compare the final hp's left for the winner vs. the distribution expected if the numbers were perfectly random. If there is any streakiness in the random numbers, then we expect to see an excess of results where one of the units is left on 4 hp, and a defecit of results where the surviving unit is left on 1hp (a fact which I confirmed by running 10,000,000 combats with a PRNG with streakiness deliberately inserted).
The results for 100,000,000 battles fought (numbers given are the percentage chance of a given outcome).
spearman
hp left expected prob observed prob
1 6.249 6.249 +/- 0.003
2 4.551 4.550 +/- 0.002
3 2.652 2.650 +/- 0.002
4 0.966 0.967 +/- 0.001
infantry
hp left
1 13.686 13.691 +/- 0.004
2 21.829 21.824 +/- 0.005
3 27.854 27.855 +/- 0.005
4 22.214 22.215 +/- 0.005
The uncertainties quoted in the observed probabilities are 1 sigma errors (meaning that you'd expect the 'expected' and 'observed' values to match within this error in roughly 2/3 of the cases).
Pretty obviously, there is no significant deviation away from the prediction of purely random numbers (i.e. no streakiness). I also did a simulation with the spearman fighting 2 attackers in a row, which also showed no significant deviation from the expected distribution.
So, assuming my version of the civ3 PRNG is accurate, it is pretty safe to say that there is no undue streakiness causing strange combat results in civ3; whatever streakiness there is in the generator doesn't show up in 100,000,000 combats (probably 20,000-200,000 games worth, depending on how often you go to war).
So, if you are convinced that you are getting unusual results too often, then you'll have to chalk it up to either a bug or a deliberate cheat on the part of the programmers (or more plausbily, the fact that our 'natural' understanding of randomness *very* badly underestimates the number of long streaks of odd results you can get).
First off, the exact algorithm wasn't revealed, but linear congruent generators all work in the same way AFAIK. My implementation was as follows (in C):
unsigned int seed; /* global variable */
unsigned int prng(void)
{
unsigned long long dummy;
unsigned int tmp;
dummy = ((unsigned long long) seed * 1103515245) + 12345;
seed = dummy & 0xffffffff;
tmp = seed & 0xffc00000;
return(seed >> 22);
}
(on my machine, int is 32 bits, long long is 64 bits).
The bottom 32 bits of 'dummy' are kept as the next seed, and the higest 10 bits of seed are returned are to generate a random number between 0 and 1023 inclusive. As I understand it, this is how the civ3 PRNG works. If any of the programmers wish to correct me...
This PRNG generates a nice flat distribution, with all values being equally likely. The question for combat in civ3 is whether the generator is streaky - whether it has a tendency to produce say 3 or 4 low numbers in a row. Of course there are various tests that can be done on this, but I set it up to simulate 100,000,000 combats in civ3 between a 4 hp, 6.0 attack unit attacking a 4 hp 2.75 defense unit (i.e. an infantry attacking a fortified spearman across a river).
The test is to compare the final hp's left for the winner vs. the distribution expected if the numbers were perfectly random. If there is any streakiness in the random numbers, then we expect to see an excess of results where one of the units is left on 4 hp, and a defecit of results where the surviving unit is left on 1hp (a fact which I confirmed by running 10,000,000 combats with a PRNG with streakiness deliberately inserted).
The results for 100,000,000 battles fought (numbers given are the percentage chance of a given outcome).
spearman
hp left expected prob observed prob
1 6.249 6.249 +/- 0.003
2 4.551 4.550 +/- 0.002
3 2.652 2.650 +/- 0.002
4 0.966 0.967 +/- 0.001
infantry
hp left
1 13.686 13.691 +/- 0.004
2 21.829 21.824 +/- 0.005
3 27.854 27.855 +/- 0.005
4 22.214 22.215 +/- 0.005
The uncertainties quoted in the observed probabilities are 1 sigma errors (meaning that you'd expect the 'expected' and 'observed' values to match within this error in roughly 2/3 of the cases).
Pretty obviously, there is no significant deviation away from the prediction of purely random numbers (i.e. no streakiness). I also did a simulation with the spearman fighting 2 attackers in a row, which also showed no significant deviation from the expected distribution.
So, assuming my version of the civ3 PRNG is accurate, it is pretty safe to say that there is no undue streakiness causing strange combat results in civ3; whatever streakiness there is in the generator doesn't show up in 100,000,000 combats (probably 20,000-200,000 games worth, depending on how often you go to war).
So, if you are convinced that you are getting unusual results too often, then you'll have to chalk it up to either a bug or a deliberate cheat on the part of the programmers (or more plausbily, the fact that our 'natural' understanding of randomness *very* badly underestimates the number of long streaks of odd results you can get).
Comment