On tech diffusion

Simple mistake that is throwing everything off, and it's my fault:

TECH_DIFFUSION_KNOWN_TEAM_MODIFIER * (0.85 ^ exponent + 0.5)

should be

(TECH_DIFFUSION_KNOWN_TEAM_MODIFIER * (0.85 ^ exponent) + 0.5)

The exponent has 0.5 added to it to effectively "round up". Due to order of operations, the 0.5 is added at the very last step for that set of parenthesis.

Also, if the modifier is negative it is not used, tech diffusion can not hurt your research rate.

Guys, I'm missing something: the actual code is

Code:
		int techDiffMod = GC.getTECH_DIFFUSION_KNOWN_TEAM_MODIFIER();
		if (knownExp > 0.0)
		{
			//ensure tech diffusion can not hurt research, only help
			iModifier += std::max(0, techDiffMod - (int)(techDiffMod * pow(0.85, knownExp) + 0.5));
		}

		// Tech flows downhill to those who are far behind
		int iTechScorePercent = GET_TEAM(getTeam()).getBestKnownTechScorePercent();
		int iWelfareThreshold = GC.getTECH_DIFFUSION_WELFARE_THRESHOLD();
		if( iTechScorePercent < iWelfareThreshold )
		{
			if( knownExp > 0.0 )
			{
				int iWelfareMod = GC.getTECH_DIFFUSION_WELFARE_MODIFIER();
				//ensure tech diffusion can not hurt research, only help
				iModifier += std::max(0, (iWelfareMod - (int)(iWelfareMod * pow(0.98, iWelfareThreshold - iTechScorePercent) + 0.5)));

so it looks to me your code is right Afforess. Or am I lost somewhere in the discussion?
 
Well, the formula parts seem to be tuned with Afforess correction. But aside from that the code seems a mess when my brain tries to unlock its secrets (I have newb knowledge on C, and almost 0 knowledge on any C++ features)

EDIT: My attempts at discerning it:

If no civ knows the tech, forget about all of this ( knownExp > 0.0 )

It can never hurt you ( std::max(0, formula) )

If you are at least 90%+ the tech points of the most developed civ, forget the second addition to the modifier ( if( iTechScorePercent < iWelfareThreshold ) )

Is anything written above right/wrong?
 
So correcting my math:

Best Scenario
1st iModifier = 70 - (70 * (0.85 ^ 24.5) + 0.5) = 70 - (70 * 0.02 + 0.5) = 70 - 1.8 = 68.2 = 68

For the second I guess the same correction is done right?

So instead of (TECH_DIFFUSION_WELFARE_MODIFIER * (0.98 ^ (TECH_DIFFUSION_WELFARE_THRESHOLD - iTechScorePercent) + 0.5))
It should be (TECH_DIFFUSION_WELFARE_MODIFIER * (0.98 ^ (TECH_DIFFUSION_WELFARE_THRESHOLD - iTechScorePercent)) + 0.5)

2nd iModifier = 68 + 1000 - (1000 * (0.98 ^ (90 - 0)) + 0.5) = 1068 - (1000 * 0.16 + 0.5) = 905.19 = 905

So dividing by 100 (9.05), multiplying by tech rate and adding to it, we achieve 10.05x our tech rate.

And in Worst scenario

1st iModifier = 70 - (70 * (0.85 ^ 0) + 0.5) = 70 - (70 * 1 + 0.5) = -0.5 = 0

2nd iModifier = 0 + 1000 - (1000 * (0.98 ^ (90 - 100)) + 0.5) = 1000 - (1000 * 1.22 + 0.5) = -224.38 = -224

As it's negative it's turned into 0, and doesn't do anything to your tech rate.

Now is it right?

Looks right.

45°38'N-13°47'E;13296559 said:
so it looks to me your code is right Afforess. Or am I lost somewhere in the discussion?

My code is fine. My explanation a bit earlier was a tiny bit wrong. ;)
 
45°38'N-13°47'E;13296619 said:
OK got it. Have you seen my proposal some posts above? What's your opinion?

I *think* it is ok, but I wouldn't stake my life on it, I only skimmed. I'd add a bit of logging to test and find out what iRatio turns out to be at the end.

You can use GC.getGameINLINE().logMsg("...") to log to A_New_Dawn.log in the logs folder.
 
Hey 45°, if it's not too much to ask, could you give a detailed explanation of your addition?

I understood you've created another mechanic to add to the one already in use, as you didn't modify existing code and yours is applied like this: iModifier *= iRatio

But I couldn't understand how you get the iRatio.

It seems iBestScore is just the score of the civ with the best score in game, but I couldn't understand the code to discover this:

The max is just to be sure somehow BestScore isn't < (1+GET_PLAYER((PlayerTypes)iI).calculateScore(false, false)) (this underlined part I can't comprehend what it does with my limited C++ knowledge).

