Can someone explain exactly how bulbing mechanics work ?

At most yes but sometimes you'll get N-1 with the last one stuck at 1 turn even though you have enough from the bulb to cover the whole cost. I must not be the only one to have seen that happen.

can you demonstrate it with IGE or something? I'm not sure if you get any techs at all if you bulb while not researching anything, but you say that when you set the research path, you don't always get 1 tech per bulb?
 
Probably only way to know for sure would be looking at (and understanding) the code.
I can look at it, but i fear i don't speak C++ fluently :mischief:
 
I've been wondering about this too, because you used to be able to bulb all of your scientists at once and rip off several techs then get the last few 1/turn. I think the great scientist bulb has to cover the entire research cost of the tech, by itself, to get the second tech. So, bulbing a tech that takes 9 turns, even with 3 turns of overflow in, won't learn the tech if you've already bulbed 1 tech that turn. Bulbing a second one would, but after the patch you lose overflow so it's not worth it. I'm guessing this is because the overflow isn't applied to that tech until the next turn in some way.
 
In my current Attila wonderwhoring game I got double DOWed by Germany and Portugal so I thought I'd better bulb Radar sharpish. After Flight was completed I bulbed Electronics which I got, then I bulbed Ballistics which I got, leaving Radar with a few turns to go. 3 techs in the same turn! This isn't a stellar science game as I'm generating about 700bpt
 
Right, so the more accurate thing for me to have said was One tech per turn per bulb. We've figured that out, great.
The last question is what counts as overflow when overflow is capped. I have found a function in the source code called ChangeResearchProgress(<TechnologyEnum>, int, <PlayerEnum>) . It aliases, in the obvious way, the function ChangeResearchProgressTimes100(<TechTypes>, int, <PlayerTypes>), which calls SetResearchProgressTimes100(theTech, GetResearchProgressTimes100(theTech) + theChange, thePlayer) .

So SetResearchProgressTimes100 looks like this
Spoiler :
Code:
void CvTeamTechs::SetResearchProgressTimes100(TechTypes eIndex, int iNewValue, PlayerTypes ePlayer)
{
	CvAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	CvAssertMsg(eIndex < GC.getNumTechInfos(), "eIndex is expected to be within maximum bounds (invalid Index)");
	CvAssertMsg(ePlayer >= 0, "eIndex is expected to be non-negative (invalid Index)");
	CvAssertMsg(ePlayer < MAX_PLAYERS, "ePlayer is expected to be within maximum bounds (invalid Index)");

	if(GetResearchProgressTimes100(eIndex) != iNewValue)
	{
		m_paiResearchProgress[eIndex] = iNewValue;
		CvAssert(GetResearchProgressTimes100(eIndex) >= 0);

		if(m_pTeam->GetID() == GC.getGame().getActiveTeam())
		{
			GC.GetEngineUserInterface()->setDirty(GameData_DIRTY_BIT, true);
			GC.GetEngineUserInterface()->setDirty(Score_DIRTY_BIT, true);
		}

		int iResearchProgress = GetResearchProgressTimes100(eIndex);
		int iResearchCost = GetResearchCost(eIndex) * 100;

		// Player modifiers to cost
		int iResearchMod = std::max(1, GET_PLAYER(ePlayer).calculateResearchModifier(eIndex));
		iResearchCost = (iResearchCost * 100) / iResearchMod;
		int iNumCitiesMod = GC.getMap().getWorldInfo().GetNumCitiesTechCostMod();	// Default is 40, gets smaller on larger maps
		iNumCitiesMod = iNumCitiesMod * GET_PLAYER(ePlayer).GetMaxEffectiveCities(/*bIncludePuppets*/ true);
		iResearchCost = iResearchCost * (100 + iNumCitiesMod) / 100;
		
		int iOverflow = iResearchProgress - iResearchCost;

		// April 2014 Balance Patch change - EFB
		//    Don't allow the overflow to get out of hand
		int iMaxOverflow = GetMaxResearchOverflow(eIndex, ePlayer);
		if (iOverflow > iMaxOverflow)
		{
			iOverflow = iMaxOverflow;
		}

		if(iOverflow >= 0)
		{
			GET_PLAYER(ePlayer).changeOverflowResearchTimes100(iOverflow);
			m_pTeam->setHasTech(eIndex, true, ePlayer, true, true);
			SetNoTradeTech(eIndex, true);

			// Mark city specialization dirty
			GET_PLAYER(ePlayer).GetCitySpecializationAI()->SetSpecializationsDirty(SPECIALIZATION_UPDATE_RESEARCH_COMPLETE);

			// Culture bonus for Player researching a Tech
			PlayerTypes eLoopPlayer;
			int iCulture;
			TeamTypes eTeamID = m_pTeam->GetID();
			for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++)
			{
				eLoopPlayer = (PlayerTypes) iPlayerLoop;
				CvPlayerAI& kLoopPlayer = GET_PLAYER(eLoopPlayer);

				if(kLoopPlayer.getTeam() == eTeamID)
				{
					iCulture = kLoopPlayer.GetCulturePerTechResearched();
					if(iCulture != 0)
					{
						kLoopPlayer.changeJONSCulture(iCulture);
					}
				}
			}
		}
	}
}

