Random is not random - Reaction game

Hello,
This is all very new to me and I am learning a lot. I am trying to make a reaction game, the game will drop items for the player to catch, the items need to be dropped in a random order at random intervals.
Via lots of reading, getting things wrong, pinching bits from other example programs from here and there, I have managed to make a program that gets pretty close.

To the best of my understanding, the program seems to have the functionality to randomly select the outputs and incorporates a random delays between each. (sometimes, as it is random it could select an output that's already dropped - so nothing happens - ideally it would be good for it to not repeat, but currently its not an issue and just adds to the randomness.)

BUT when I run the program in the simulator the LED's that visualise the release signal always light up in the same order with the same timing, there is nothing random about it.
8>2>3>1small pause >4>5>6 long pause >7. its this order and timing every time.

I haven't physically built anything yet so don't know if its just a quirk of the simulator and it would work in real life? (didn't want to spend money and time building the game till I knew the code worked)

There are probably many improvements that can be made, like how I flash the green LED after the start button is pressed but my main priority is to get the randomness working.

Can anyone provide any assistance as to why my random selections and timings just aren't random.

Thank you in advance for any help/advise/pointers you can give.

// put your setup code here, to run once:
// constants won't change. They're used here to set pin numbers:
const int StartPin = 2;   // the number of the pushbutton pin
const int CrazyPin = 4;   // Crazy mode - drop all
const int ReadyLED = 3;   // Get ready LED
const int GatePin1 = 5;   // drop gate 1
const int GatePin2 = 6;   // drop gate 2
const int GatePin3 = 7;   // drop gate 3
const int GatePin4 = 8;   // drop gate 4
const int GatePin5 = 9;   // drop gate 5
const int GatePin6 = 10;  // drop gate 6
const int GatePin7 = 11;  // drop gate 7
const int GatePin8 = 12;  // drop gate 8

// variables will change:
int buttonStateStart = 0;
int buttonStateCrazy = 0;

void setup() {
  // initialize the gate pins as outputs:
  pinMode(GatePin1, OUTPUT);
  pinMode(GatePin2, OUTPUT);
  pinMode(GatePin3, OUTPUT);
  pinMode(GatePin4, OUTPUT);
  pinMode(GatePin5, OUTPUT);
  pinMode(GatePin6, OUTPUT);
  pinMode(GatePin7, OUTPUT);
  pinMode(GatePin8, OUTPUT);
  pinMode(ReadyLED, OUTPUT);
  // initialize the pushbutton and reset pin as an input:
  pinMode(StartPin, INPUT);
  pinMode(CrazyPin, INPUT);
}

void loop() {
  // read the state of the pushbutton value:
  buttonStateStart = digitalRead(StartPin);
  buttonStateCrazy = digitalRead(CrazyPin);


  // checks for the Start button being pressed when in Crazy Mode
  if (buttonStateCrazy == HIGH and buttonStateStart == HIGH) {
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    digitalWrite(GatePin1, HIGH);
    digitalWrite(GatePin2, HIGH);
    digitalWrite(GatePin3, HIGH);
    digitalWrite(GatePin4, HIGH);
    digitalWrite(GatePin5, HIGH);
    digitalWrite(GatePin6, HIGH);
    digitalWrite(GatePin7, HIGH);
    digitalWrite(GatePin8, HIGH);
  }

  // check if the pushbutton is pressed. If it is, start the program:

  if (buttonStateCrazy == LOW and buttonStateStart == HIGH) {
    digitalWrite(ReadyLED, HIGH);  //flashing the LED to prep player and forms a delay after pressing start button
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
    digitalWrite(ReadyLED, HIGH);
    delay(500);
    digitalWrite(ReadyLED, LOW);
    delay(500);
start:
    int const ran = random(1, 9);
    int const ranDel = random(50, 200);

    if (ran == 1) {
      digitalWrite(GatePin1, HIGH);
      delay(ranDel);
      goto start;
    }

    if (ran == 2) {
      digitalWrite(GatePin2, HIGH);
      delay(ranDel);
      goto start;
    }

    if (ran == 3) {
      digitalWrite(GatePin3, HIGH);
      delay(ranDel);
      goto start;
    }

    if (ran == 4) {
      digitalWrite(GatePin4, HIGH);
      delay(ranDel);
      goto start;
    }

    if (ran == 5) {
      digitalWrite(GatePin5, HIGH);
      delay(ranDel);
      goto start;
    }

    if (ran == 6) {
      digitalWrite(GatePin6, HIGH);
      delay(ranDel);
      goto start;
    }

    if (ran == 7) {
      digitalWrite(GatePin7, HIGH);
      delay(ranDel);
      goto start;
    }
    if (ran == 8) {
      digitalWrite(GatePin8, HIGH);
      delay(ranDel);
      goto start;
    }
  }
}

PS - There is also a "Crazy mode" that will be a hidden switch that can drop all the items at the same time, just for laughs
PPS - There are 8 gates for dropping in the code/simulator but this might not be the final physical design I plan to decide this later when physically building it and will just adjust the code as appropriate

Add this to setup() function...

randomSeed(analogRead(unused_floating_analog_pin)); // for example, A0

*there is no true random in computers, only lava lamps.

3 Likes

That is by design, for debugging. The random number generator operates in a specific, but extremely long sequence that eventually repeats. You can start the sequence anywhere you want, using the seed method mentioned above.

Obtaining true random numbers on a computer requires an external source of guaranteed unpredictability.

1 Like

The reliable but not very sexy way to seed random - #53 by Coding_Badly is possibly the best you can get on an Uno.

You can read the full story.

1 Like

To me it's a question of being "random enough". That is, unpredictable by the user. Beyond that you can get into philsophy. "Does randomness really exist?" :wink:

I was doing some experiments once and I added a short wire (about 1-foot long) to the analog input to pick-up "random noise" for the seed. It did give a wider range of readings. But you might have to throw-away, or ignore, zero or maximum readings.

If there is user-input, aAnother "trick" is to run random() in a fast loop at the beginning of your program, maybe in setup(), and grab a random number, and exit the loop when a button is pressed. You can use that as a seed, or simply to random() to an unknown place in the sequence.

Or if you are making something like a slot machine that requires user action every time you need a random number, you can keep running the loop, reading random numbers continuously, and "grab" one when the user presses a button (or pulls the handle).

No... It's because there is no mathematical/algorithmic way to get a random number. Math is predictable and repeatable.

1 Like

... and imaginary!
sqrt(-1)

1 Like

Yes, there is, by consulting an external source of guaranteed unpredictable behavior. It is done all the time by the professionals. Arduino simply chose the easy way out, by design.

You can also just waste random numbers from the preordained list. I never use the seed or any of the other ways to get a random start.

void loop() {
  // read the state of the pushbutton value:
  buttonStateStart = digitalRead(StartPin);
  buttonStateCrazy = digitalRead(CrazyPin);

  static int whoCares;
  whoCares += random();

You can also grab random numbers whilst a button is down, or while no character is arrived you are waiting for and so forth.

In the above, I cannot be sure the optimiser won't just toss that as useless. Sry I cannot test it from where I am, if so I am sure there is a way to make it seem important enough to be left alone.

This is probably a bad idea in crypto applications. Here it matters less.

In any case, as has been pointed out, the fact that a reliable sequence is issued can help and should be easy to return to for testing.

That's neither maths nor algorithmic! The point @DVDdoug makes is that without such a source of real randomness, you can do all the regular computing you want and fila to get random numbers.

a7

1 Like

The approach I describe is algorithmic, well defined and in very common use.

Thank you Xfpd, I have added that and it has made an instant improvement to the system and it far more like what I want it to do, Its all very interesting how random wont give random without the seed.
I need to play with the random time ranges so its not like a machine gun or wating too long but that's easy enough to tinker with. I feel more confident to start making the thing in real life now,

Yes, its definitely more "unpredictable" than completely random for this purpose. I like the idea of the wire to introduce additional noise - a "random Antenna"!

Yes there will be a start button as user input then a short delay till the dropping starts 5 second. the randomseed has improved it to be what I wanted and unpredictable enough I can go ahead with building the thing, your other option may be a possible upgrade to the system

My comments apply to the concept of a computer, which takes an input and executes an algorithm to produce an output, not to a purely mathematical function or abstraction.

However, whether an abstract mathematical algorithm can produce a random sequence is an open question (for specific definitions of "random sequence").

A very interesting example is the string of digits representing PI, which is infinitely long and was recently computed out to about 62.8 trillion digits. People have checked and so far, all 10 decimal digits seem to occur at the same frequency, in random but of course well defined order.

Those of you who have read the excellent science fiction book "Contact" by Carl Sagan will recognize the potential importance of the question.

The random number generator is like a very specific shuffle of a 2^32 2^31-1 card deck, and seeding it with randomSeed(analogRead(unused_floating_analog_pin)) is like cutting the deck at 0-1023 from the top. There's only 1023 different sequences it will start with, less if the analog isn't wandering full scale. Calling random() fast repeatedly is like quickly peeling cards off of the top of the deck and putting the cards on the bottom.

Since you are doing a reaction game, you are getting user input at fairly random times. Old video arcade games used the timing of a "Hit any key to begin..." input to seed the RNG.

You can use the timing of your user inputs to re-cut the deck from the current state with:

srandom(random()+micros());

Seeding with random()+micros() rather than micros() or analogRead() starts from the current state of the deck rather than re-setting back to the initial shuffle.

A few of those could cut pretty deeply into the 4294967296 2147483647-number shuffled deck.

Edit to correct for 2^32 versus 2^31-1 error.

Here's the avr-libc routine that defines the shuffle of the deck:

static long
do_random(unsigned long *ctx)
{
	/*
	 * Compute x = (7^5 * x) mod (2^31 - 1)
	 * wihout overflowing 31 bits:
	 *      (2^31 - 1) = 127773 * (7^5) + 2836
	 * From "Random number generators: good ones are hard to find",
	 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
	 * October 1988, p. 1195.
	 */
	long hi, lo, x;

	x = *ctx;
	/* Can't be initialized with 0, so use another value. */
	if (x == 0)
		x = 123459876L;
	hi = x / 127773L;
	lo = x % 127773L;
	x = 16807L * lo - 2836L * hi;
	if (x < 0)
		x += 0x7fffffffL;
	return ((*ctx = x) % ((unsigned long)RANDOM_MAX + 1));
}

from:

avr-libc just moved over to github this year, so it is easier to find the source.

1 Like

Of course there is. All modern 64 bit processors include circuitry to emit a random string of bits. In some markets, that's a requirement.

Please don't. As has been repeatedly shown, proven, and mentioned using a floating pin is unreliable.

See post #4. The code is well tested and shown to work well.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.