+1 vote
by (710 points)


I am trying to pick a random number (lets say between 1 and 100), but I would like numbers near the middle to have a much higher chance of being picked than numbers at the extremes. Usually what I do is:

<<set $number to either (random(0,40),random(40,60),random(40,60),random(40,60),random(60,100))>>

Is there a more elegant way to pick items from a list with a bell-curve like frequency? I use the same approach for strings, so being able to select strings from a list randomly at a custom frequency for each string would be nice too.

2 Answers

+1 vote
by (8.6k points)
selected by
Best answer

You can use the Box-Muller method:

/* Generate two independent random numbers in the [0, 1] range */
var u = Math.random();
var v = Math.random();
/* calculate the normally distributed random values x and y from them */
var x = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
var y = Math.sqrt(-2.0 * Math.log(u)) * Math.sin(2.0 * Math.PI * v);

The numbers x and y are normally distributed with a mean of 0 and standard deviation of 1. For a different standard deviation, multiply them by that; for a different mean, add the mean to them. In your example, the mean would be 50.5 and the standard deviation about 18 to 25.

I also created a helper module for this and a bunch of other useful distributions here: https://github.com/Akjosch/sugarcube-modules/blob/master/module/random.js

Using it (copy & paste into the JavaScript portion of your game, include as a JavaScript file if using TweeGo, or load via requireJS if you're using it), the code to generate a random value is:

/* in StoryInit */
<<set setup.distribution = new setup.Random.NormalDistribution(50.5, 18)>>

/* In all passages using it */
<<set _randomValue = setup.distribution.sample()>>

Note that a proper normal distribution can supply you with values outside the (1, 100) range, and it will generate floating-point values. For the generation of values strictly inside a specific range you can use the triangular distribution (with the parameters 1, 50.5 and 100) or the Kumaraswamy distribution (which generates values strictly between 0 and 1; see its Wikipedia page for more details about the shapes possible) - or run the results through Math.clamp(). To get only integer number, use Math.round().

+1 vote
by (980 points)

Hi Choices,

I googled this out of curiousity. Here is a thread that explains different ways to approach this question. I am not sure how well any of them translate into Twine’s system.