Skip to content

Change batch/parallel Ranged Ingredient rolls from uniform random to gaussian random#4212

Draft
DilithiumThoride wants to merge 2 commits into
1.20.1from
dt/uniform-ranged
Draft

Change batch/parallel Ranged Ingredient rolls from uniform random to gaussian random#4212
DilithiumThoride wants to merge 2 commits into
1.20.1from
dt/uniform-ranged

Conversation

@DilithiumThoride
Copy link
Copy Markdown
Contributor

What

For normal ranged ingredients using UniformInt as their provider
When modified by a RecipeModifier to add batch/parallel counts, also replace the Uniform provider with a Gaussian provider, so that the roll is somewhat closer to "average" for a large number of rolls.

Implementation Details

The implementation here is still somewhat weird. Tech suggested using the Central Limit Theorem to modify the Gaussian standard deviation, but the Gaussian Random sources I have access to do not accept params to modify with. So this is getting held as a draft until I either have an answer to that problem, or we decide it doesn't matter.

@DilithiumThoride DilithiumThoride added type: refactor Suggestion to refactor a section of code 1.20.1 Release: Major - 0.X.0 Releases focused on Content, changes to gameplay; While maintaining mostly API stability. labels Nov 21, 2025
@github-actions github-actions Bot added the Tests: Passed Game Tests have passed on this PR label Nov 21, 2025
@krossgg
Copy link
Copy Markdown
Contributor

krossgg commented Nov 22, 2025

You could just call it a NormalInt, since you're trying to adhere to a scaled normal distribution. The CLT is just a way of justifying the fact that the sample average distribution for a population of independent variables will tend towards a normal distribution as the sample size becomes larger. Skip to the last paragraph for TL;DR.


In our case, we are essentially rolling $n=$parallel identical dice (discrete uniform distribution) that have faces from $a=$minInclusive to $b=$maxInclusive and summing up the results of each die.

The mean $\mu$ and variance $\sigma^2$ of one die is

$$\mu=\frac{a+b}{2} \text{ and } \sigma^2 = \frac{(b-a+1)^2 - 1}{12}$$

(Taken from Wikipedia, but this can be confirmed with the formulas for expected value and variance)

Then, when rolling $n$ dice and summing the values, the mean and variance change linearly with $n$, giving us

$$\mu_n=n\mu=n\frac{a+b}{2} \text{ and } \sigma_n^2 = n\sigma^2 = n\frac{(b-a+1)^2 - 1}{12}$$

If we were to continue doing these rolls of $n$ dice and observing the distribution of the sums, it would look more and more like a bell curve as $n$ increased. That is to say, the sums would tend towards being normally distributed with a mean of $\mu_n$ and variance of $\sigma_n^2$, or $\sum_{i=1}^n X_i \sim N(n\mu, n\sigma^2)$

If you want to learn more (and with visuals), 3b1b has a great video on the topic.


Simply put, you just need a sample from a normal distribution that has the mean and variance that we want. This is fairly easy because the normal distribution is defined very conveniently. You can just take a random sample from the standard normal distribution (via a call to nextGaussian()), then scale it by $\sigma$ and shift it by $\mu$.

float mean = parallel * (min + max) / 2f;
int s = max - min + 1;
float sd = Math.sqrt(parallel * (s * s - 1) / 12f);
return random.nextGaussian() * sd + mean;

Also I just looked and Mojang already has a ValueProvider that does this - ClampedNormalInt xdd

@DilithiumThoride
Copy link
Copy Markdown
Contributor Author

That makes this all so much easier to both understand and implement. Thank you.

@github-actions github-actions Bot added Tests: Failed Game Tests have failed on this PR and removed Tests: Passed Game Tests have passed on this PR labels Nov 22, 2025
@krossgg
Copy link
Copy Markdown
Contributor

krossgg commented Nov 22, 2025

I thought about it again and there is a small inconsistency in the math here. Our ContentModifier class supports both multiplication and addition. I don't think anyone uses it (could be wrong), but regardless it should work properly.
In my opinion, the parallel local (you should rename it to like n or something, parallel is kind of a misnomer, since it would work for any multiplier), should depend specifically on the multiplier part of the modifier. The min and the max are still affected by both the multiplier and the adder, since that just changes the range of the roll.

Glad to have helped you learn though :^)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.20.1 Release: Major - 0.X.0 Releases focused on Content, changes to gameplay; While maintaining mostly API stability. Tests: Failed Game Tests have failed on this PR type: refactor Suggestion to refactor a section of code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants