Random

Randomness lives in the random library, an installable package in the loft registry. Add it to a project once with

loft install random

and import it at the top of your file with use random;. The library gives you three functions, all backed by a fast PCG-64 generator:

rand(lo, hi)      a uniform random integer in [lo, hi] inclusive; null if lo > hi
rand_seed(seed)   seed the generator so a run is reproducible
rand_indices(n)   a vector holding 0, 1, ..., n-1 in a random order

The generator is thread-local and starts from a fixed default seed, so results are reproducible across runs unless you seed it with something varying (for example rand_seed(now() as integer)).

use random;
fn main() {

Basic Random Integers

rand(lo, hi) returns a uniformly distributed integer between lo and hi, both included. Seed first with rand_seed when you want a repeatable sequence.

  rand_seed(42);
  roll = rand(1, 6);       // a simulated die roll, 1..6
  assert(roll >= 1 && roll <= 6, "die roll out of range: {roll}");

Reproducibility

The same seed always produces the same sequence — essential for tests and for replaying a game from a saved seed.

  rand_seed(0);
  a = rand(0, 999);
  rand_seed(0);
  assert(rand(0, 999) == a, "the same seed must reproduce the sequence");

rand returns null when the range is empty (lo > hi), so you can detect a bad range with ! instead of getting a crash.

  assert(!rand(10, 5), "an inverted range returns null");

Random Ordering: rand_indices

rand_indices(n) returns a vector containing 0, 1, ..., n-1 in a random order. Use it to visit another collection in random order without copying the data.

  rand_seed(7);
  order = rand_indices(5);
  assert(len(order) == 5, "rand_indices(5) has five entries");

Every value 0..4 appears exactly once — it is a permutation, not a sample with repeats. We verify that by marking each position as seen.

  seen = [for _ in 0..5 { false }];
  for idx in order {
    seen[idx] = true
  }
  all_seen = true;
  for s in seen {
    if !s {
      all_seen = false
    }
  }
  assert(all_seen, "rand_indices must cover every position exactly once");

Sampling Without Replacement

Take the first k entries of a shuffled index vector to pick k distinct items.

  items = ["apple", "banana", "cherry", "date", "elderberry"];
  rand_seed(1);
  indices = rand_indices(len(items));

Note on null-flow: indexing a vector by a *variable* (here indices[i]) yields a NULLABLE element — text? — because the compiler cannot prove the index is in range. Appending a text? to the non-null picked would change its type, so we supply a fallback with ?? ""; the value is never actually null here.

  picked = "";
  for i in 0..3 {
    if i > 0 {
      picked += ", "
    }
    picked += items[indices[i]] ?? ""
  }

picked now holds 3 distinct fruit names in a random order.

  assert(len(picked) > 0, "should have picked some items: {picked}");

The same guard is needed for numbers — use ?? 0 when accumulating an integer that you read through a variable index.

  values = [10, 20, 30, 40, 50];
  total = 0;
  for i in order {
    total += values[i] ?? 0
  }
  assert(total == 150, "every value summed once: {total}");
}