Randomness & determinism
Seeded random numbers and deterministic math with disko:math.
Some parts of a game must come out exactly the same every time: a map generated from a seed, a replay of last night's match, a result computed on one machine and checked on another. Code that always gives the same result for the same input is called deterministic.
JavaScript's built-in math doesn't guarantee that. Math.sin or Math.pow
can return slightly different numbers on different computers, and
Math.random gives different numbers every run. Usually that doesn't
matter. When it does, use the disko:math standard library instead: its
results are identical, bit for bit, on every computer.
Deterministic math
disko:math has its own versions of the math functions that can differ
between machines: sin, cos, tan, asin, acos, atan, atan2,
exp, log, log2, pow and hypot. It also has helpers you'd otherwise
write by hand:
Numbers:
clamp,lerp,remap,smoothstep.Vectors:
vec,add,scale,normalize,rotate,distanceand more.Angles:
angleOf,normalizeAngle,lerpAngleand more.
Two conventions to know:
Angles are in radians.
The y axis points down on screen, as it does in most 2D graphics, so a positive rotation turns clockwise.
Mistakes fail loudly: a wrong type throws a TypeError, and an impossible
value, such as normalizing a vector of length zero, throws a RangeError.
Seeded random numbers
A seed is a starting point for a sequence of random numbers. The same seed always produces the same sequence, which is how a seed can stand for a whole map: share the seed, and everyone generates the same map.
Most random number generators hide their state. disko:math doesn't: you
hold the state yourself and pass it along. Each draw gives you a value
and the next state to use for the following draw.
Because nothing is stored globally, two parts of your game can't disturb each other's numbers by accident, and the same seed always replays the same sequence.
seed(input) accepts a safe integer or a string:
The draw functions:
| Function | Returns |
|---|---|
random | a number from 0 (included) to 1 (not included) |
randomRange | a number in a range |
randomInt | a whole number in a range, both ends included |
randomBool | true or false |
pick | one element of an array |
shuffle | a new, shuffled copy of an array |
Independent streams
Sometimes you want two sequences that don't affect each other, for example
one for the map and one for power-ups, so that adding a power-up doesn't
change the map. fork(state) splits off an independent child state as its
value, while next continues the original:
See the disko:math reference for every function.