testing SDI

Where the hell you see any recursive call!?
What you look at is CyTeam.cpp not CvTeam.cpp

cyXXX are interface wrappers to avoid null-pointer calls, nothing like recursion. The entire python stuff if so poorly done but there is nothing like recursion, actually civ developers are so fond of brute force everything and inner loops that seeing a recursion would be a bliss.

Doh.
 
where is cvteam.cpp mentioned? Better yet, where is getsorenrandnum defined?

getsorenrandnum is only mentioned twice, in the form of
Spoiler :
Code:
												iValue += GC.getGameINLINE().getSorenRandNum(10, "Found Corporation (Player)");
http://civ4bug.sourceforge.net/PythonAPI/index.html only mentions getsorenrandnum in cyteam.cpp

This is like a terrible scavenger hunt.
 
^ not really, it's in CvGame.cpp
Code:
int CvGame::getSorenRandNum(int iNum, const char* pszLog)
{
	return m_sorenRand.get(iNum, pszLog);
}

m_sorenRand is from class CvRandom (in CvRandom.cpp)
 
If you flip a coin 100 times then you would expect a couple of heads only and tails only runs. i.e 7 heads in row. School teachers will set this as a homework problem, then spot the people that didn't do it and faked it because they will not had a long run of the same result.

I can fully understand 5 misses in a row lead to claims of AI manipulation, however it's the way probabilty works.
 
^ not really, it's in CvGame.cpp
Code:
int CvGame::getSorenRandNum(int iNum, const char* pszLog)
{
	return m_sorenRand.get(iNum, pszLog);
}



m_sorenRand is from class CvRandom (in CvRandom.cpp)

Since I had to look up most of this, I might as well share what I found

Code:
void CvRandom::init(unsigned long ulSeed)
{
	//--------------------------------
	// Init saved data
	reset(ulSeed);

	//--------------------------------
	// Init non-saved data
}

Spoiler reset() :
Code:
void CvRandom::reset(unsigned long ulSeed)
{
	//--------------------------------
	// Uninit class
	uninit();

	m_ulRandomSeed = ulSeed;
}
Important thing is m_ulRandomSeed is initialized as ulSeed.

Code:
unsigned short CvRandom::get(unsigned short usNum, const TCHAR* pszLog)
{
	if (pszLog != NULL)
	{
		if (GC.getLogging() && GC.getRandLogging())
		{
			if (GC.getGameINLINE().getTurnSlice() > 0)
			{
				TCHAR szOut[1024];
				sprintf(szOut, "Rand = %d on %d (%s)\n", getSeed(), GC.getGameINLINE().getTurnSlice(), pszLog);
				gDLL->messageControlLog(szOut);
			}
		}
	}

	m_ulRandomSeed = ((RANDOM_A * m_ulRandomSeed) + RANDOM_C);

	unsigned short us = ((unsigned short)((((m_ulRandomSeed >> RANDOM_SHIFT) & MAX_UNSIGNED_SHORT) * ((unsigned long)usNum)) / (MAX_UNSIGNED_SHORT + 1)));

	return us;
}

So if m_sorenrand is in the cvrandom, .get should be the algorithm we're looking for.

Code:
	TCHAR szOut[1024];
creates character string called szOut of length 1024.
Code:
	sprintf(szOut, "Rand = %d on %d (%s)\n", getSeed(), GC.getGameINLINE().getTurnSlice(), pszLog);
sprintf prints the second term into our string szOut. The first %d is the number given by getSeed(), second %d is the number given by GC.getGameINLINE()..., and the %s is pszLog, which in our case should be the string "Nuke". Basically it's recording the conditions for our pseudo random variable.

getSeed itself is just m_ulRandomSeed, which is defined in the get function
Spoiler getSeed() :
Code:
unsigned long CvRandom::getSeed()
{
	return m_ulRandomSeed;
}
getTurnSlice isn't in this file, and since I'm sicking of playing Alice in Wonderland I'll assume it has something to do with where we'll check pick out of our random seed based on the position of the event in the turn. Hopefully it's not too important

Code:
	m_ulRandomSeed = ((RANDOM_A * m_ulRandomSeed) + RANDOM_C);

	unsigned short us = ((unsigned short)((((m_ulRandomSeed >> RANDOM_SHIFT) & MAX_UNSIGNED_SHORT) * ((unsigned long)usNum)) / (MAX_UNSIGNED_SHORT + 1)));

	return us;

Ah ha! Here's our random number. First our seed value is changed each time the same way: multiply by a constant and add a constant.
Second to generate our random number, our random seed is shifted by a set amount, something to do with the maximum length of unsigned short integer, (likely binary 2^n-1) it's multiplied by usNum (in our case getsorenrand(100,"nuke") means multiply by 100)), then it's divided by said max_unsigned_short+1 (likely 2^n), so chop off.