But iRatio is recalculated for every iI, I couldn't understand that as well.

GET_PLAYER((PlayerTypes)iI).getTeam() seems to get the team of the Player #iI, but what getTeam() alone means?
[ in: if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam() ]


Sorry to flood here with so many questions, I'll understand if you don't want to waste time explaining the code here, but instead of studying to pass my exams I try to learn digging CIV code.

EDIT: And why my underlined part separates the 'fals' from the 'e' is also a big mystery
 
Honestly I'm not sure of the code myself, but what it should do is looping through every civ and comparing scores to get the best one. When the team we've calculated the score of is the one we want to apply tech diffusion, iratio is calculated. That 1+ part is only meant to avoid a division by zero. In practice tech diffusion is multiplied by ratio between best score and that civ's score.
 
I see, so what's left to understand in this code that I couldn't is the if statement of getTeam.



You create and initialize both integers iRatio and iBestScore.

Then you do a loop for all players in the game.

In this loop you check if the picked player's team is equal to getTeam() (which I can't understand what it is). If this check fails, go to the next iteration of the loop.

If it doesn't fail, you enter a secondary loop. So for each instance that passes the if statement you do another loop through all players.

In each iteration of this secondary loop, you get the score of the player #(iteration value) and put it in iScore, an integer that you created inside this loop.

Then you compare if this score is >= iBestScore. If it is, then you put its value on iBestScore, and continue the loop until all players have been checked.

