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}");
}