Oddly enough our "random" constants are more constant than random.
Code:
#define RANDOM_A      (1103515245)
#define RANDOM_C      (12345)
#define RANDOM_SHIFT  (16)

---

Hmmm, so could we attain strange nuke odds? If getrandsoren works properly and has a nicely random seed, no. In the short run, if calling getrandsoren(100,"nuke") repeatedly does not reset m_ulrandseed, since we're shifting it in the exact same way, albeit in a very large, repetitive way. You could have a really stupid seed, but that's pretty much the same as being unlucky.

Multiplying by 100 could induce some strange division results, but that should show up in basic tests. I have to say the onus is on someone for showing a reasonably large sample size of unlucky SDI interceptions.
 
The random function is quite standard, it's called 'Linear congruential' generator/method and it is very well described in D. Knuth's famous 'The Art of Computer Programming', Chapter 3 (random numbers).

It's the most used method for generating pseudo random numbers. The generator has some minor flows but nothing to affect the result of the SDI.

It has been bashed numerous of times on this very forum.
 
Now this is very interesting, I was reading the code, and apparently it multiplies getNukeInterception by ivalue, the number of the projects.

Spoiler :
Code:
void CvTeam::changeProjectCount(ProjectTypes eIndex, int iChange)
{
	CvWString szBuffer;
	bool bChangeProduction;
	int iOldProjectCount;
	int iI, iJ;

	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < GC.getNumProjectInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");

	if (iChange != 0)
	{
		GC.getGameINLINE().incrementProjectCreatedCount(eIndex, iChange);

		iOldProjectCount = getProjectCount(eIndex);

		m_paiProjectCount[eIndex] = (m_paiProjectCount[eIndex] + iChange);
		FAssert(getProjectCount(eIndex) >= 0);
		
		//adjust default art types
		if(iChange >= 0)
		{
			int defaultType = -1;
			for(int i=0;i<iChange;i++)
				m_pavProjectArtTypes[eIndex].push_back(defaultType);
		}
		else
		{
			for(int i=0;i<-iChange;i++)
				m_pavProjectArtTypes[eIndex].pop_back();
		}
		FAssertMsg(getProjectCount(eIndex) == (int)m_pavProjectArtTypes[eIndex].size(), "[Jason] Unbalanced project art types.");

		CvProjectInfo& kProject = GC.getProjectInfo(eIndex);

		changeNukeInterception(kProject.getNukeInterception() * iChange);

So if you have two SDI's, you will intercept whenever you roll under 150 (out of 100), and tactical nukes under 75. I tested it for tactical nukes, indeed it is 75%. Giving the AI two SDI's will cause tactical nukes to be intercepted 75% of the time.
So if you believe that the AI can have two SDIs, it can indeed intercept 75% of them and give you absurd strings of interceptions.

If you're really skeptical, you should be able to turn on the python console and look it up somehow.
 
So if you have two SDI's, you will intercept whenever you roll under 150 (out of 100), and tactical nukes under 75. I tested it for tactical nukes, indeed it is 75%. Giving the AI two SDI's will cause tactical nukes to be intercepted 75% of the time.
So if you believe that the AI can have two SDIs, it can indeed intercept 75% of them and give you absurd strings of interceptions.
I haven't looked at the code, but what you are describing sounds like there is a bug somewhere...

If there are two SDIs each with an independent 75% interception chance, then the total chance of interception should be ~94%. (The probably that the first will fail to intercept is 1/4, the probability that they will both fail is 1/4 * 1/4, so chance of success is 15/16). Ok - but I accept that there could be some strange game-logic which says that the SDIs aren't independent and that they interception probability actually add up to 150%. Well, it is nonsensical to have 150% probability of something happening, so lets call it 100% - ie. it incepts everything. But with 50% chance to evade interception, that should mean that the tactical nukes get through 50% of the time. If they got through less often than that then it would either imply that the SDI was getting multiple interception chances as well as having higher interception rates, or that the tactical nuke had less than 50% chance to evade.

If each SDI worked independently and had to be evaded independently, (as I think they probably should), then then probably that a tactical nuke gets through would be (1/2+1/8)^2 = 39%.

But whatever... none of this really matters, because you can't build more than one SDI anyway. So it doesn't really make much difference if there is a problem with how it is implemented.
 
If you flip a coin 100 times then you would expect a couple of heads only and tails only runs. i.e 7 heads in row. School teachers will set this as a homework problem, then spot the people that didn't do it and faked it because they will not had a long run of the same result.

I can fully understand 5 misses in a row lead to claims of AI manipulation, however it's the way probabilty works.

It's a little ignorant to counter what I'm saying with this. A string of 5 heads or tails has a 3% chance (each), and if you throw 100 flips you'd fully expect to encounter it on average. 7 consecutive heads or tails is a little under 1% each, but on average you should see one or the other in 100 throws.

Nuke interceptions here are supposedly a .375 probability. .375^7 is indeed a very small number. Encountering that and a .375^5 outcome in the same set of SIXTY FIVE (not 100) trials is a hell of a lot different from a set of coin flips which should on average appear. It's possible but statistically improbable, unless the assumption of .375 is wrong of course.

So if you believe that the AI can have two SDIs, it can indeed intercept 75% of them and give you absurd strings of interceptions.

But whatever... none of this really matters, because you can't build more than one SDI anyway. So it doesn't really make much difference if there is a problem with how it is implemented.

No...the 2nd quote isn't quite true. More than 1 SDI can certainly trigger if the nuke launcher is at war with multiple civs with the SDI and the target tile would involve them. This isn't relevant to my issue, since every interception was triggered by the same civ, but it's noteworthy that maybe one should think twice about nuking such a tile unless the #'s you get to kill are huge.
 
Nuke interceptions here are supposedly a .375 probability. .375^7 is indeed a very small number. Encountering that and a .375^5 outcome in the same set of SIXTY FIVE (not 100) trials is a hell of a lot different from a set of coin flips which should on average appear. It's possible but statistically improbable, unless the assumption of .375 is wrong of course.

I think I've discussed this with you before but there is a small problem with the sort of analysis you apply. On any already performed experiment of binomial trials, it's always possible to find a bunch of particular occurences that should have been statistically improbable had you not had the benefit of hindsight.

For example, suppose I toss a coin ten times...

HTTHTTTHHH.

I could pick out a number of patterns from that...
1) I got TTTHHH or HHHTTT.
2)I got tails in a row twice.
3)I got exactly 5 heads and 5 tails (only 10C5 * (1/2)^10 chance of that happening).
4) I got a H as first and last throw.
5) I never got a single T (a tail that wasn't in a run of 2 or more).

... I could go on. We have a particular bias for some reason to note the occurence of streaks in particular, probably because it is revealed most obviously when we are losing several units all in one assault.

My point is it's always a little bit of a fallacy to talk about the odds of particular occurences after they've already happened. I admit though, that all of us are guilty of it at some point or other, except maybe statisticians. ;) Really you should specify before your experiment what types of occurences you are going to look for so that a negative result (an uninteresting result) wouldn't just get swept under the rug.

