Food waste at hight surplus

sorcdk

Chieftain
Joined
Jun 26, 2014
Messages
75
Location
Denmark
I was poking around in the c++ source file to figure out how food waste was calculated, and in there I noticed a bug in the code, which could explain some weird behavior I had notice in an earlier playthrough.
It is specifically the following line (CvCity.cpp@9037 in version 10573):
Code:
return calculatedWaste[MAX_SURPLASS-1] + (surplass - MAX_SURPLASS + 1);
The problem is that calculatedWaste might not have been calculated to its end (MAX_SURPLASS-1), which means it will use whatever value was stored in there. This could in principle be any float value, but in practice it will tend to be 0.
This coincide with a phenomenon I experiance where some cities with very high surplus suddenly had much smaller amounts of wasted food, causing them to grow extremely fast.
A solution to this would to replace the array reference with a call to calculate the value if not already calculated (a recursive call to foodWastage).

I also noticed the formula for food wastage per extra surplus assymtotically approaches 1 - wastageGrowthFactor / (1+wastageGrowthFactor) from bellow (and we are VERY close to it by MAX_SURPLASS), but that abovethe maximum we have all food be wasted. It would make more sense to use the assymtotic limit as the efficiency above the amount we will bother to calculate, since the behavior would be extremely close to what we would have gotten if we let MAX_SURPLASS go toward infinity. This also mean we could lower MAX_SURPLASS to around 150-200, since we have less than 1% to a few ppm error in food ratio left after wastage (which is a more meaningfull error than amount of waste, since the limit of food ratio left after wastage is roughly 4.8%.

I therefor recommend we change the line to:
Code:
return foodWastage(MAX_SURPLASS - 1) + ((float)1.0 - wastageGrowthFactor/((float)1.0+wastageGrowthFactor)) * (surplass - MAX_SURPLASS + 1);
 
I was poking around in the c++ source file to figure out how food waste was calculated, and in there I noticed a bug in the code, which could explain some weird behavior I had notice in an earlier playthrough.
It is specifically the following line (CvCity.cpp@9037 in version 10573):
Code:
return calculatedWaste[MAX_SURPLASS-1] + (surplass - MAX_SURPLASS + 1);
The problem is that calculatedWaste might not have been calculated to its end (MAX_SURPLASS-1), which means it will use whatever value was stored in there. This could in principle be any float value, but in practice it will tend to be 0.
This coincide with a phenomenon I experiance where some cities with very high surplus suddenly had much smaller amounts of wasted food, causing them to grow extremely fast.
A solution to this would to replace the array reference with a call to calculate the value if not already calculated (a recursive call to foodWastage).

I also noticed the formula for food wastage per extra surplus assymtotically approaches 1 - wastageGrowthFactor / (1+wastageGrowthFactor) from bellow (and we are VERY close to it by MAX_SURPLASS), but that abovethe maximum we have all food be wasted. It would make more sense to use the assymtotic limit as the efficiency above the amount we will bother to calculate, since the behavior would be extremely close to what we would have gotten if we let MAX_SURPLASS go toward infinity. This also mean we could lower MAX_SURPLASS to around 150-200, since we have less than 1% to a few ppm error in food ratio left after wastage (which is a more meaningfull error than amount of waste, since the limit of food ratio left after wastage is roughly 4.8%.

I therefor recommend we change the line to:
Code:
return foodWastage(MAX_SURPLASS - 1) + ((float)1.0 - wastageGrowthFactor/((float)1.0+wastageGrowthFactor)) * (surplass - MAX_SURPLASS + 1);
I spoke to the designer of this code quite a long time ago and kinda understood it a little then but since I've written it off as beyond my comprehension. I felt it was a little... difficult to explain to say the least.

If anyone else can follow this discussion, I'm happy to make the suggested change if it can get a second from a team member.
 
The author explained his calculation here: https://forums.civfanatics.com/threads/wasted-food.446490/page-4#post-11310204

Here is my interpretation:

There are two variables declared in the code block, the first one:
  • wastage start consumption percent = 50
Means that part of your food surplus, equal to half of the consumption by your citizens, will never go wasted. The second one:
  • wastage growth factor = 0.05
For convenience, let's just use B for this number, and B$ for its complement (B$=1-B=0.95).

The formula takes the input S, which is the raw surplus minus half the consumption by population, and calculates through recursion (calling on itself):
  • W(S) = W(S-1) + [ 1 - ( B + B$^S ) / (1+B) ]
  • W(0) = 0
Which we can simplify through these steps:
First we combine and reduce the terms 1 - B/(1+B):
  • W(S) = W(S-1) + [ 1/(1+B) - B$^S/(1+B) ]
Then we expand the recursive terms until W(0):
  • W(S) = W(0) + [ S/(1+B) - ( B$^1 +...+ B$^S ) / (1+B) ]
The latter term here ( B$^1 +...+ B$^S ) is a geometric series and evaluates to [ B$ - B$^(S+1) ] / ( 1 - B$ ), which equals to [ B$ - B$^(S+1) ] / B. Therefore,
  • W(S) = { S - [ B$ - B$^(S+1) ] / B } / (1 + B)

As OP suggests, this formula approaches an asymptotic limit and we should lower the MAX_SURPLASS to 150-200. It can be even lower. See the following graph. The approximation gets very close already at 80. Note that the x axis, or S, does not mean the raw surplus (see above).

Spoiler Graph :
2019-05-18 (16).png

The wastage amount approximates ( S - B$/B ) / (1+B) as S goes to 100 and above. We can therefore cache the curve for S less than 100, and use the simpler formula otherwise.

PS. I used the notation B$ because B′ would not go together with the ^ (power) symbol well.
PS. Thank you @sorcdk, your instructive article below helps me rewrite it better.
 
Last edited:
The author explained his calculation here: https://forums.civfanatics.com/threads/wasted-food.446490/page-4#post-11310204

Here is my interpretation:

There are two variables declared in the code block, the first one:
  • wastage start consumption percent = 50
I believe it's intention is to prevent wastage if our food surplus, that is total food minus the consumption by pops, doesn't pass 50% of consumption by population. The second one:
  • wastage growth factor = 0.05
For convenience, let's just use B for this number, and B$ for its complement (B$=1-B=0.95).

The formula takes the raw surplus amount as S, and calculates through recursion (calling on itself):
  • W(S) = W(S-1) + [ 1 - ( B + B$^S ) / (1+B) ]
  • W(0) = 0
Which we can simplify through steps:

W(S) = W(S-1) + [ 1/(1+B) - B$^S/(1+B) ]
W(S) = W(0) + [ S/(1+B) - ( B$^1 +...+ B$^S ) / (1+B) ] => the ( B$^1 +...+ B$^S ) part would just equal to [ B$ - B$^(S+1) ] / ( 1 - B$ ), which is simply [ B$ - B$^(S+1) ] / B.

Therefore, W(S) = { S - [ B$ - B$^(S+1) ] / B } / (1 + B) is the final formula without recursion.

The wastage amount approximates ( S - B$/B ) / (1+B) as raw surplus goes to 100 and above. We can cache it for raw surplus less than 100, and use the simpler formula otherwise.

In this formula, the coefficient of wastage growth is 1/1+B.

(Sorry a lot of mistakes
The overall idea in this is nice. I went through the calculates and there are a few small mistakes, mainly forgetting to some extra terms, which changes the final result. Before I go through these, I thought it would be best to shown how I derived the assymtotic boundary.
Using your notation, we can rewrite the recoursive form of W(S) as follows:
  • W(S) = W(S-1) + w(S)
  • W(0) = 0
  • w(S) = 1 - (B+B$^S)/(1+B)
We then notice that this equivalent to a sum:
  • W(S) = sum_(s=1)^(S)(w(s))
We can therefor just analyse w(s), which is "the wastage on the last surplass". Note how w(s) contains two parts, a 1 and the part with B's, so we can analyse it as the following:
  • w(s) = 1-f(s)
  • f(s) = (B+B$^S)/(1+B)
Notice how f(s) is the amount of food left after wastage on the last surplass. We can characterise f(s) as two terms initially summing to 1 (at s = 1) and then having the second part exponentially decay. Note that the divisor of B+1 normalizes f(0) = (B+B$^0) / (1+B) = (B+1) / (1+B) = 1.
This means that as s-> infinity, then B$^s -> 0, and we get:
  • limit_(s->infinity) f(s) = (B+0)/(1+B) = B/(B+1)
Since f(s) is the only part of w(s) that changes with s, we can just plug this into w(s):
  • limit_(s->infinity) w(s) =1 - limit_(s->infinity) f(s) = 1-B/(B+1) ~ 0.952 @ B=0.05
Which is the assymtotic limit from my original post.
As for the mistakes in your deriviation (which are different than before you edited it, I will only comment on the version shown in the qoute):
In the step where you distributded the divided (1+B), you forgot the term based on B, and the 1 term should not have the division applied to it. It should have been: W(S) = W(S-1) + [ 1 - B/(1+B) - B$^S/(1+B) ].
Carrying this forward we get from the point of summing the recursive terms:
  • W(S) = S - {S*B+ [ B$ - B$^(S+1) ] / B } / (1 + B)
Rearranging the terms to have a part dependent on S and one independent, we get:
  • W(S) = [S * {1 - B/(1+B) } + B$^(S+1)/(B*(B+1)) ] - [B$/(B*(1+B))] ~ S * 0.952 + 19.05*0.95^(S+1) - 18.14 @ B = 0.05
The B$ goes to 0 at large S, so W(S) assymtotically approaches:
  • limit_(S->infinity) W(S) = S * {1 - B/(1+B) } - [B$/(B*(1+B))]~ S * 0.952 - 18.1 @ B = 0.05
Let F(S) be the the food after wastage, it is given as F(S) = S - W(S):
  • F(S) = [S * B/(1+B) - B$^(S+1)/(B*(B+1)) ] + [B$/(B*(1+B))] ~ S * 0.048 - 19.05*0.95^(S+1) + 18.1 @ B = 0.05
Looking at the assymtotic limit we get:
  • limit_(S->infinity) F(S) = S * B/(1+B) + [B$/(B*(1+B))] ~ S * 0.048 + 18.1 @ B = 0.05

Personally I think having f(s) decay exponentially causes food above safe surplass to be valued at almost nothing, as you can only get roughly 18 food from all of the exponential decay before the you are effectively evaluating food at 5% value. This causes food bonuses to loose most of their value in terms of improving growth, which causes other kinds of ballance problems through the game. An alternative would be an f(s) with a less drastic decay rate, such as:
  • f_2(s) = B + B$/(1+A*s)
Where A is a new constant determining the decay rate of food efficiency. Forms of 1/(1+A*s) does not have a finite limit when s goes to inifinity, which means that we can let B=0 if we want (since B's effective primary role is the part of food efficiency that is safe from wastage), and still have unlimited growth rate with increasingly poorer efficiency, which in turns means players can stat valueing food bonuses again without this causing too explosive growth of cities. The idea of using the old B is that we can reuse the cache calcullations and use B as an assymtotic boundary for f_2(s) outside of the cache.

Here are some graphs over f,w,F, and W, included with both the base, the assymtotic approximation and the effective version of my proposed f_2, where I set A=0.1:
Spoiler Graphs :
FoodWastagew.png
FoodWastagef.png
FoodWastageWW.png
FoodWastageFF.png
 
Last edited:
The problem is that calculatedWaste might not have been calculated to its end (MAX_SURPLASS-1), which means it will use whatever value was stored in there. This could in principle be any float value, but in practice it will tend to be 0.
Should we precalculate to MAX_SURPLASS-1, and extrapolate by a slope of 1/(1+B), or use direct calculation in place of recursion, while still keeping cache, whichever way is more efficient is the way to go.

In the original code, if we omit the portion warranted by the pop, the food surplus capped at 42, the result of 500-foodWastage(500). I change this to never cap for the sake of simplicity, so the result will be a bit different at the higher end.

<Edit: there is a minimally modified code sample in post #10, where I retain the recursive code.>

Spoiler No recursion code, same functionality as original except when surplass > 500 :
Code:
@@ -8996 +8996 @@ float CvCity::foodWastage(int surplass) const
-#define    MAX_SURPLASS    500
+#define    MAX_SURPLASS    100
@@ -8998 +8997,0 @@ float CvCity::foodWastage(int surplass) const
-    static    int calculatedTo = -1;
@@ -9028 +9027 @@ float CvCity::foodWastage(int surplass) const
-    else if ( surplass <= calculatedTo )
+    else if ( surplass < MAX_SURPLASS )
@@ -9029,0 +9029,4 @@ float CvCity::foodWastage(int surplass) const
+        if ( !calculatedWaste[surplass] )
+        {
+            calculatedWaste[surplass] = (surplass - ((float)1.0 - wastageGrowthFactor - pow((float)1.0 - wastageGrowthFactor, surplass + 1)) / wastageGrowthFactor) / ((float)1.0 + wastageGrowthFactor);
+        }
@@ -9034,12 +9037 @@ float CvCity::foodWastage(int surplass) const
-        if ( surplass >= MAX_SURPLASS )
-        {
-            //    After the max we bother calculating it all gets wasted
-            return calculatedWaste[MAX_SURPLASS-1] + (surplass - MAX_SURPLASS + 1);
-        }
-        else
-        {
-            calculatedWaste[surplass] = foodWastage(surplass-1) + ((float)1 - (wastageGrowthFactor + pow((float)1.0 - wastageGrowthFactor, surplass))/((float)1.0+wastageGrowthFactor));
-            calculatedTo = surplass;
-
-            return calculatedWaste[surplass];
-        }
+        return (surplass - ((float)1.0 - wastageGrowthFactor) / wastageGrowthFactor) / ((float)1.0 + wastageGrowthFactor);
Spoiler The entirety of my foodWastage(), in case we need it :
Code:
float CvCity::foodWastage(int surplass) const
{
#define    MAX_SURPLASS    100
    static    float    calculatedWaste[MAX_SURPLASS];
    int        startWasteAtConsumptionPercent = GC.getDefineINT("WASTAGE_START_CONSUMPTION_PERCENT");
    float    wastageGrowthFactor = GC.getDefineFLOAT("WASTAGE_GROWTH_FACTOR");

    if ( wastageGrowthFactor == 0 )
    {
        wastageGrowthFactor = (float)0.05;    //    default
    }

    if ( startWasteAtConsumptionPercent >= 0 )
    {
        if ( surplass == -1 )
        {
            int iPopulationExponent = getPopulation() - 1; // Each pop past the first increases consumption per population by .1, rounded down.  Each point of population means more actual people the higher the amount goes.
            int iConsumptionPerPopulationBase = iPopulationExponent + (GC.getFOOD_CONSUMPTION_PER_POPULATION() * 10);
            int iConsumptionbyPopulation = (getPopulation() * iConsumptionPerPopulationBase) / 10;
            surplass = foodDifference(true, false) - (iConsumptionbyPopulation * startWasteAtConsumptionPercent)/100;
        }
    }
    else
    {
        surplass = -1;
    }

    //    Nothing wasted if there is no surplass
    if ( surplass <= 0 )
    {
        return 0;
    }
    //    Cache what we can as it's not a trivially cheap computation
    else if ( surplass < MAX_SURPLASS )
    {
        if ( !calculatedWaste[surplass] )
        {
            calculatedWaste[surplass] = (surplass - ((float)1.0 - wastageGrowthFactor - pow((float)1.0 - wastageGrowthFactor, surplass + 1)) / wastageGrowthFactor) / ((float)1.0 + wastageGrowthFactor);
        }
        return calculatedWaste[surplass];
    }
    else
    {
        return (surplass - ((float)1.0 - wastageGrowthFactor) / wastageGrowthFactor) / ((float)1.0 + wastageGrowthFactor);
    }
}
Personally I think having f(s) decay exponentially causes food above safe surplass to be valued at almost nothing, as you can only get roughly 17 food from all of the exponential decay before the you are effectively evaluating food at 5% value. This causes food bonuses to loose most of their value in terms of improving growth, which causes other kinds of ballance problems through the game. An alternative would be an f(s) with a less drastic decay rate, such as:
  • f_2(s) = B + B$/(1+A*s)
Where A is a new constant determining the decay rate of food efficiency. Forms of 1/(1+A*s) does not have a finite limit when s goes to inifinity, which means that we can let B=0 if we want (since B's effective primary role is the part of food efficiency that is safe from wastage), and still have unlimited growth rate with increasingly poorer efficiency, which in turns means players can stat valueing food bonuses again without this causing too explosive growth of cities. The idea of using the old B is that we can reuse the cache calcullations and use B as an assymtotic boundary for f_2(s) outside of the cache.

What are the thoughts from other people, on the food balance problems? I did this post just to post my sample code here...
 
Last edited:
The problem with not using recursion and still keeping the cache is knowing when the cache is already calculated. Your current form (asside from using an incorrect formula - it doesnt factor surplass correctly, and is approximaged by W(S) = S + constant) has the problem of using calculatedWaste before it is assigned a value (specifically when you check whether it is 0).
 
I myself think that there is at least an amount equal to half the food consumed that contributes to growth. The extra food got exponentially diminished because we'd like to create a balance between players where an incredibly high surplus of food would hardly do better than a modest amount.

The problem with not using recursion and still keeping the cache is knowing when the cache is already calculated. Your current form (asside from using an incorrect formula - it doesnt factor surplass correctly, and is approximaged by W(S) = S + constant) has the problem of using calculatedWaste before it is assigned a value (specifically when you check whether it is 0).
I use "if ( !calculatedWaste[surplass] )" to catch the case if it hasn't been calculated.

I checked that:
sum(n=1->S) [ 1 - ( B + B$^n ) / (1+B) ] @ B=0.05 is 77.25 @ S=100, 7.44 @ S=20
{ S - [ B$ - B$^(S+1) ] / B } / (1+B) @ B=0.05 is 77.25 @ S=100, 7.44 @ S=20
could not be missing terms.
 
Last edited:
I myself think that there is at least an amount equal to half the food consumed that contributes to growth. The extra food got exponentially diminished because we'd like to create a balance between players where an incredibly high surplus of food would hardly do better than a modest amount.


I use "if ( !calculatedWaste[surplass] )" to catch the case if it hasn't been calculated
The problem is that caching relies on the system supplying you with a 0 filled array, and this is not necessarily guarenteed (it depends on what mechanics and system that gives you that piece of memory), and I am not familiar with the mechanics that c++ uses for allocating a methods static variable. Worst case it might be platform dependant, which means we could barry a bug in the code that would only show up for some people at some random point in the future.

The problem with food wastage exponential feature is that for most cities outside of the early prehistoric, they will be well into food wastage territory, which means additional bonuses to food have to be weighted based on food efficiency under wastage, and that quickly approaches levels (the 5% limit) where the effect on growth is miniscule. This causes the evaluation to be mostly based around "do I have enough to feed my population times" maybe with a factor 1.5-1.6 on that, and otherwise it is safe to completly ignore. This means that many of those food civics you tend to unlock in the ancient-medival periode are fairly imballanced, since the part which they give you more of is practically useless to you.
 
[...]
I checked that:
sum(n=1->S) [ 1 - ( B + B$^n ) / (1+B) ] @ B=0.05 is 77.25 @ S=100, 7.44 @ S=20
{ S - [ B$ - B$^(S+1) ] / B } / (1+B) @ B=0.05 is 77.25 @ S=100, 7.44 @ S=20
could not be missing terms.
After checking through it, our expressions are indeed equal after some rearrangement.
I have also uploaded a python script (I had to change its ending to .txt, to upload, just change it back to .py), which adds our closed form solutions to the same graphs I posted earlier.
 

Attachments

  • foodWaste.txt
    2 KB · Views: 77
  • Like
Reactions: Anq
Ah! I realize that you can prevent the problem that
calculatedWaste might not have been calculated to its end (MAX_SURPLASS-1)
, by calling foodWastage(MAX_SURPLASS) where it had used internal calculatedWaste[MAX_SURPLASS-1].
Then, the recursive version with little modification is viable too. Note I also extrapolate from the max cached value upwards, as it is unreasonable to cap food for growth at even lower surplus values (500->100).

Spoiler Close to original code, little modification :
Code:
@@ -8996 +8996 @@ float CvCity::foodWastage(int surplass) const
-#define    MAX_SURPLASS    500
+#define    MAX_SURPLASS    100
@@ -9037 +9037,2 @@ float CvCity::foodWastage(int surplass) const
-            return calculatedWaste[MAX_SURPLASS-1] + (surplass - MAX_SURPLASS + 1);
+            //  Anq: After the max we extrapolate linearlly
+            return foodWastage(MAX_SURPLASS) + (surplass - MAX_SURPLASS + 1)/((float)1.0 + wastageGrowthFactor);

The problem is that caching relies on the system supplying you with a 0 filled array, and this is not necessarily guarenteed (it depends on what mechanics and system that gives you that piece of memory), and I am not familiar with the mechanics that c++ uses for allocating a methods static variable. Worst case it might be platform dependant, which means we could barry a bug in the code that would only show up for some people at some random point in the future.
I don't know this for sure, but static fields tend to be initialized with zeroes at compile time, so we don't go through the array and initialize with zeroes. Instance fields initialize at run time, so we need to write that part. (learned this idea before from python class tutorials)
 
Last edited:
This means that many of those food civics you tend to unlock in the ancient-medival periode are fairly imballanced, since the part which they give you more of is practically useless to you.
Can you give examples of this please? What do you mean by "are fairly imbalanced" and "part which they give you more of is practically useless".

How much of this is a direct correlation to T-brd's recent upping of the amount of :food: needed per Citizen to maintain stability and how much :food: are you actually getting from a real example ie value.

Is a Civic that gives +2% :food: to All cities actually giving any :food: with the new :food: consumption rates that have been applied recently?
 
Can you give examples of this please? What do you mean by "are fairly imbalanced" and "part which they give you more of is practically useless".

How much of this is a direct correlation to T-brd's recent upping of the amount of :food: needed per Citizen to maintain stability and how much :food: are you actually getting from a real example ie value.

Is a Civic that gives +2% :food: to All cities actually giving any :food: with the new :food: consumption rates that have been applied recently?
Admittedly much of my experiance with this is from when I was playing 38.5 (I dont have a game in the SVN that is that deep yet). I do therefor not have relevant numbers at this time, but those percentages are applied before consumtion, so any stable size 10+ city (assuming it doesnt already have % bonus to food) would get at least 1 more food before wastage. I do seem to recall also having cities that broke past the calculation limit of 500 surplass in that periode of the game, since I did observe that weird behavior mentioned in the first post.
I am thinking of the old substiance (when it came after bartering), serfdom and that kind. There is also the point if simply not building farms, even when they become really powerfull in raw yields compared to other things, because all they would do is add food I wouldnt need more of (plenty of good cheap food buildings) while losing the other yields that I can use more of.
 
Can you give examples of this please? What do you mean by "are fairly imbalanced" and "part which they give you more of is practically useless".

How much of this is a direct correlation to T-brd's recent upping of the amount of :food: needed per Citizen to maintain stability and how much :food: are you actually getting from a real example ie value.

Is a Civic that gives +2% :food: to All cities actually giving any :food: with the new :food: consumption rates that have been applied recently?
I'm not going to pretend to be able to follow even a portion of what they're talking about here BUT, I do understand that waste deals with the food amount you pull in over and above what the city needs to support itself. It's an amount of loss on overflow that's being calculated. You won't start getting overflow until the needs of the consumption are considered. +2% food will definitely still matter for cities that are eating more per citizen than they were - that's not different.

What I changed was:
1) How much food needs to be collected to trigger population growth
and
2) Base consumption per citizen increases +.1 per citizen after the first.

So this means that (and keep in mind the city rounds the final amounts down after these calculations are performed):

1 Population, the city eats 4 food (as normal - though I think it's actually only 2 in Vanilla BtS).

2 Population, the city eats 8.2 food. 4/population = 8 + the extra .1/population past the first = 2 Pop - 1 = 1 * .1 = .1 * Total Population of 2 = .2 extra. Total: 8.2.

3 Population, the city eats 12.6 food. 4/population = 12 + the extra .1/populatino past the first = 3 Pop - 1 = 2 * .1 = .2 * Total Population of 3 = .6 extra. Total: 12.6

4 Population, the city eats 17.2 food. 4/population = 16 + the extra .1/population past the first = 4 Pop - 1 = 3 * .1 = .3 * Total Pop of 4 = 1.2 extra. Total: 17.2

5 Population, the city eats 22 food. 4/pop = 20 + the extra .1/pop past the first = 5 pop - 1 = 4 * .1 = .4 * Total Pop of 5 = 2 extra. Total 22.

This extra amount REALLY starts to be quite a lot at higher populations:
10 Population, the city eats 49 food. 4/pop = 40 + the extra .1/pop past the first = 10 pop - 1 = 9 * .1 = .9 * Total Pop of 10 = 9 extra. Total 49.

20 Population, the city eats 118 food. 4/pop = 80 + the extra .1/pop past the first = 20 pop - 1 = 19 *.1 = 1.9 * Total Pop of 20 = 38. Total 118.

50 Population, the city eats 445 food. 4/pop = 200 + the extra .1/pop past the first = 50 pop - 1 = 49 *.1 = 4.9 * Total Pop of 50 = 245. Total 245.

100 Population, the city eats 1390 food. 4/pop = 400 + the extra .1/pop past the first = 100 pop - 1 = 99 *.1 = 9.9 * Total Pop of 100 = 990. Total 1390.

If I'm not mistaken, at size 41, the city is eating twice what it did before the change, but at size 1, it's eating the same amount.

The idea was to curb later game city sizes to help with long term balance and curb the need to address overflow factors resulting from stronger specialists being introduced by the new traits, as well as diminish some of the runaway crime and disease problems from massive population numbers, while making food matter more later into the game.



What he may be trying to say is that +2% food may NOT have a meaningful effect when you have enough wastage (the more food you have in excess need, the more is wasted) that you're already wasting everything past a certain amount because you already have a LOT more food coming in than your people need to eat.

One thing they showed, if I was reading this right, is that after a certain amount of food overflow, no additional amount of food generation will go towards anything other than waste. I think he was proposing to create a cap that just keeps giving further diminishing returns on additional food, rather than completely capping how much overflow can be maintained without all excess beyond that point becoming waste - and I do support that - a lot.

If you guys are going to adjust the formula and get all math smart on it, I'm in full support. However...

Please consider how some tags could be created for buildings that would, rather than reduce the amount of food needed to grow or adjust how much food remains after growth, directly affect how much food is wasted.

I have always felt our current waste system was desperately lacking for direct moddability by buildings and tech improvements.

It would be nice if we could say:
  • Adjustments to the amount of food needed to increase population = adjustments to the fertility levels and willingness to reproduce.
  • Adjustments to how much food is left after growth = food storage improvements for foods that do not quickly expire (like granaries)
  • Adjustments to the food wastage = food storage improvements to extend the amount of time that food takes to expire (like icehouses, sausage technology, salting, refrigeration)
Makes sense right?

The current waste mechanism doesn't play into this with modifiable tags at all and I've always wanted to rewrite it eventually to enable the above reconsidered brackets of play adjustment.

You guys think you'd be able to work out a realistic method of food storage along these lines?
 
Last edited:
I can think of 2 relatively easy ways to implement a tag that could affect food wastage (maybe even use both).
  1. Have a tag that modify the ratio of safe surplass based on population. For instance, increasing this by 5% would increase the safe region from 50% to 55%, which would be fairly close to giving you a 10% increase in growth rate.
  2. Have a tag that modifies how far up the surplass calculations you would go. For instance, a 10% increase would divide your surplass with 1.1 before you look up in the table, and the multiply the result with 1.1 again, allowing you to compress more food into the region that allows for reasonable wastage. How elegant this solution is depends on how exactly we want to deal with all of this, as we would have to do some rounding if we want to preserve a cache/force an integer based scheme, which can cause some minor disconituites.
Thinking about option 2 also got me thinking that it might make sense to have the whole wastage (not just the safe part) be affected by either population or food eaten by population. This could by done by having a fixed structure of surplass efficiency, with a constant safe region (possibly added to by with a tag) and then have the surplass first scaled down by a factor of population|food eaten (possibly multiplied/divided by another tag), have it look up how much of that scaled value is left in the fixed structure and then scale it back up to actual surplass. The motivation for this is that it would make sense that the amount you can get in through the heavy waste would be compariable to the amount you can safely have more off without starting to waste it (in the sense that it would be a constant per pop).

The next question is what kind of requirement we want to food wastage in general. What is the goal of it (I assume it is to stop rampant growth of cities too early in the game), and is it supposed to be a full stop sign, something to be lifted more or less through the game, or just a lowering of efficiency in investing in that area (this is the main criteria I have been considering it as, at least until I looked at the code and saw it was effectively a full stop sign).
 
Originally, when I proposed the concept of wastage, I wanted it to be a reflection of how difficult it was for earlier people, and even still to some extent a problem today, to keep excess food from going to waste quickly. It's a reflection of the entropy on excessive efforts. That IS the goal. Stopping rampant growth would be a secondary effect - food consumption increasing per population seems to be doing that even more effectively at this stage. Mostly it's another way to show how technological improvements can have a dramatic impact to enable greater populations than previously possible and to curb problems that nature throws at us as a default.

Any tag that plays into the values in the waste system would have to have a clear 'meaning' in RL terms. If you invent a way to store food longer without it going bad, is that going to reduce the maximum amount of waste or will it simply reduce how much waste would take place AFTER the calculations on waste come up with a conclusion on how much wasted food there should be deducted from the total food income for the round? If you did add a tag that adjusts how much food could be stored before waste, I'd think that would be a reflection of adding to the volume of potential effective storage, as opposed to the efficacy of the storage method itself. So perhaps both would be useful for different purposes. However, keep in mind that our buildings usually assume that they scale to the volume and size that the city requires of them once they are built - aka we don't need to keep building more libraries and schools for emerging communities and additional neighborhoods as the city grows larger - the one library or school building is assumed to represent all of them. We don't build multiple granaries, we just assume that once we've built a granary building, the city keeps adding more as needed naturally. So sometimes volumetric concepts might not be the best approach for a tag, unless somehow you are showing how your new TYPE of granary can enable more food to be stored than previously possible?

All of this is stated as 'food' for thought.
 
  • Like
Reactions: Anq
I'm pleased to see so enthusiastic discussion on this topic by you two (sorcdk & Anq).
At least one of you should become member of this team soon. ^^
There are no pressure to produce results on any members, the only thing we frown upon is someone who breaks the mod with a commit (or actively sabotage it) without any intention to actually fix potential bugs introduced as quickly as possible.

I'm not all that interested in enveloping myself in all the details of the food wastage system, I nevertheless have some questions regarding this discussion to get a clearer picture of the current code and the suggested modification.

A) What are the perceived game-mechanic problems with the current code? (e.g. does it converge too fast/slow toward 100% food wasted?)

B) Is the performance of the suggested new code better?
Any other technical problems with the current code?​

C) In the graph posted by sorcdk, does W_2(s) represent the suggested code and W(s) the current code?
If so, then I find W_2(s) to be a more interesting calibration of the food wastage system.​

D) Do I understand the OP correctly that MAX_SURPLASS is not always calculated (given a value), before being used by the code here?
Also explain the point of MAX_SURPLASS and why it should be lowered as I think was suggested somewhere in this discussion.​

E) Question A is really the big question here, so give a thorough summary of all the important points in a brief and concrete manner when answering it.
Pretend that I haven't read any of your previous posts at all, and there's no reason to rewrite any equations, I can read the discussion closer if I'm curious about them. ^^​
 
Last edited:
What if food waste causes diseases, like diarrhea and dysentery when you eat spoiled food. Rotten meat attracts scavengers which are actually helpful at limiting disease spread. Fruits and vegetables grow molds, but they are easily dumped in earth. Grains in storage have grain beetles that feed on them and cause them to spoil faster. Food preservation like salting and making sausages therefore does not add more net food, instead they prevent the waste of fruits and vegetables and meat. Flour and bread making also does not add more net food, but they make grains edible for a longer time. It's a good idea to review those buildings and give them a certain ability to reduce waste amount instead of giving more food.

To mod the waste calculation isn't a hard thing, once we have a clear vision that some effects are better described by adjusting the waste dynamic. Thank you for always thinking on the ground of realism and make this topic a lot more brighter.

On the other hand, which just came to my mind, keeping too many animal herds in one city may increase the food waste. Depending on how many farm and camp buildings, if one city becomes too dense with them, there is going to be an excess that couldn't benefit the people. This threshold can be dynamic based on the population count. This could also apply to later crop farms and orchard and plantation buildings, although you can usually build a smaller variety of them in a given city, depending on its vicinity sources. The collection of animal buildings is much greater than vegetable ones especially early on, and you also have the freedom to spread the herd or flock with animal units. It wouldn't hurt to treat excess meat sources as waste, as the potential food can be eaten by more people when cities grow. That is, this part of waste actually shrinks when you get more people to feed.
 
