3

How you I generate a random dice roll in Rust?

I know I can use rand::random, but that requires I want to generate a value of an integer type. Using rand::random<u8>() % 6 introduces a bias.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Chris Jefferson
  • 7,225
  • 11
  • 43
  • 66
  • 4
    `rand::thread_rng().gen_range(1,7)` might work. See this page on [Programming a Guessing Game](https://doc.rust-lang.org/book/2018-edition/ch02-00-guessing-game-tutorial.html). Make sure to put lines `extern crate rand;` and `use rand::Rng;` in your program. – Nathan Mills Sep 22 '18 at 08:17

2 Answers2

7

Use Rng::gen_range for a one-off value:

use rand::{self, Rng}; // 0.8.0

fn main() {
    let mut rng = rand::thread_rng();

    let die = rng.gen_range(1..=6);

    println!("The die was: {}", die);
}

Under the hood, this creates a Uniform struct. Create this struct yourself if you will be getting multiple random numbers:

use rand::{
    self,
    distributions::{Distribution, Uniform},
}; // 0.8.0

fn main() {
    let mut rng = rand::thread_rng();
    let die_range = Uniform::new_inclusive(1, 6);

    let die = die_range.sample(&mut rng);

    println!("{}", die);
}

Uniform does some precomputation to figure out how to map the complete range of random values to your desired range without introducing bias. It translates and resizes your original range to most closely match the range of the random number generator, discards any random numbers that fall outside this new range, then resizes and translates back to the original range.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • unfortunately this does not even compile – nikoss Jan 22 '19 at 17:46
  • no method named `gen_rng` found for type `rand::prelude::ThreadRng` in the current scope – nikoss Jan 22 '19 at 17:46
  • @nikoss it worked fine, even before I updated. Did you use the same version of the crate specified in the answer? Did you copy the **entire** example? Did you import the appropriate trait? Did you fully read the error message? For example, you [get that error if you don't import `Rng`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a324a847f0db68268d4ef6805a0090af) and it tells you how to fix it: *the following trait is implemented but not in scope, perhaps add a `use` for it: `use rand::Rng;`* – Shepmaster Jan 22 '19 at 17:51
3

You're correct that a bias is introduced; whenever you want to map from set A to set B where the cardinality of set B is not a factor or multiple of set A, you will have bias.

In your case, 42*6=252. So you can just throw away any u8 values of 252 or greater (and call random again).

Your output can then be safely mapped with the modulus operator. Finally add 1 to achieve the standard [1,6] dice output.

It might seem unclean to call random again but there is no way of mapping a set of 256 values to a set of 6 without introducing bias.

Edit: looks like the rand crate has something which takes bias into account: https://docs.rs/rand/latest/rand/distributions/uniform/struct.Uniform.html

lynks
  • 5,599
  • 6
  • 23
  • 42
  • Thanks. This is a correct answer, but I hoped that rust would have something to do this for me :) (looks like Uniform might do it) – Chris Jefferson Sep 22 '18 at 09:56