Packages

random

Seedable random numbers, picks, shuffles and strings for games, sampling and test data, and when to use crypto instead.

import "std:random"

std:random makes pseudo-random values: dice rolls, picks from a list, shuffles, random text. It is fast and, importantly, repeatable: seed it and the same sequence comes out every time, which makes games, simulations and tests reproducible.

It is not safe for secrets. Passwords, tokens and keys need std:crypto, because a seeded generator can be predicted.

Examples on this page that print raw random values are marked as varying, since their output changes on every run. The ones that call random.seed first always print the same thing.

Numbers#

random.number() is from 0 up to but not including 1. Give it one bound for 0 up to that, or two for a range. random.int(min, max) is a whole number and includes both ends, so random.int(1, 6) is a die.

main.osl
import "std:random"

log random.number()
log random.number(10)
log random.number(5, 10)
log random.int(1, 6)
Click the code to edit it

You can rely on the ranges even when you can't predict the value. Here we roll a thousand dice and check every roll was a legal one:

main.osl
import "std:random"

auto ok = true
for _ in 1 to 1000 (
  auto roll = random.int(1, 6)
  if roll < 1 or roll > 6 ( ok = false )
)
log ok
Click the code to edit it

Seeding#

random.seed(n) restarts the generator. Seed it with the same number and every call after that repeats:

main.osl
import "std:random"

random.seed(42)
auto first = [random.int(1, 100), random.int(1, 100), random.int(1, 100)]

random.seed(42)
auto second = [random.int(1, 100), random.int(1, 100), random.int(1, 100)]

log first == second
Click the code to edit it

Without a call to random.seed, each run starts differently. Seed with a fixed number in tests, and with something like the time (date.now().unix()) when you want a repeatable value you can also record and replay later.

Choosing and sampling#

random.item(items) picks one item, typed as the array's items are. random.sample(items, count) picks several different ones, in random order. Neither changes the array.

main.osl
import "std:random"

random.seed(7)
string[] names = ["Ada", "Bo", "Cy", "Di", "Ed"]

string winner = random.item(names)
string[] team = random.sample(names, 3)

log names.contains(winner)
log team.len
log team.deDupe().len
log names.len
Click the code to edit it

team.deDupe().len being 3 shows the picks are all different. Asking for more items than the array has returns them all, shuffled.

Shuffling#

random.shuffle(items) returns a shuffled copy and leaves the original alone:

main.osl
import "std:random"

random.seed(1)
auto deck = [1, 2, 3, 4, 5, 6, 7, 8]
auto shuffled = random.shuffle(deck)

log deck
log shuffled.len
log shuffled.toSorted() == deck
Click the code to edit it

Sorting the shuffled copy gives the deck back, so nothing was lost or duplicated. That is a good property to test: you can't assert the order, but you can assert what must stay the same.

Chance#

random.bool() is a coin flip. Give it a probability from 0 to 1 to weight it. 0 never gives true and 1 always does:

main.osl
import "std:random"

log random.bool(1)
log random.bool(0)

random.seed(3)
int hits = 0
for _ in 1 to 1000 (
  if random.bool(0.25) ( hits++ )
)
log hits > 150 and hits < 350
Click the code to edit it

Bell curves#

random.gaussian() has mean 0 and standard deviation 1. Pass a mean and a standard deviation for another curve. Most values fall near the mean, which suits heights, noise and anything natural:

main.osl
import "std:random"
import "std:math"

random.seed(5)
number[] heights = []
for _ in 1 to 2000 (
  heights.append(random.gaussian(170, 10))
)
auto mean = math.avg(heights).unwrap()
log mean > 168 and mean < 172
Click the code to edit it

Random text#

random.string() gives 16 letters and digits. Pass a length, and optionally an alphabet to draw from:

main.osl
import "std:random"

random.seed(9)
log random.string().len
log random.string(8).len

string bits = random.string(10, "01")
log bits.len
log bits.replace("0", "").replace("1", "").len
Click the code to edit it

The last line removes every 0 and 1 and finds nothing left, so only the alphabet was used. Use this for test data, throwaway ids and filler. For tokens that protect something, use std:crypto.

Pattern: a seeded game#

Seeding makes a whole game reproducible: the same seed is the same dungeon. Share the seed and players share the map.

main.osl
import "std:random"

def rollDice(int count) int[] (
  int[] rolls = []
  for _ in 1 to count (
    rolls.append(random.int(1, 6))
  )
  return rolls
)

random.seed(2024)
auto a = rollDice(5)
random.seed(2024)
auto b = rollDice(5)

log a == b
log a.len
Click the code to edit it
Quick check

A test needs the same "random" shuffle on every run. What do you do?

Your turn

A fair die

Finish roll so it returns a random whole number from 1 to sides, both ends included. The loop rolls a thousand times and checks every roll was legal.

main.osl
Ctrl Enter runs
Expected output
true

API reference#

9 members, generated from the std:random source of OSL 9.3.0.

Functions

#random.bool(any ...probability) boolean

bool returns true or false with equal odds, or true with the given probability from 0 to 1, as in random.bool(0.25). Throws ValueError for a probability outside 0 to 1.

#random.gaussian(any ...meanAndStdDev) number

gaussian returns a normally distributed number: gaussian() has mean 0 and standard deviation 1, and gaussian(mean, stdDev) uses the given ones. Throws ValueError for a negative stdDev.

#random.int(int min, int max) int

int returns a whole number from min to max, including both ends, so random.int(1, 6) rolls a die. The bounds may come in either order.

#random.item(array items) any

item returns a random item of items, typed as the array's items are, or null (the item type's zero value for a typed array) when items is empty.

#random.number(any ...bounds) number

number returns a random number. number() is from 0 up to but not including 1, as JavaScript's Math.random() is, number(max) from 0 up to max, and number(min, max) from min up to max.

#random.sample(array items, int count) array

sample returns count different items of items in random order, typed as items is, or all of them shuffled when count is larger than items. Throws ValueError for a negative count.

#random.seed(int seed)

seed restarts the generator from seed, so the same seed always produces the same sequence of results from every random function.

#random.shuffle(array items) array

shuffle returns a shuffled copy of items, typed as items is, and leaves items unchanged.

#random.string(any ...lengthAndAlphabet) string

string returns random characters: string() gives 16 letters and digits, string(length) gives length of them, and string(length, alphabet) draws from the characters of alphabet instead. Throws ValueError for a negative length, one over 16777216, or an empty alphabet, and TypeError for other argument types.