Last edited:
A) What are the perceived game-mechanic problems with the current code? (e.g. does it converge too fast/slow toward 100% food wasted?)
There's no issue with the wastage mechanic, but as Thunderbrd said, it lacks moddability to reflect the real-life meaning on food waste.

B) Is the performance of the suggested new code better?
Any other technical problems with the current code?​
Performance-wise, the code without recursion will hardly do any better than the existing code, because the curve is calculated only once in a session running the program. Later references are just lookups.

D) Do I understand the OP correctly that MAX_SURPLASS is not always calculated (given a value), before being used by the code here?
Also explain the point of MAX_SURPLASS and why it should be lowered as I think was suggested somewhere in this discussion.​
It was the case because in the condition that input is greater than MAX_SURPLASS, it calculates the result directly using its internal results-array. This becomes the problem when the input surplass jumps from, say, 360, to 540, in which occasion its internal results-array is only calculated to 360, and the rest are just zeroes. Thus when it looks up the array with the maximum index, it's just zero, not 458 (actually). The fix is simply switch to call on itself with the max input, in which case it will calculate and fill the rest of the array with results to look up.

The MAX_SURPLASS defines the size of the result array for us to look up values on the curve. As he pointed out, the curve approaches a linear asymptote as the input gets larger, and the error of estimate becomes so small that we can safely use the linear expression to extrapolate the result from the furthest defined point.
 
