I’m trying a very simple “roll a die” example. In C stdlib, this would entail usingsrand()and rand(). In modern C++, there are more sophisticated features, such as std::random_device which provides an entropy()method, std::default_random_engineas well as various other engines, and std::uniform_int_distributionas well as different variants.
My search for Zig examples turned up this very simple one: Zig Cookbook, but it doesn’t cover seeding. I found std.random.int in the std library docs but it doesn’t seem to explain the “underlying magic”. I also found this more extensive example but it’s against an earlier release (I’m using 0.15.2). After fixing it, it looks like this:
BTW, I had to guess on how to do the above because if you click on DefaultPrngit takes you to Xoshiro256and there are no descriptions in there.
Zig doesn’t like that last line, giving the error error: expected type ‘u8’, found ‘u64’with the note unsigned 8-bit int cannot represent all possible unsigned 64-bit values. What bothers me is that, yes, random_numberis u64, but by the mathematical definition of modulus division, any number modulo 6 can only give an integer between 0 and 5, which definitely fits in a u8, so why should I have to add an additional cast? Am I missing something?
Side note: My original title was “PRNGs” but that was not acceptable because it’s less than 10 characters. I’m left wondering if that is a local limitation or a software “feature” and what purpose does it serve?
This recent thread covers some non-cryptographic rng stuff, including using the (juicy) init.io.random for the “easiest” way forward the likes of die-rolling. You might find some help there.
Yes, it looks like next() returns a u64; even though you %6 that value, you could add a million to it, instead of just 1. Obviously you don’t, and you might think the compiler should see that the result can fit into 8 bits, but, alas, you’ll have to @truncate that result. Since you’re sure that won’t modify your value, you can do it safely. This expresses your intent clearly, rather than leaving it as “implied”, anyway.
I didn’t mention it before, but the error is given even if nothing is added, i.e.,
const die: u8 = random_number % 6;
still gives the error with the same error and note. It’s not a big deal, but it seems the compiler would be better if, when processing the % operator it would take into account the size of the divisor rather than invoking “peer type resolution”. In any case, I used an @intCastrather than a @truncatesince the former looks more appropritate.
The Random type is the “interface”/vtable. @pzittlau’s post instantiates a concrete implementation of that “interface” of type DefaultPrng as a variable called prng. The .random method is on DefaultPrng. I put “interface” in quotes, because Zig doesn’t really have that concept at a language level. Instead, for dynamic dispatch, a struct full of function pointers (a manually created vtable) is often used (usually with convenience wrapper methods).
The .random() call returns the vtable (of type std.Random) associated with the concrete instantiation of DefaultPrng (the variable prng).