# 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`, `distance` and
  more.
- **Angles:** `angleOf`, `normalizeAngle`, `lerpAngle` and 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.

```mermaid alt="seed('arena') gives a starting state. Each draw from a state gives a value and the next state, which the following draw uses, and so on."
flowchart TB
  seed["seed('arena')"] --> d1(["draw"])
  d1 --> v1["value"]
  d1 -- "next" --> d2(["draw"])
  d2 --> v2["value"]
  d2 -- "next" --> d3(["…"])
```

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:

```ts title="src/map.ts"
// The same seed gives the same sequence on every host.
let rng = seed("arena-map-7");

const roll = randomInt(rng, 1, 6);
rng = roll.next;

const order = shuffle(rng, ["red", "blue", "green"]);
rng = order.next;
```

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:

```ts title="src/map.ts"
// An independent stream for map layout, so other draws don't shift it.
const layout = fork(rng);
rng = layout.next;

let mapRng = layout.value;
const obstacles = randomInt(mapRng, 3, 8);
mapRng = obstacles.next;
```

See the [`disko:math` reference](/reference/math) for every function.
