Learn

Random numbers on an Arduino, with a dice to roll

Intermediate

Or see every lesson.

  • 455parts on the bench
  • 43boards running now
  • 1.00xreal time, on every board
Arduino Uno: an electronic dielive0.000 s 0.00x
Press Run, then press the button
Press the button to roll. random(1, 7) picks the face, and the tumble slows down before it lands.

random(min, max) returns a whole number from min up to, but not including, max. So a six-sided die is random(1, 7), which gives 1 to 6, and a coin toss is random(2), which gives 0 or 1. Call randomSeed() once in setup() with something unpredictable, or the numbers come out in the same order every time the board starts.

The circuit above is an electronic die: a button on pin 9 and a seven-segment display on an Uno. Press Run, then press the button. The display tumbles through faces, slows down and stops, and the serial monitor prints what you rolled. Our first roll after Run was a 3, then a 5, then a 3 and a 5 again.

Press Run again and roll: 3, then 5, then 3, then 5. The die does not call randomSeed(), so every run starts the same list. Below is why, what to seed it with, and what that means for games.

random(max) and random(min, max)

With one number, random(10) gives 0 to 9. With two, random(5, 10) gives 5 to 9. The top number is never returned, which is the one thing to remember: a die is random(1, 7), not random(1, 6), and random(1, 6) would never roll a six. If min is not less than max there is nothing to choose from, and random(5, 5) simply returned 5.

Over 6000 rolls of random(1, 7) the board counted 980 ones, 1009 twos, 968 threes, 1069 fours, 1000 fives and 974 sixes. A fair die would average 1000 of each, and real dice wander about that much too.

firmware/uno/examples/dice (the roll, from the sketch running above)
  int face = 1;
  unsigned long step = 40;
  for (int i = 0; i < 14; i++) {
    face = (int)random(1, 7);
    show(DIGITS[face]);
    delay(step);
    step += step / 4 + 5;
  }

  Serial.print("rolled ");
  Serial.println(face);

Why the same numbers come out every time

A microcontroller cannot roll a die. random() is a pseudo-random generator: a formula that takes one number and makes the next, so the list looks random but is fixed by where it starts. After a reset it always starts in the same place. Ten calls to random(1, 7) printed "4 2 4 6 6 5 5 1 6 5", and the same ten on every run.

A real Uno does the same thing, with different numbers. The Arduino IDE’s random() uses the generator in avr-libc, and Mokxi’s runtime uses a different one (xorshift32), so the same sketch rolls a different list on your desk from the one it rolls here. What carries over is the behavior: no seed, same list, every reset.

randomSeed(n) picks the starting point. randomSeed(42) followed by ten rolls printed "1 5 4 3 1 1 1 5 2 6", and seeding with 42 again printed exactly the same ten. That is useful while you test: a fixed seed makes a game repeatable, so a bug that happens on the fifth roll happens on the fifth roll every time.

Seed it with something nobody can predict

The seed has to be different every time the board starts. The usual advice is randomSeed(analogRead(A0)) with A0 connected to nothing, so the pin picks up electrical noise. On a real board that gives a different seed most of the time, though the noise is weak and the readings bunch together.

In Mokxi it does not help, and it is worth knowing why. An unconnected pin here reads noise that is worked out from the board, the pin and the time, so a run repeats exactly: A0 read 324 at the start of every run, and the rolls that followed were the same every time.

Seed from a person instead. The moment someone presses a button, counted in microseconds, is different every time, on a desk or in a browser. The sketch below waits for the first press and seeds from micros(). Pressed at 700 ms it rolled "6 1 6 1 3 1"; pressed at 1300 ms it rolled "4 6 6 3 5 6". Add the same two lines to the die above, before its first roll, and it stops repeating itself.

Seed from the first press
void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  while (digitalRead(2) == HIGH) {
  }
  randomSeed(micros());
  for (int i = 0; i < 6; i++) {
    Serial.print(random(1, 7));
    Serial.print(' ');
  }
  Serial.println();
}

void loop() {
}

Pick from a list: a magic eight ball

random() is most fun as an index. Keep the answers in an array, pick a number from 0 to one less than the count, and use it to choose. The sketch below printed "Not a chance", "Not a chance", "No", "Yes" and "Definitely". Notice the repeat: random means any answer can come up twice in a row. If your game must not repeat, remember the last pick and roll again when it matches.

The same idea picks a random LED to light in whack-a-mole, a random delay before a reaction test begins with random(700, 2000), or a random note in a melody.

A magic eight ball
void setup() {
  Serial.begin(9600);
  const char *answers[] = {"Yes", "No", "Ask again later", "Definitely", "Not a chance"};
  const int COUNT = sizeof(answers) / sizeof(answers[0]);
  for (int i = 0; i < 5; i++) {
    Serial.println(answers[random(0, COUNT)]);
  }
}

void loop() {
}

Questions

Does Arduino random() include the maximum?

No. random(1, 7) returns 1 to 6 and random(10) returns 0 to 9. Add one to the top of the range you want: a number between 1 and 10 is random(1, 11), and 0 to 255 is random(256).

Why does my Arduino give the same random numbers every time?

Because random() starts from the same seed after every reset. Call randomSeed() once in setup() with a value that changes, such as micros() at the moment of the first button press.

Is randomSeed(analogRead(A0)) good enough?

On a real board it is usually enough for a game, though the readings of an unconnected pin bunch together. In Mokxi an unconnected pin repeats from run to run, so seed from the time of a button press instead. Neither is good enough for passwords or anything secret.

Will my sketch roll the same numbers in Mokxi as on a real Uno?

No. Mokxi’s random() is a different generator from the Arduino IDE’s, so the lists differ. Both repeat after a reset without a seed, both obey randomSeed(), and the range rules are the same.

How do I get a random decimal number?

Divide a random whole number: random(0, 1001) / 1000.0 gives 0.000 to 1.000 in steps of a thousandth. Write 1000.0, not 1000, or the division throws the fraction away.

How do I make a random delay on Arduino?

`delay(random(500, 1500));` waits somewhere from 500 to 1499 ms. With no seed, an Uno here printed 869, 1189, 961, 1195 and 733 for its first five calls of random(500, 1500), and the same five after every reset, so call randomSeed() in setup() first.

Sources

Where the facts on this page come from

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.