[SOLVED] Shuffle a char array?

Hi there!

I have an array:

char states[4] = {'Null','Win','Lose','Accel'};

I need these four states to be shuffled every time I reset the code. Maybe I use integers instead (e.g. 1-4?)

Any help would be appreciated!

Can't you just use random instead? Like so:

states[ random(4) ]

First of all, you can’t assign a string to a single character.

You can use the millis() function to give “random” numbers, than mod it by 4, since there are 4 elements. Up until now, I didn’t succeed on using the micros() function (I don’t know why), which would have been better, since it changes it’s value faster, and thus helps on the “randomness”.

Here’s the code.

char states[4][6] = {"Null","Win","Lose","Accel"};

void shuffle()
{
  int new_order[4],k=0;
  
  for(int i=0; i<4; i++)
  {
    int new_position = millis()%4;
    for(int j=0; j<k; j++)
    {
      if(new_position==new_order[j])
      {
        new_position = millis()%4;
        j = -1;
      }
    }
    new_order[k] = new_position;
    k++;
  }
  
  for(int i=0; i<4; i++)
  {
    for(int j=0; j<6; j++)
    {
      char aux = states[new_order[i]][j];
      states[new_order[i]][j] = states[i][j];
      states[i][j] = aux;
    }
  }
  
  for(int i=0; i<4; i++)
  {
    Serial.print(states[i]);
    Serial.print(" ");
  }
  Serial.println();
}

At the end I put some prints for debugging.
At every new start up, there should be a different order between the 4 strings.

Good luck

guix: Can't you just use random instead? Like so:

states[ random(4) ]

Like this?

int states[random(4)];

That didn't work for me.

RobertEagle:
First of all, you can’t assign a string to a single character.

You can use the millis() function to give “random” numbers, than mod it by 4, since there are 4 elements. Up until now, I didn’t succeed on using the micros() function (I don’t know why), which would have been better, since it changes it’s value faster, and thus helps on the “randomness”.

Here’s the code.

char states[4][6] = {"Null","Win","Lose","Accel"};

void shuffle()
{
  int new_order[4],k=0;
 
  for(int i=0; i<4; i++)
  {
    int new_position = millis()%4;
    for(int j=0; j<k; j++)
    {
      if(new_position==new_order[j])
      {
        new_position = millis()%4;
        j = -1;
      }
    }
    new_order[k] = new_position;
    k++;
  }
 
  for(int i=0; i<4; i++)
  {
    for(int j=0; j<6; j++)
    {
      char aux = states[new_order[i]][j];
      states[new_order[i]][j] = states[i][j];
      states[i][j] = aux;
    }
  }
 
  for(int i=0; i<4; i++)
  {
    Serial.print(states[i]);
    Serial.print(" ");
  }
  Serial.println();
}




At the end I put some prints for debugging.
At every new start up, there should be a different order between the 4 strings.

Good luck

When I tried that (putting the shuffle() function in the setup and pressing the Arduino reset button), it kept repeating the same 4 values. They weren’t random.

Thanks for the replies!

I don't know why it's repeating the values. Mine doesn't. I have a Leonardo.

Also consider that the values stay the same for the rest of the run.

Do you get different sequences after each reset?

Like this:

char *states[] = { "Null", "Win", "Lose", "Accel" };

void setup()
{
    Serial.begin( 9600 );
    Serial.print( states[ random(4) ] );
}

Live example: http://codepad.org/2ji3b66O (C doesn't have a native random(max) function so it's not exactly the same code)

RobertEagle: I don't know why it's repeating the values. Mine doesn't. I have a Leonardo.

Also consider that the values stay the same for the rest of the run.

Do you get different sequences after each reset?

I have a Mega 2560.

I just pressed the on-board reset button on the Arduino. Every time, the values were the same.

Strange...

guix: Like this:

char *states[] = { "Null", "Win", "Lose", "Accel" };

void setup() {    Serial.begin( 9600 );    Serial.print( states[ random(4) ] ); }




Live example: http://codepad.org/9XkJ4GuL (C doesn't have a native random(max) function so it's not exactly the same code)

Ahhh...I see what you mean. The only problem is that it only outputs ONE value (and not all 4).

I'm wondering if numbers (instead of strings) would be easier to work with?

Hmm replace my millis()%4 with random(4) and see what you get. I'm curious.

RobertEagle: Hmm replace my millis()%4 with random(4) and see what you get. I'm curious.

Nope :-/. Still outputs "Null Win Lose Accel" (same as initial variable value).

Sorry , forgot to say that you need to add a randomSeed just before calling random().

guix:
Sorry , forgot to say that you need to add a randomSeed just before calling random().

Bingo! Thanks! Worked like a charm!

Thanks! This will work for my needs.

RobertEagle: Hmm replace my millis()%4 with random(4) and see what you get. I'm curious.

millis() returns milliseconds since startup. The same code will take the same number of ms to reach the millis()%4 every time unless tied to an external event.