Before proceeding its important to know that technologies are processed by team, but in a non-team game every player is its own team. Players assign their science independently, but the beakers are accumulated to an instance of the tech tree that exists for their team.

The previously quoted function is a member of the TeamTechs class, which is instantiated for each team in the game. The function can be summarized like this:
Code:
	Given a certain technology (thatTech), a rounded amount of science (PlusBeakers), and a player (thatPlayer),
	(and implicitly, a handle for this team,)

		int iResearchProgress --> thisTeam.GetResearchProgressTimes100(thatTech);
		int iResearchCost --> GetResearchCost(thatTech) * 100;

		The actual variables for science are offset to make the decimal point go away, so they measure "hundredths of science".

		// Player modifiers to cost
		Let the int type iResearchMod be determined as the calculateResearchModifier() of thatPlayer for thatTech, at minimum the smallest positive integer.

		iResearchCost is modified by iResearchMod.

		The reference of getWorldInfo.GetNumCitiesTechCostMod can't be determined easily, as that source code is supplied by text substitution at compilation so I can't Ctrl-F it.
		However the math is consistent with this applying a certain percent modifier for each city to the research cost, which would be 5%, which means the "40" comment is  WAYYYY wrong.

		iResearchCost is modified for the cities they control, as counted by GetMaxEffectiveCities(<Including Puppets>)

		NOW iOverflow IS DETERMINED AS PROGRESS - COST
		int iOverflow = iResearchProgress - iResearchCost;

		AND HERE IS PROBABLY OUR ANSWER:
		// April 2014 Balance Patch change - EFB
		//    Don't allow the overflow to get out of hand
		int iMaxOverflow = GetMaxResearchOverflow(eIndex, ePlayer);
		if (iOverflow > iMaxOverflow)
		{
			iOverflow = iMaxOverflow;
		}

		If overflow is positive, then the very next line executed is to set the overflow accumulator for this team to iOverflow.
		Therefore, the overflow is any amount of science past the cost of this very technology, regardless of queueing things.

		if(iOverflow >= 0)
		{
			add to the overflow accumulator
			GET_PLAYER(ePlayer).changeOverflowResearchTimes100(iOverflow);
			give the team the tech	
			m_pTeam->setHasTech(eIndex, true, ePlayer, true, true);
			And it looks like the game supported tech trading at the codebase level, they just didn't actually go for it.
			SetNoTradeTech(eIndex, true);

			// Mark city specialization dirty
			(Dirty bits are implementation details)

			// Culture bonus for Player researching a Tech
			(Exactly what it says on the tin, although I don't think there actually is a trait or policy or building that gives culture for finishing a tech)
		}

So I look for getMaxResearchOverflow , expecting to find a function that caps at the cost of the tech passed in and some measure of the player's last 5 turns of science (audited how?) and also look up changeOverflowResearchTimes100()

The former indeed gets Overflow and is properly offset to hundredths of science. And it actually shortcuts to 5 times your -current- beaker delta per turn. The latter performs no logic and just updates an internal representation.
Well, now we've got to explain what reduces the overflow accumulator. I'd guess it's either awarding the tech or measuring researchProgress.

Awarding the tech triggers updates for discovering tech, but calls back to SetHasTech (capital S) inside the TeamTechs object, which does nothing of note. Measuring research progress reveals something neat, that only players have overflow, but they still share accumulated research with the team. I've run out of time to check the logic on this.

If there actually is no time when overflow is zeroed except in the doResearch() call, then that explains why you can't bulb. You have to wait until your next turn is processed to zero your overflow, although if you repeatedly bulb to very nearly the exact remaining cost to go, the overflow that gets stored up will be small.

This is heavyhanded as obviously the fix would be to remove the math error that is applying multipliers to an overflow amount polynomially.
 
Here's an example, based on my experience and reinforced by the code that was dug up for the occasion :

At the start of the turn you have 5k of research applied to an 11k tech. You bulb for 10k, leaving you with the tech and 4k of overflow (that isn't applied until the next turn). Now you cue up a tech that costs 9k, and bulb. You get that tech because the bulb was 10k. You now have 5k of overflow, still unapplied. You finally cue up another tech that costs 11k, and bulb again. You don't get this tech. You now have 10k applied to that tech, and still have 5k of unapplied overflow. If you then bulbed a 4th GS, you would get that 11k tech because 10k has already actually been applied. You're then left with 15k overflow, which gets clipped by the patch.

To summarize, you can only bulb for additional techs if the full cost of that tech is covered by the bulb alone, because overflow isn't applied until the next turn.
 
Here's an example, based on my experience and reinforced by the code that was dug up for the occasion :

At the start of the turn you have 5k of research applied to an 11k tech. You bulb for 10k, leaving you with the tech and 4k of overflow (that isn't applied until the next turn). Now you cue up a tech that costs 9k, and bulb. You get that tech because the bulb was 10k. You now have 5k of overflow, still unapplied. You finally cue up another tech that costs 11k, and bulb again. You don't get this tech. You now have 10k applied to that tech, and still have 5k of unapplied overflow. If you then bulbed a 4th GS, you would get that 11k tech because 10k has already actually been applied. You're then left with 15k overflow, which gets clipped by the patch.

To summarize, you can only bulb for additional techs if the full cost of that tech is covered by the bulb alone, because overflow isn't applied until the next turn.

Ok now that starts to make sense based on my experience and was more or less my guess.

So in other words a bulb applies a certain amount of beakers to a tech but anything that is overflow from that turn gets into some sort of overflow pool which does not count until next turn right ? So that even if you see your tech at say 4K/10K, if these 4K are overflow from the previous bulb then it doesn't count yet as being applied to the tech. So if I bulb for 9K I don't get the tech because it is still at 9/10K until one turn as passed and the overflow is emptied.
 
Yes, that is exactly what I meant. I dont like the capping fix. I think the solution was to not inflate overflow at all for humans.
 
2 questions:
The patch notes says overflow is capped at the last tech researched or the last 5 turns of science whatever is greater, is this reflected in the code above?
If true what happens if you bulb multiple techs in the same turn what tech is used for the "last" tech? Is it really the last tech or someother?
 
Ok now that starts to make sense based on my experience and was more or less my guess.

So in other words a bulb applies a certain amount of beakers to a tech but anything that is overflow from that turn gets into some sort of overflow pool which does not count until next turn right ? So that even if you see your tech at say 4K/10K, if these 4K are overflow from the previous bulb then it doesn't count yet as being applied to the tech. So if I bulb for 9K I don't get the tech because it is still at 9/10K until one turn as passed and the overflow is emptied.

I did some testing as well with my Attilla GOTM and can confirm this. The amount of science added to the tech is the value of the bulb. The tech tree shows the value including the overflow, but the overflow is not added instantly, but only next turn.

I bulbed a scientist for a tech, got 5K overflow, bulbed 2nd one for Radar, which was bit less then the tech cost. Tech tree showed 12K, tech cost was 7K, but still 1 turn to research it. I bulbed architecture with another one and got it the same turn. Overflow increased, but still 1 turn to research radar. I then switched to other techs until I spent all my overflow on other techs. Every turn when I looked at radar it showe less and less science, until all overflow was spent.
 
Based on Ctrl-F in the source code, for all the aliases of "set overflow research" evaluated at 0, it seems there is indeed exactly one place and one time your overflow is zeroed - when your research occurs at start of turn.

Now, the issue to understanding these mechanics (well, one of the myriad issues) is that there are two distinct things that might casually be termed "overflow". In order to explain the process, I will use some new, unbound terminology.

Could you explain it in simple terms ? :/ Or with an example.

Sure. I made a mistake in interpreting earlier. I can't explain it with an example, though, as I don't understand how examples explain things.

So, I was really surprised to find that teams don't have their own overflow. Apparently if you're playing a team game, you should have the first player to go on your team nurture all the overflow, to get any tech you're researching together as soon as possible. Meanwhile the rest of ya should bulb science.

First I'll give the situation for a non-team game. Then if I have time I guess I can diagram the whole thing.

Terminology:
Multiplier: Hopefully obvious
Modifier: An expression of a percentage which adds up with other modifiers with which it is grouped, which expresses an amount increased to a base quantity as a fraction of that quantity. The modifiers you see in the game are grouped by default to wherever the game entity that houses the modifier lives - to a city or your empire.
So they're added within a group, and multiplied group-by-group.

There's a lot of malicious rounding that goes on in the research functions. Even though the game claims to track hundredths of a beaker, those get rounded off at almost every step. Still, all the units are nominatively hundredths of a beaker, so we'll pretend.

Every player has their own tech tree. A certain number describes your relationship to each tech on the tree - how much science you've put into that tech. I will call this "Accumulated beakers". You also have your own measure of overflow accumulated. This is a number that belongs to you, the player, and has no relationship to a tech.
The sum of these two numbers is your ResearchProgress. It is a quantity of (hundredths of) beakers.

Any time something gives you science, it ends up going through the "SetResearchProgressTimes100" method. But it does that first by going through this statement:
Code:
SetResearchProgressTimes100(eIndex, (GetResearchProgressTimes100(eIndex) + iChange), ePlayer);
This statement is what gets called when the function to increment or accumulate to your research is called. It invokes functions for the team research, which does not have overflow. It invokes the set function on the amount "This team's ResearchProgress + the amount you're adding".

To be sure this is the whole picture, I would have to know the context of -every single- call to this function, and whether any math takes place on the numbers put into it. The logic of handling research should exist in one and only one place, but in fact it does not - some of it exists in doResearch(), which is exactly why the glitch exists. I'll get to that.

The function SetResearchProgressTimes100() sets your team's accumulation of beakers for the current tech, and calculates the cost of that tech, which is the raw cost multiplied by two things, each of which is a sum of modifiers. The first factor is Civs who know the tech , and the Scholars in Residence resolution. The second factor is the city count penalty. Based on the XML files in Assets, up to 30% of the cost of a tech is discounted, as a proportion of the number of met civs with the tech out of all living major civs. 30% doesn't sound right, but it is calculated as a proportion. And 20% of the cost is discounted with Scholar's in Residence (which actually means it increases the rate of acquiring the tech by 25%, on its own). Let's suppose the actual discount from met civs is 1% and the Scholars resolution is passed.

So that is applied as 79% (=100 +(-20) +(-1)) multiplied to the cost of the tech. Then that modified cost gets modified by the City count rule, +5% cost for each city. If you have 4 cities that's 115% multiplier. If these were both active that's 90.85% of the base cost.

This is the adjusted cost, BaseCost * (100 + ScholarsResolutionModifier + Technological Diffusion Modifier)% * ExtraCostFromCitiesMultiplier = actual Cost

At the very moment your research is adjusted with new beakers, this actual cost - which you can see is based on the rules and is kept absolutely up to date based on any changes - is subtracted from your ResearchProgress, to obtain what we will call "Overshot Beakers". That formula again is, for the current tech:

(Team Accumulated beakers) - (base tech cost * (100 + ScholarsResolutionModifier + Technological Diffusion Modifier)% * ExtraCostFromCitiesMultiplier)
where accumulated beakers is previously accumulated beakers + the change amount, so, substituting from the ChangeResearchProgress() call, is actually

(Previously accumulated beakers + change amount) - (base tech cost * (100 + ScholarsResolutionModifier + Technological Diffusion Modifier)% * ExtraCostFromCitiesMultiplier)

The preceding formula is your "Overshot beakers", and it is your Overshot beakers to which the cap is applied. This is not your overflow; this is the measure of surpassing the current tech. When your overshot beakers are 0 or more, it means you completed the tech. And notice, since the above equation does not involve your overflow, it means your overflow does not get you science.

Before we go further, let's call your privately owned overflow quantity your "Excess beaker stockpile". If you get a tech, then your Overshot beakers are added to your Excess beaker stockpile. Actually in a team game, the player responsible for overshooting the tech gets the excess beakers.

The last part of this mystery is the doResearch function, which as I said is the only place your overflow gets zeroed. I already quoted that function.

The bulb call, which I looked up, is straightforward. It calls the previous function on the change amount equal to the bulb quantity.
I will have to come in and give a complete rundown of the process in a separate, yet other, post.

I know why the exploit exists. I also know it's still there in a certain form - it's just harder to control the numbers to use, like flying a nanomachine through a cheerio.
edit: with an original NES controller.
 
This is how you exploit the previously described mechanics:

Get to a turn with zero overflow. This is any turn that starts without you discovering a tech. What you want is a technology to which the remaining beakers to complete it is at least X, where (X + (5 times your bpt)) = your bulb amount, and of which its RAW cost is at least the difference of X from the bulb amount (maximally, 5 times the beakers you make in a turn). If I understand right, as long as you have zero overflow, the game UI can be trusted to tell you the modified cost of a tech and your accumulated beakers in the tech screen, but you must either consult tables or work out the MetCivs modifier yourself in order to calculate back to the raw tech cost.
Spoiler :
(The modified cost is Raw cost * [100 + (proportion of met civs who know the tech / alive major civs) * (SOME CONSTANT that's apparently 30)]/100 * WorldCongressMultiplier * [100 + 5 * CountOfCitiesThatIncreaseTechCosts] / 100 ) .)


Basically, this is roughly when a tech says "3 turns to complete", if your bpt has grown only slightly in the last 8 turns. Bulbing when you are closer than this to the end of a tech is very likely you'll be capped by the 5 * bpt limit.

Bulb a tech like this (an expensive one). Finish your turn with a certain technology on current research. Grow your overflow by bulbing technologies and doing the calculations, to be such that the following is true:

  • Your designated technology calculates a modifier. This modifier applies to the cost like this: ModdedCost = cost * 100 / ResearchModifier[this tech] , and it applies to the overflow before it gets reified like this on your turn start:
    Code:
    reified overflow = overflow * ResearchModifier[this tech] / 100
    Beakers to accumulate = bpt + reified overflow

    Both these multiplications work in the same direction, increasing the value of your actual beaker in, spent as though it were "ResearchMod"% of a beaker (e.g. 120% of a beaker). So the Research modifier basically gets squared, once when reifying overflow, and again when accruing to the decreased cost of a tech. But it can happen even more times than this, if you keep overshooting a fixed beaker sum through multiple techs - multiplying by the modifier on the way "in" and on the way "out". The exploit is maximizing this.​
  • Heuristically, manage your overflow such that after modifiers, it does not exceed 8 times your current bpt. Actually, you need your modified overflow to not be greater than the designated tech's modified cost + 4*next turn's bpt.
  • On your next turn, your overflow will be reified into accumulated beakers. So you want your (modified overflow) + your bpt to get funneled into a tech whose unmodified cost + its modified cost is exactly equal to that summation, not less. You will overshoot by that amount, of course, which you are maximizing under the cap.
  • You're looking for a tech whose cost is at least as specified; a costlier one will also avoid the cap, but you exploit the system less.

Your overflow is now (if maximized) the unmodified cost of the tech you just finished, which should be about half of your overflow last turn. But you put in overflow + bpt beakers, and gained (modified tech cost) of actual research. You got the modified tech cost plus the unmodified tech cost out of this, but you put in just your overflow and your bpt; substituting from the stipulations, you achieved (modified overflow + bpt) beakers out of (overflow + bpt) beakers .

Which is (ResearchModifier * your overflow) of beakers for nothing.

And, these free beakers exist in your overflow, which will get modified before being reified to another tech, becoming ResearchMultiplier * ResearchModifier * the original overflow of research progress. (Those factors are for two different techs, though.)

You want to continue to pilot the given overflow quantity through as many techs as possible, each of which requires a new turn, in order to get that multiplier on the way in. This means minimizing the cost of the tech you apply it to. But you must avoid the overshoot cap, so you can't choose a tech that's too cheap - specifically, not cheaper than half, since the cost is one of the caps.
You can bulb more scientists to bring the overflow up again, in which case, you must queue a costlier tech, but you should enqueue a sequence of techs whose cost decreases to half to use up the overflow when you're "done".

------------
Summary:

  1. Overshoot is not overflow.
  2. Overshoot is capped, then accumulated to overflow.
  3. Overflow gets dumped when your turn starts and your research is designated, and at no other time.
  4. Overflow gets modified by the same modifiers that reduce the costs of techs, except inverted; whereas costs go down by the formula (* 1/(1+x)), overflow is increased by (*(1+x)) .
  5. You must not overshoot a tech inside of "3 turns remaining" with a bulb. That gets capped.
  6. The unmodified cost of a tech is the maximum you can accumulate to overflow after completing that tech. This is only distinct from the previous cap if the game says it would take 5 turns to complete that tech from nothing.
  7. Apart from these, you can add bulbs however you please, but in order not to lose beakers next turn, you must have a sufficiently costly tech to dump the overflow into.
  8. Everything hinges on finding a tech that costs enough to overflow out of, while having techs that you can overshoot by enough to make up overflow with.

If you're not trying to use the exploit, then

A) TOO BAD, it's built into the game just like before, and is happening every turn, every time you overshoot a tech by enough beakers that the modifiers don't get rounded into 0 (guaranteed no rounding at 100 of course; amounts like 10 or 20 for even 10% or 5% modifier.)
B) You can bulb to complete a tech, and bulb to complete another tech, or bulb to not complete a tech (although that's just suboptimal for external reasons), and as long as that overshoots by not a lot each time, you can get a huge overflow, which may be safely dumped into the very expensive tech you just unlocked with all the bulbs you just did.


Since the overshoot is capped to 5 times your current bpt, with the previous considerations of the exploit, the maximum effectiveness of the exploit, accessible per turn, is like 2 extra turns of research: say 25% research modifier, with the right tech, you're putting in 1.25 OVERFLOW :c5science: , which can't be more than the modded research cost + 4*bpt, but the modded cost is related to the cost which is related to the bpt as:
modded research cost = 1/1.25 * COST ; COST > 5* bpt

When the modded cost goes up the overflow goes down, so finding the minimum in this inequality is where moddedresearchcost = 4*bpt, which, incidentally, means overflow is just 8*bpt, so the modifier (fractional part of the multiplier) of your overflow is a whole 2 bpt beakers - which is the amount of free research you get on your turn. If you've met fewer civs or not passed Scholars, it's less.

The exploit occurs because overflow is multiplied before being applied. I cannot think of why this operation even exists at all, let alone how obviously backward the math is. If this line were removed, then the 2013 cap patch could be removed as well, as there would be no cumulative benefit to overflow growth at all. Players could bulb without paranoid bean counting. The overflow would apply to a tech, subtract to a difference, get stored, and apply to something else. I think I even know how to make it give you all the techs you've earned in one turn, but I don't know if the UI can actually handle that.

edit: Also, due to another documentation error, never start your turn without a tech set for research (if that's even possible). It divides the research you accumulate by 100.
 
It would seem that complicating this issue as much backfired quite dramatically. It would also seem that I've been doing the bulbing wrong all this time :lol:
 
I figured out why the player has a function that tells you researchprogress which adds the actual ResearchProgress to the overflow, yet I can't find a reference to this function.

The UI uses it to display your tech progress. It's why the game says you have 44 beakers or whatever in every tech for the turn you're nurturing overflow. This amount is a lie. However, it does allow you to track your overflow precisely so long as there's a tech you know you haven't spent anything on - the amount shown there is exactly your overflow. Subtract that from the tech you're working on to get your actual beakers on that tech.
 
im wondering if modders can try to make it so the math display correctly for the players?
 
Can you explain (as briefly as possible without formulas) for stupid people what's the correct way to bulb to get the maximum benefit? :rolleyes:

P.S. I did not say I was stupid :D
 
I'll gladly be that example of a stupid person if we can get a precise summary on what to do and not to do :)

Agreed it is tough for the casual reader to follow this thread and apply it back to the game without spending a few hours building a flow chart.
 
Top Bottom