So you come back to the end of the if statement and make iRatio = max(1, Ratio between Max Score and the iterated player's score). And after this you go back to the primary loop and start all over again.


Not knowing what the if statement inside the primary loop does, it becomes hard to me to get to any conclusion, but taking a look at CvPlayer.cpp on afforess SVN that he posted on the religion decay thread, I think I may have figured out. getTeam() alone picks the team of the player that is in consideration for the whole thing (where are the boundaries of this 'whole thing' I'm yet to discover). What made me think of this was these lines:
Code:
if( GET_TEAM(getTeam()).isOpenBorders((TeamTypes)iI) || GET_TEAM((TeamTypes)iI).isVassal(getTeam()) )
{
knownExp += 1.5;
}
else if( GET_TEAM(getTeam()).isAtWar((TeamTypes)iI) || GET_TEAM(getTeam()).isVassal((TeamTypes)iI) )
{
knownExp += 0.5;
}
Which adds 1.5 to knownExp if our team has open borders with team #iI or if Team #iI is vassal to us (of course when I refer to us, I mean the picked player for the whole process, I presume this is done for all players in player order)
Or adds 0.5 to knownExp if our team is at war with the team #iI, or if we are a vassal of team #iI


So actually going back to the first loop with this understanding, you're just checking if Player #iI is the current player being analyzed, and then in the secondary loop you check who has the best score of the game, and in the end you make iRatio = max(1, iBestScore/ ~Player #iI Score) [ I put ~ because the addition of 1, as you said, it's only to prevent a division by 0, and in most cases it'll not make much difference, as scores start in the dozens, and when this tech diffusion feature may actually be considered, a +1 won't make much difference in the hundreds or thousands of score points ]


Sorry if I'm making serious mistakes here, but I'm truly trying to understand what you've written with my limited knowledge.
 
Here, I'll try and explain it.

iRatio and iBestScore are initialized to default values.

Then there is a loop through all the players,

If the player is on our team (vassal or permanent alliance)

Then loop through all the players & barbarian player (MAX_CIV_PLAYERS = players + barbarian, MAX_PLAYERS is just players)

We calculate the iJ players score

If the iJ player's score is better than the best score, save it as the best score

Now we finish the iJ loop.

iRatio is set as the value of iBestScore divided by iI player's score

Now we finish the iI loop.

iModifier is multiplied by iRatio...


Alright, I see a couple of problems. For one, iRatio should be a float, not an integer. You have a strong likelihood of iRatio being zero due to rounding. That is bad.

Secondly, MAX_CIV_PLAYERS should be replaced with MAX_PLAYERS, I see no reason to consider the barbarian player score, it might be totally out of whack due to tons of barbarian cities.

Also, the score calculation should check to make sure players are actually alive.

I believe the reason there are two loops is to include the score of our entire team, but it doesn't do that correctly.

Also multiplying iModifier by iRatio isn't the desired effect.

Here is my suggested modified version:

Code:
	if( iTechScorePercent < iWelfareThreshold )
		{
			if( knownExp > 0.0 )
			{
				int iWelfareMod = GC.getTECH_DIFFUSION_WELFARE_MODIFIER();
				//ensure tech diffusion can not hurt research, only help
				int iWelfareTechDiffusion = std::max(0, (iWelfareMod - (int)(iWelfareMod * pow(0.98, iWelfareThreshold - iTechScorePercent) + 0.5)));
				int iBestScore = 0;	
				int iOurScore= calculateScore(false);
				for (int iI = 0; iI < MAX_PLAYERS; iI++)
				{
					if (GET_PLAYER((PlayerTypes)iI).isAlive() && iI != getID())
					{
						int iScore = GET_PLAYER((PlayerTypes)iI).calculateScore(false);
						if (iScore > iBestScore)
						{
							iBestScore = iScore;
						}
					}		
				}
				//If iOurTeamScore > iBestScore we are the best team in the game
				if (iOurTeamScore < iBestScore)
				{
					float fRatio = iBestScore / ((float)iOurTeamScore);
					iWelfareTechDiffusion *= fRatio;
					iModifier += iWelfareTechDiffusion;
				}
			}
		}


What my version does is find the score for our player, then find the best score of all players that are not us, and alive. If our score is less than the best score, we take the ratio of the best score / our score (which has to give us a number greater than 1) and multiply the welfare tech diffusion by it, then add it to the total tech diffusion.

If our score is greater than the best score (meaning we are the #1 player) we don't get any welfare tech diffusion.
 
Here, I'll try and explain it.

iRatio and iBestScore are initialized to default values.

Then there is a loop through all the players,

If the player is on our team (vassal or permanent alliance)

Then loop through all the players & barbarian player (MAX_CIV_PLAYERS = players + barbarian, MAX_PLAYERS is just players)

We calculate the iJ players score

If the iJ player's score is better than the best score, save it as the best score

Now we finish the iJ loop.

iRatio is set as the value of iBestScore divided by iI player's score

Now we finish the iI loop.

iModifier is multiplied by iRatio...


Alright, I see a couple of problems. For one, iRatio should be a float, not an integer. You have a strong likelihood of iRatio being zero due to rounding. That is bad.

Secondly, MAX_CIV_PLAYERS should be replaced with MAX_PLAYERS, I see no reason to consider the barbarian player score, it might be totally out of whack due to tons of barbarian cities.

Also, the score calculation should check to make sure players are actually alive.

I believe the reason there are two loops is to include the score of our entire team, but it doesn't do that correctly.

Also multiplying iModifier by iRatio isn't the desired effect.

Here is my suggested modified version:

Code:
	if( iTechScorePercent < iWelfareThreshold )
		{
			if( knownExp > 0.0 )
			{
				int iWelfareMod = GC.getTECH_DIFFUSION_WELFARE_MODIFIER();
				//ensure tech diffusion can not hurt research, only help
				int iWelfareTechDiffusion = std::max(0, (iWelfareMod - (int)(iWelfareMod * pow(0.98, iWelfareThreshold - iTechScorePercent) + 0.5)));
				int iBestScore = 0;	
				int iOurScore= calculateScore(false);
				for (int iI = 0; iI < MAX_PLAYERS; iI++)
				{
					if (GET_PLAYER((PlayerTypes)iI).isAlive() && iI != getID())
					{
						int iScore = GET_PLAYER((PlayerTypes)iI).calculateScore(false);
						if (iScore > iBestScore)
						{
							iBestScore = iScore;
						}
					}		
				}
				//If iOurTeamScore > iBestScore we are the best team in the game
				if (iOurTeamScore < iBestScore)
				{
					float fRatio = iBestScore / ((float)iOurTeamScore);
					iWelfareTechDiffusion *= fRatio;
					iModifier += iWelfareTechDiffusion;
				}
			}
		}


What my version does is find the score for our player, then find the best score of all players that are not us, and alive. If our score is less than the best score, we take the ratio of the best score / our score (which has to give us a number greater than 1) and multiply the welfare tech diffusion by it, then add it to the total tech diffusion.

If our score is greater than the best score (meaning we are the #1 player) we don't get any welfare tech diffusion.


Well, I guess you've seen that this can't be compiled. Beside
int iOurScore= calculateScore(false);
I suppose it should be
nt iOurTeamScore= calculateScore(false);

you can't simply call calculateScore(false) like that.
Should it be something like
GET_PLAYER((PlayerTypes)getID()).calculateScore(false);?

Edit: And I don't get it why you're making the modifier additive instead of multiplicative. Let's say that poor civ needs 100 turns to discover a tech already discovered by someone else; with a "normal" tech diffusion (before our changes) it takes 50 turns for them to discover that tech. With a multiplicative modifier (let's say their score is 1/4 of the best score), they need only 12-13 turns to discover that tech. With an additive modifier, what should the result be? I guess anyway significantly higher than 12 turns. Hence that poor civ will never catch up with others and will lose more terrain. Or am I missing something?
 
45°38'N-13°47'E;13298157 said:
Well, I guess you've seen that this can't be compiled.

I was at work, can't actually test there.
45°38'N-13°47'E;13298157 said:
Beside
int iOurScore= calculateScore(false);
I suppose it should be
nt iOurTeamScore= calculateScore(false);

you can't simply call calculateScore(false) like that.
Should it be something like
GET_PLAYER((PlayerTypes)getID()).calculateScore(false);?

Sure you can. Inside of CvPlayer you can use any player functions - it is on ourselves.

45°38'N-13°47'E;13298157 said:
Edit: And I don't get it why you're making the modifier additive instead of multiplicative. Let's say that poor civ needs 100 turns to discover a tech already discovered by someone else; with a "normal" tech diffusion (before our changes) it takes 50 turns for them to discover that tech. With a multiplicative modifier (let's say their score is 1/4 of the best score), they need only 12-13 turns to discover that tech. With an additive modifier, what should the result be? I guess anyway significantly higher than 12 turns. Hence that poor civ will never catch up with others and will lose more terrain. Or am I missing something?

It was already additive, I just moved it to a variable and then added it at the end. The ratio multiplies the welfare amount, not the total.

If you are confused:

iModifier is the total research modifier. Multiplying it is extreme, it is already a large number. Each part of tech diffusion adds to the modifier. I made the ratio multiply the tech diffusion amount, then add it to the modifier.
 
Ok here is the final code:

Code:
				int iWelfareMod = GC.getTECH_DIFFUSION_WELFARE_MODIFIER();
				//ensure tech diffusion can not hurt research, only help
				int iWelfareTechDiffusion = std::max(0, (iWelfareMod - (int)(iWelfareMod * pow(0.98, iWelfareThreshold - iTechScorePercent) + 0.5)));
				int iBestScore = 0;
				int iOurScore = calculateScore(false);
				for (int iI = 0; iI < MAX_PLAYERS; iI++)
				{
					if (GET_PLAYER((PlayerTypes)iI).isAlive() && iI != getID())
					{
						int iScore = GET_PLAYER((PlayerTypes)iI).calculateScore(false);
						if (iScore > iBestScore)
						{
							iBestScore = iScore;
						}
					}
				}
				//If iOurScore > iBestScore we are the best team in the game
				if (iOurScore < iBestScore)
				{
					float fRatio = iBestScore / ((float)iOurScore);
					GC.getGameINLINE().logMsg("Tech Welfare amount: %d, iOurScore: %d, iBestScore: %d, fRatio: %f, modified welfare amt: %d for civ: %S", iWelfareTechDiffusion, iOurScore, iBestScore, fRatio, ((int)(fRatio * iWelfareTechDiffusion)), getCivilizationDescription());
					iWelfareTechDiffusion = (int)(iWelfareTechDiffusion * fRatio);
					iModifier += iWelfareTechDiffusion;

				}

It is in the latest revision. Turn on XML logging, go play a game. Check A_New_Dawn log and see what the actual values are.
 
wow - great work... is this included in the installer yet?
 
wow - great work... is this included in the installer yet?

There is a launcher included in the mod folder now. You can use to update the game or configure some options !
 
So talking about Tech Diffusion on New/Old World maps.

So as I was seeing on my game, you make a Low Speed game in a huge map with Old/New World option on and suddenly when the lucky guy who got Caravels first finds some organized natives, then suddenly, when you're finally making the tech that allows you to put the first ocean-crossing ship to conquer new lands, New World nations already have techs that you don't. (If you want a more detailed version of this info, check it here on my story: http://forums.civfanatics.com/showpost.php?p=13294425&postcount=84. It's the second spoiler that will show tech relations between players and me, and in the end I mention New World nations)


But Tech Diffusion is an awesome mechanic that makes the game more realistic, and IMO what's more realistic is to be included in my games.

So I have a proposal which I guess isn't so hard.

If there is an option for New/Old World maps, somewhere there must be a way to distinguish Old and New World civs. We could include a simple check to see if a nation is a new worlder. If it is it can't receive tech diffusion. And another check to invalidate this, a simple: Has someone researched Navigation (enables Ocean Trade Routes) yet? If yes, forget about old/new world on tech diffusion and make it normal again.

Someone may say this shouldn't be so restrictive, and new world civs should have tech diffusion between themselves, and this should be actually to all landmasses without trade routes with each other, so each solitary landmass has tech diffusion on itself, but not with other nation from other landmasses which don't have navigation yet. I think this is too complicated to a rather simple problem: Only New World landmasses should have this restriction because even different old world landmasses may have trade in knowledge before trade routes are established, because they will know each other more or less in the same tech level, and caravels may navigate well before trade routes may be established. The only true problem we have here is New World nations having almost the same tech level as the Old World, before New World Colonization may effectively begin.

And it won't be that harsh on New World nations the lack of tech diffusion until someone has navigation (I guess). When we meet them they normally are still minors (if minor nations are allowed, if not I think it won't be much different). And Players can only be Old Worlders (except in MP games where you have Take Over AI, or through cheating in Single Player, but these are exceptions)


Do you guys think this is too complicated? It would be a great addition to Old/New World game option.
 
So I have a proposal which I guess isn't so hard.

If there is an option for New/Old World maps, somewhere there must be a way to distinguish Old and New World civs. We could include a simple check to see if a nation is a new worlder. If it is it can't receive tech diffusion. And another check to invalidate this, a simple: Has someone researched Navigation (enables Ocean Trade Routes) yet? If yes, forget about old/new world on tech diffusion and make it normal again.

Someone may say this shouldn't be so restrictive, and new world civs should have tech diffusion between themselves, and this should be actually to all landmasses without trade routes with each other, so each solitary landmass has tech diffusion on itself, but not with other nation from other landmasses which don't have navigation yet. I think this is too complicated to a rather simple problem: Only New World landmasses should have this restriction because even different old world landmasses may have trade in knowledge before trade routes are established, because they will know each other more or less in the same tech level, and caravels may navigate well before trade routes may be established. The only true problem we have here is New World nations having almost the same tech level as the Old World, before New World Colonization may effectively begin.

And it won't be that harsh on New World nations the lack of tech diffusion until someone has navigation (I guess). When we meet them they normally are still minors (if minor nations are allowed, if not I think it won't be much different). And Players can only be Old Worlders (except in MP games where you have Take Over AI, or through cheating in Single Player, but these are exceptions)


Do you guys think this is too complicated? It would be a great addition to Old/New World game option.

Wrong, very hard. There is no concept of old vs new world, of which is which. Hardcoding techs into the DLL is also bad.
 
Wrong, very hard. There is no concept of old vs new world, of which is which. Hardcoding techs into the DLL is also bad.

Damn! The old/new world issue is really a problem, but the tech on DLL could be used like the values you use in Global Defines, something like: TECH_OCEAN_ROUTE that is defined in Global Defines. Would this solve the 'hardcoding tech' issue?

Anyways, maybe the Caravel could be postponed a bit? This would prevent a so early contact between Old and New world, solving the problem without changing a bit in the tech diffusion mechanic (because the only need here is to postpone the Tech Diffusion rush to New World). After we may trade with them, New World civs should indeed get the techs fast (i.e. North American Natives), but not before we set foot there (like Maya, Inca and Aztec).

I'll ask on the units thread again, so to make things go on their right place. It's a shame it's hard to do this, I had no idea. I thought like if there is an option to make Civs spawn later in the New World, maybe we may play more with this concept of New World Civs, as if it exists for mapscript options, it may exist to everything.
 
Top Bottom