D) Do I understand the OP correctly that MAX_SURPLASS is not always calculated (given a value), before being used by the code here?
Also explain the point of MAX_SURPLASS and why it should be lowered as I think was suggested somewhere in this discussion.
And why is it SURPLASS as opposed to SURPLUS?
On the other hand, which just came to my mind, keeping too many animal herds in one city may increase the food waste. Depending on how many farm and camp buildings, if one city becomes too dense with them, there is going to be an excess that couldn't benefit the people. This threshold can be dynamic based on the population count. This could also apply to later crop farms and orchard and plantation buildings, although you can usually build a smaller variety of them in a given city, depending on its vicinity sources. The collection of animal buildings is much greater than vegetable ones especially early on, and you also have the freedom to spread the herd or flock with animal units. It wouldn't hurt to treat excess meat sources as waste, as the potential food can be eaten by more people when cities grow. That is, this part of waste actually shrinks when you get more people to feed.
I was thinking on how some foods waste faster than others and wondering if food resource access should come with a food waste adjustment index to try to loosely reflect that fact. Meat, for example, could increase the waste index, while grains reduce it. The higher the index, the more excess is wasted. I dunno... just a thought.

There's no issue with the wastage mechanic, but as Thunderbrd said, it lacks moddability to reflect the real-life meaning on food waste.
Well... @sorcdk was trying to point something out... was it somehow shown to be a non-issue that he was trying to address? I wasn't sure I understood the problem exactly.

the curve is calculated only once in a session running the program
What do we mean by session? Game session? So is waste fixed for a city with each load of the game? That wouldn't seem right as it should dynamically change every round shouldn't it?

As he pointed out, the curve approaches a linear asymptote as the input gets larger, and the error of estimate becomes so small that we can safely use the linear expression to extrapolate the result from the furthest defined point.
Yeah, I understood some 10% of that. This is all much higher level mathspeak than I comprehend.
 
What do we mean by session? Game session? So is waste fixed for a city with each load of the game? That wouldn't seem right as it should dynamically change every round shouldn't it?
The output (surplus after wastage) from a given input (surplus amount) is only calculated once for each input smaller than MAX_SURPLASS+1.
So the game look-up the wasted amount from a cache instead of calculating the wasted amount every time.
The calculation should only be needed to be done once per game session.

That's at least how I understood Anq's explanation.
 
Top Bottom