This reminds me of the debate about why most homeopothy trials are dodgy (only positive results of trials i.e. ones that say the drug works get published - this is very dodgy scentifically speaking).
 
I haven't looked at the code, but what you are describing sounds like there is a bug somewhere...

Programming in individual trials for each nuke interception in itself would be a bug. You would have to put in a for loop for each SDI present, and you would only do that if you wanted to allow multiple interceptions. What should happen is something like
if SDI then interception=getnukeinterception*(100-evasion)/100
else interception=0

And of course you can't build multiple SDI's? Probably, but you haven't proved that the AI can't. If you assumed everything worked perfectly, there would be no bugs. I've been going through all this to rule out every possible alternative.

It's a little ignorant to counter what I'm saying with this. A string of 5 heads or tails has a 3% chance (each), and if you throw 100 flips you'd fully expect to encounter it on average. 7 consecutive heads or tails is a little under 1% each, but on average you should see one or the other in 100 throws.

Nuke interceptions here are supposedly a .375 probability. .375^7 is indeed a very small number. Encountering that and a .375^5 outcome in the same set of SIXTY FIVE (not 100) trials is a hell of a lot different from a set of coin flips which should on average appear. It's possible but statistically improbable, unless the assumption of .375 is wrong of course.




No...the 2nd quote isn't quite true. More than 1 SDI can certainly trigger if the nuke launcher is at war with multiple civs with the SDI and the target tile would involve them. This isn't relevant to my issue, since every interception was triggered by the same civ, but it's noteworthy that maybe one should think twice about nuking such a tile unless the #'s you get to kill are huge.

Actually, that shouldn't happen. It should pick the team affected with the highest nuke interception chance and roll the dice against it.
Code:
for (iI = 0; iI < MAX_TEAMS; iI++)
{
	if (abTeamsAffected[iI])
	{
		if (GET_TEAM((TeamTypes)iI).getNukeInterception() > iBestInterception)
		{
			iBestInterception = GET_TEAM((TeamTypes)iI).getNukeInterception();
			eBestTeam = ((TeamTypes)iI);
		}
	}
}

