Skip to content
Kavachकवच

Random Numbers

● Intermediate3 minBefore this: FunctionsSpec 14.15

std.random gives random whole numbers, picks and shuffles. Its numbers are safe for passwords and keys, unless a block asks for repeatable ones from a seed.

use std.random

fn roll() -> Int uses Random {
    random.int(1, 7)
}

fn main() {
    let rolls: [Int] = []
    for _ in 0..1_000 {
        rolls.push(roll())
    }
    print("every roll from 1 to 6: {rolls.all(r => 1 <= r <= 6)}")
    print("all six faces came up: {[1, 2, 3, 4, 5, 6].all(face => rolls.contains(face))}")
}

The program ran and printed:

kavach run dice.kav

every roll from 1 to 6: trueall six faces came up: true
dice.kav: Kavach code, then what running it prints

random.int(1, 7) gives 1 to 6: the end is left out, as in 1..7. The program checks its own rolls, because random output can't be written down in advance.

use std.random

fn main() {
    const names = ["Asha", "Omar", "Zoe", "Ravi"]
    const winner = random.pick(names) ?? "nobody"
    print("the winner is one of them: {names.contains(winner)}")
    const order = random.shuffle(names)
    print("same names, new order: {order.sort() == names.sort()}")
    print("a token of 16 bytes: {random.bytes(16).len()}")
}

The program ran and printed:

kavach run pick.kav

the winner is one of them: truesame names, new order: truea token of 16 bytes: 16
pick.kav: Kavach code, then what running it prints
FunctionGives
random.int(from, until) -> IntA whole number from from up to, not including, until; each equally likely. until must be greater than from (a require)
random.float() -> FloatA number from 0.0 up to, not including, 1.0
random.bool() -> Booltrue or false, each half the time
random.pick<T>(items) -> T?One of items, each equally likely; none when there are none
random.shuffle<T>(items) -> [T]A copy of items in a random order, every order equally likely
random.bytes(n) -> Bytesn random bytes. n is from 0 to 65,536 (a require), as in browsers' crypto.getRandomValues; for more, call it again

Every function needs uses Random. Under kavach test, random numbers come from the test run's seed instead, so a failing run can be repeated exactly.

Seeded

random.seeded(seed) { … } runs its block with every std.random function drawing from seed instead of the system. The same seed gives the same numbers, on every machine and in every version of Kavach: for simulations, generated worlds and games whose players must all see the same thing.

use std.random

fn world(seed: Int) -> [Int] uses Random {
    random.seeded(seed) {
        [random.int(0, 100), random.int(0, 100), random.int(0, 100)]
    }
}

fn main() uses Random {
    print(world(7))
    print(world(7))
    print(world(8))
    print(world(7) == world(7))
}

The program ran and printed:

kavach run seeded.kav

[86, 74, 30][86, 74, 30][91, 42, 50]true
seeded.kav: Kavach code, then what running it prints
  • These numbers are not safe for keys, tokens or passwords: anyone who knows the seed knows the numbers.
  • uses Random is still needed. Under kavach test, the seed wins over the test run's.
  • Seeds nest: an inner seeded block draws from its own seed, and the outer one goes on where it left off.
  • The seed belongs to the task running the block. A task spawned inside it draws from the system, since tasks may take turns in different orders on different machines.
  • The numbers come from xoshiro256**, its state made from the seed by SplitMix64, as their published versions make it.

Limits and sizes

WhatLimit or sizeSource
random.bytes(n)n from 0 to 65,536 per call15-random.md

Every limit in Kavach, on one page: Limits and Sizes.