0

I'm using the random() to generate random coordinates for coins in a simple game console. The problem here is that the random() function is not generating random numbers, since the coin is at the same coordinates every single time I upload it. I am using the Adafruit GFX library with the Adafruit 1.44" Color TFT LCD Display. Here is my code (sorry, it's kind of long).

//necessary libraries
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>

//colors
#define BLACK    0x0000
#define BLUE     0x001F
#define RED      0xF800
#define GREEN    0x07E0
#define CYAN     0x07FF
#define MAGENTA  0xF81F
#define YELLOW   0xFFE0 
#define WHITE    0xFFFF

//pins
#define TFT_CS  10
#define TFT_RST  9
#define TFT_DC   8

//joystick variables
const int VRxPin = A1;
const int VRyPin = A0;
const int SWPin  = 7;
bool mov = false;

//data read from joystick pins
int x = 0;
int y = 0;
int SW = 0;

// X and Y coords for entities
int playerx = 50;
int playery = 50;

long coinx;
long coiny;

//game variables
int score = 0;

//defining the tft class
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

//useful text function
void output(char *text, int x, int y, uint16_t color, bool wrap = 0){
  tft.setCursor(x, y);
  tft.setTextColor(color);
  tft.setTextWrap(wrap);
  tft.print(text);
}

//there is probably a better way to do this but im too lazy to find it
void num_output(int text, int x, int y, uint16_t color, bool wrap = 0){
  tft.setCursor(x, y);
  tft.setTextColor(color);
  tft.setTextWrap(wrap);
  tft.print(text);
}

//clears screen
void clear() {
  tft.fillScreen(BLACK);
}

//setup (lcd init & startup screen)
void setup() {
  Serial.begin(9600);
  pinMode(SWPin,INPUT_PULLUP);
  tft.initR(INITR_GREENTAB);
  
  long coinx = random(20, 100);
  long coiny = random(20, 100);
  
  clear();
  output("RAMBUTAN", 40, 60, RED);
  delay(1000);
  clear();
  tft.fillRect(playerx, playery, 5, 5, WHITE);
  tft.fillCircle(coinx, coiny, 5, YELLOW);
}

void loop() {
  //reads data from joystick pins every single tick
  int VRx = analogRead(VRxPin);
  int VRy = analogRead(VRyPin);
  int SW = digitalRead(SWPin);
  
  //converts joystick pin data into directions
  if (VRx > 250 && VRx < 750 && VRy > 250 && VRy < 750) {
    // middle
  } else if (VRx > 511.5 && VRy < 750 && VRy > 240) {
    // right
    playery -= 5;
    mov = true;
  } else if (VRx < 511.5 && VRy < 750 && VRy > 240) {
    // left
    playery += 5;
    mov = true;
  } else if (VRy > 511.5 && VRx < 750 && VRx > 240) {
    // up
    playerx += 5;
    mov = true;
  } else if (VRy < 511.5 && VRx < 750 && VRx > 240) {
    // down
    playerx -= 5;
    mov = true;
  } else {
    mov = false;
  }

  if (mov == true) {
    clear();
    tft.fillRect(playerx, playery, 5, 5, WHITE);
    num_output(score, 10, 10, WHITE);
    tft.fillCircle(coinx, coiny, 5, YELLOW);
    mov = false;
  }
  delay(100);
}
4
  • Have you read the documentation to the Arduino random() function? Especially the section "Notes and Warnings"? A microcontroller cannot easily generate random numbers without a good source of randomness.
    – chrisl
    Feb 18 at 20:58
  • Most pseudo-random libraries (like the one in python) at least seems to be random, so I thought I could just use this :/
    – jort57
    Feb 18 at 21:00
  • The random libraries on a PC will also pull randomness/entropy from some sure. On a PC there are many factors, which could be used for that. On a microcontroller the resources are much more limited. Here you need to dedicate an analog input for that (which might or might not be acceptable for you).
    – chrisl
    Feb 18 at 22:31
  • 5
    Does this answer your question? Getting a truly random number in Arduino Feb 18 at 23:09
1

You are correct, random() isn't random - it's pseudo random. It returns successive results from a mathematical formula which, to all intents and purposes, is random. It's also predictable, as you have seen.

The formula has a starting value, called the seed, which you can choose your self. Changing that seed gives you a different sequence of random-like numbers.

So the trick is to set the seed to something that is truly random. One common source of randomness, or entropy, is an unconnected ADC input:

randomSeed(analogRead(A3));

You can read more about the randomSeed() function here.

7
  • This seems to work better, but when I move, it automatically goes to the corner like it did before... I'm not sure what's happening.
    – jort57
    Feb 18 at 21:07
  • @jort57 You aren't calling randomSeed() over and over again are you? You only want to call it once in setup().
    – Majenko
    Feb 18 at 21:15
  • 2
    randomSeed(analogRead(A3)); is better than nothing but, in my experience, it has very little entropy (a couple of bits or so). Unless of course you connect a hardware entropy source to A3. Feb 18 at 23:11
  • 1
    A trick that has worked for many of my projects requiring randomness is to include some sort of a "start" trigger. This could be a network communication response, a push button (e.g, a "go" button), or some other event that will occur at a random point in time. After observing the "start" trigger, call millis() and use the return value as the random seed. You could probably also use micros() in place of millis() if the start trigger may occur quickly (e.g. a local network communication).
    – GMc
    Feb 19 at 2:02
  • .... or perform analogRead() a few times in sequence and join the last few bits of each call.
    – PMF
    Feb 19 at 6:13
0

As an alternative to Majenko's answer, this is how I generate random n-bit-numbers: for each of the n bits I read the chip temperature (which is pretty noisy), take the least significant bit of the reading and assign it to the corresponding bit in the number. The results are sufficiently random for my purpose at least.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.