If at startup the user is required to press a switch of some kind (a led and a resistor can be a switch) then you will get random time. I would use micros() bits 2 and 3 rather than millis() but that's just me.

GoForSmoke:

RobertEagle: Hmm replace my millis()%4 with random(4) and see what you get. I'm curious.

millis() returns milliseconds since startup. The same code will take the same number of ms to reach the millis()%4 every time unless tied to an external event.

If at startup the user is required to press a switch of some kind (a led and a resistor can be a switch) then you will get random time. I would use micros() bits 2 and 3 rather than millis() but that's just me.

I understand what are you telling. I've also tried using micros() but the program would hang out. I do not understand why it does this.

Another solution would have been to backtrack, and select the sequence found at a specific index incrementally. Say we backtrack with a n=4, thus having 24 possible sequences. At boot up no=0x01 we select the first sequence our backtracking algorithm gives us. At boot up no=0x02 we select the second sequence our backtracking algorithm gives us. And so on for the next 22 sequences. When it reaches the end, it resets itself.

It is at the same time both predictable and random, plus it doesn't use much processing power nor ram memory (just 3 bytes for the algorithm, excluding the states array)

Can you make it happen in a very simple test sketch?

Micros() should run where millis() will. I think that even in an IRQ micros() runs but test that before using!

Micros() has a granularity of 4 which is why I would ...

byte timeRandom; .... timeRandom = ( micros() / 4 ) & 3;

I think that should run. Hold on.... yes for me. Here is a tested example using Arduino and a male-male jumper.

void setup( void )
{
  pinMode( 2, INPUT_PULLUP );
  
  Serial.begin( 115200 );
  Serial.println( "\n ground pin 2 to get randoms\n" );
}

void loop( void )
{
  static byte readTime, readHistory;
  
  readHistory = ( readHistory * 2 ) & 3;
  readHistory += digitalRead( 2 ); // now has 2 reads as 0 to 3
  
  if ( readHistory == 1 )
  {
    readTime = (byte) (( micros() & 15 ) / 4 );
    Serial.println( readTime ); 
  }
}

PS, for purity's sake it should probably be different:

static byte readTime, readHistory = 1;

if ( readHistory == 2 )

I think implementing a backtracking algorithm is not necessary.

In this situation there are only 24 permutations, therefore only 96 bytes needed to be occupied. I put them not in RAM, but in the Flash. It’s more efficient this way.

I don’t have to reset myself the EEPROM value. It automatically resets itself, right? Ones it reaches it’s maximum high value, then it goes back to 0. My only job is to increment at every boot up the value.

#include <avr/pgmspace.h>
#include <EEPROM.h>

uint8_t address = 0;
uint8_t option;
PROGMEM const String states[4] = {"Null","Win","Lose","Accel"};
PROGMEM const uint8_t SEQUENCE[24][4] = {{0,1,2,3},{0,1,3,2},{0,2,1,3},{0,2,3,1},{0,3,1,2},{0,3,2,1},
                                         {1,0,2,3},{1,0,3,2},{1,2,0,3},{1,2,3,0},{1,3,0,2},{1,3,2,0},
                                         {2,0,1,3},{2,0,3,1},{2,1,0,3},{2,1,3,0},{2,3,0,1},{2,3,1,0},
                                         {3,0,1,2},{3,0,2,1},{3,1,0,2},{3,1,2,0},{3,2,0,1},{3,2,1,0}};
uint8_t shuffle()
{
  uint8_t val = EEPROM.read(address);
  EEPROM.write(address,val+1);
  return val%24;
}

void setup()
{
  option = shuffle();
  Serial.begin(9600);
  
  for(uint8_t i=0; i<4; i++)
  {
    Serial.print(states[SEQUENCE[option][i]]);
    Serial.print(" ");
  }
  Serial.println();
}
void loop()
{
  
}

What I don’t understand is why this code doesn’t work. It’s properly written. The arduino does nothing when it runs. No logic whatsoever. But maybe this situation is of the same kind as the other ones. My Arduino seems to be burned, the government decided I have to pay another 700$ taxes for my car (retroactive measure), and now this code doesn’t work. Great day. Swell :stuck_out_tongue_closed_eyes:

    Serial.print(states[SEQUENCE[option][i]]);

You have to access SEQUENCE through pgm_read_byte( ). I wouldn't do it inside of Serial.print() but that's just me.

So you tell I have to do this?

Serial.print(pgm_read_byte(4*sizeof(String)+option*24+i));

Oh geez I hadn't noticed before -- get rid of the wasteful frikking String objects! Use C string arrays!

You can access PROGMEM data by address + offset and size and you can access by indexes too. The important part is that addresses in PROGMEM have to use functions that address PROGMEM.

AVR SRAM, flash and EEPROM are separate devices each with its own space. There are other MCU's like IIRC the SAM in the Due that address all memory as a single space.

Sorry but I haven't done the indexes way enough, I have to go to my examples to say "oh yeah" on the syntax.