In a trial of 65 nukes, a 75% chance of interception should be very distinguishable from a 37.5% chance. 75% would be 48. Of course, if you got your last necessary nuke early, you would stop nuking instead of launching 70 nukes, so it would be a little inaccurate, but not that different.
 
In a trial of 65 nukes, a 75% chance of interception should be very distinguishable from a 37.5% chance. 75% would be 48. Of course, if you got your last necessary nuke early, you would stop nuking instead of launching 70 nukes, so it would be a little inaccurate, but not that different.

You witnessed that trial first hand. Since I'd planned out 5 nukes/city (@ 13 cities), and essentially needed to double nuke some, triple nuke the rest, I ran into problems. In that war I had to leave 2 cities completely untouched and more w/o being able to take them (short on nukes). <30 struck though it was probably very high in the 20's. This is unusual but not impossible. Don't forget that the following 3-5 turns I had rebuilt reinforcement nukes and was able to use that little island base to reload them and finish the job...sort of (bull @#$% voluntary vassalage made that game take longer than it should have taken).

At any rate it wasn't anywhere near 75% intercepted, it was probably ~60% that were IIRC. I stopped tracking it in the future wars but it didn't seem as bad there (esp against ragnar). However if the code isn't turning up anything unusual I have no choice but to accept that it was a run of bad...more like terrible luck.
 
more than 35 out of 65 intercepted is more than bad luck. A mean of 41 should hit (24 intercepted), s.d. is 3.9, that's over 3 standard deviations off, which is at best 0.1%. Multiple SDI would be cute to discover; all you have to do is save the worldbuilder file and check for multiple SDI projects with one civ, or you could launch 100 icbms and if none of them hit, there are multiple SDIs.
 
Programming in individual trials for each nuke interception in itself would be a bug. You would have to put in a for loop for each SDI present, and you would only do that if you wanted to allow multiple interceptions. What should happen is something like
if SDI then interception=getnukeinterception*(100-evasion)/100
else interception=0
I really don't see how you can think having individual interception trials for each SDI would be a bug. To me that would make perfect sense. Sure, it could be implemented with a 'for loop' as you suggest, but obviously (?) with a break clause if the nuke was actually intercepted. Of course, there are other ways to replicate the probabilities associated with individual trials without actually performing the trials (ie. just work out the final total probability of interception and then do a single random trial with that probability).
 
more than 35 out of 65 intercepted is more than bad luck. A mean of 41 should hit (24 intercepted), s.d. is 3.9, that's over 3 standard deviations off, which is at best 0.1%. Multiple SDI would be cute to discover; all you have to do is save the worldbuilder file and check for multiple SDI projects with one civ, or you could launch 100 icbms and if none of them hit, there are multiple SDIs.

By saying it's more than bad luck are you implying that something has to be going wrong? As I've asserted above (or at least implied), a single game where TMIT had a pretty bad run of luck is not really enough substance to go off. Of all people, TMIT is one of the ones who plays the highest number of separate games, so is he asserting that his "bad luck" is a common pattern in his games? Runs of up to 7 tac-nuke interceptions will be seen every now and then - a perfect or imperfect RNG would both produce that sort of thing.

It's more like 0.3% at best (not 0.1%) by the way (assuming 29 of 65 got through), if you use the proper distribution instead of the normal approximation to it.
 
By saying it's more than bad luck are you implying that something has to be going wrong? As I've asserted above (or at least implied), a single game where TMIT had a pretty bad run of luck is not really enough substance to go off. Of all people, TMIT is one of the ones who plays the highest number of separate games, so is he asserting that his "bad luck" is a common pattern in his games? Runs of up to 7 tac-nuke interceptions will be seen every now and then - a perfect or imperfect RNG would both produce that sort of thing.

It's more like 0.3% at best (not 0.1%) by the way (assuming 29 of 65 got through), if you use the proper distribution instead of the normal approximation to it.

Calculate again
s := .1939665599e-2
for i from 36 to 65 do
s:=s+binomial(65,i)*0.37^i*0.63^(65-i)
> end do;
Maybe you're looking at two sides.
 
Calculate again
s := .1939665599e-2
for i from 36 to 65 do
s:=s+binomial(65,i)*0.37^i*0.63^(65-i)
> end do;
Maybe you're looking at two sides.

Wops I used 0.62 and 0.38 again. Anyway, re-checking it I got 0.0019 as you did. That means it's 0.19% at best - not 0.1%.
 
Back
Top Bottom