Farms rotating a crop and green manure don't always produce the same harvest

Farming & Food
baschana [discord] · 6 months ago

sandbox world with two farms and some sinks, as well as permanent rain to let them keep running

i can't see any reason why these two farm setups should produce different harvests

and its even inconsistent based on the tier of the farms/crop yield bonuses!

image.jpg
image.jpg
image.jpg

baschana [discord] · 6 months ago

its been running for a bit: i get 61 potatoes per harvest with 2% bonus from an office, haven't seen any farm produce a different amount than its neighbor, but!

if it takes 5 months to make 61 potatoes, thats 12.2, not 12.3 or 12.4 as displayed

image.jpg

i imagine theres an issue with the day it spends "waiting for a crop to be planted"

eryk500 [discord] · 6 months ago

This may very well be down to the rounding error
that the custom fixed-point type Mafi.Fix32 has.

The maximal precision of it is exactly 1/1024 (or to put
it in other words, it's fractional part is only 10 bits long);
if the calculations end-up approaching very close to 0 during
any point, the overall accuracy may be thrown out of the window
(which does in fact happen here, since the fertility calculations
work on percentages, which don't quite align to the 1/1024 steps).

Then there's the fact that you're seeing a "normaly"-rounded number;
not floored, not ceilinged & certainly not one that wasn't preprocesed
like this, so whatever you see, you only see so many digits in the UI.

eryk500 [discord] · 6 months ago

🤔 Actually, now that I've had a look at that, fertility calculations
use Mafi.Percent which is more precise than Mafi.Fix32,
but the UI is using Mafi.Fix32 to display that value, after dividing
an integer yearly production quantity/rate calculated ahead of time,
then converting that to Mafi.Fix32, then dividing that by 12,
potentialy leading to these inaccuracies (when combined with
the fact that fertility calculations use Mafi.Percent instead).

eryk500 [discord] · 6 months ago

Also, it doesn't help that water consumption numbers
are stored in what effectively are Mafi.Fix32 too.

eryk500 [discord] · 6 months ago

Ok, no I see the issue here.

It's not to do with display rounding the value, but instead
that integer yearly production quantity/rate calculation logic.

That is calculating that integer number by "scaling" (or multiplying)
various generated Mafi.Percent numbers (like the proportion
of a year's time to the time to grow a crop & the fertility calculations)
for each slot independently & then just summing up the results
if multiple crops happen to be producing the same resource.

This is very flexible, especially is someone wants to make
multiple recipes for the same resource's production, but
in cases where the rounding happens to not align right,
you end-up with this discrepency.

eryk500 [discord] · 6 months ago

The issue here most likely stems from the scaling by the proportion:
[days to grow this crop] / [total number of days for this farm to complete a full 4 crop cycle],
which (almost certainly) reports the half for the case where all crop slots populated, in comparison to the case
where only half of them are populated; this is an issue, because the yearly production value is effectively stored
as an integer value & the yearly production rate for the 2 crop slot case ends-up being calculated to an
odd value, where as in the 4 slot case that odd number's value gets floor-evened due to the rounding
caused when converting to that integer before accumulating that value from the other slot.

eryk500 [discord] · 6 months ago

This is the code for calculating that (as dnSpy is decompiling it):cs Percent percent = Percent.FromRatio(360, value.DaysToGrow, false); Percent percent2 = Percent.FromRatio(value.DaysToGrow, this.TotalRotationDurationDays, false); Percent percent3 = this.NaturalFertilityEquilibrium.Max(this.StoredFertilizerCount.IsPositive ? this.FertilityTargetValue.Min(this.MaxFertilityProvidedByFertilizer) : this.FertilityTargetValue); Quantity quantity = productProduced.Quantity.ScaledBy(percent * percent3 * percent2).ScaledBy(this.m_yieldMult.Value); int num2 = -1; for (int k = 0; k < num; k++) { if (this.m_avgYieldPerYear[k].Product == productProduced.Product) { num2 = k; quantity += this.m_avgYieldPerYear[k].Quantity; break; } } if (num2 < 0) { num2 = num; num++; } this.m_avgYieldPerYear[num2] = new ProductQuantity(productProduced.Product, quantity);
value contains the prototype of a crop (recipe) assigned to a slot in a farm; and this is an object of the Farm type.

The issue is most likely with the percent2 value (as that is what the previous message is talking about) &
the fact that the Quantity type is just a re-labeled int, so at the end of each ScaledBy call,
the value gets effectively re-cast to an integer, loosing any potential remainders,
that could potentially add-up to a whole additional unit.

6 Showing 1–12 of 12