+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. 



Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.