Arduino cube scrambler

Speedcubing is where competitors try to solve a rubik's cube in the fastest time possible. Every time a competitor wants to attempt an "official" solve, the cube needs to be scrambled using a randomly generated sequence of moves about 28. The standard notation for a 3x3 rubik's cube uses letters to indicate what face to turn. The letters are the following:

  • U - the top face
  • D - the bottom face
  • L - the left face
  • R - the right face
  • F - the front face
  • B - the back face

Sometimes, you might see a 2 or a " ' " symbol after a letter. The 2 means to turn the face 180 degrees and the ' symbol means to turn a face anti-clockwise. If the letter is just on it's own, then that means to turn the face clockwise once.

So a random scramble sequence might look like this:
U2 B2 D2 B2 F2 L2 B2 L' U2 L' F2 B' D2 L B2 U F L2 U L' D'

What I have been trying to do is to write a program for an Arduino to generate a random scramble sequence like the one above and then output it to the serial monitor (later, to an 0.96" OLED display instead). I have also tried to write my program so that it does not generate moves that contradict themselves either (for example: R, R2 or R L2 R'). However, when I run my program, the Arduino just spams F moves into the terminal (example: FFFFFFFFFF) and I cannot figure out why it is doing this after looking through my code many times. Here is the code
attached.


  int currentMove;
  int lastMove;
  int secondLastMove;
  int movesGenerated;
  int isSuccessful;
  int scrambleLength;
  int numericScramble[28];
  int translatedScramble[28];
  char *moves[] = {"F", "F'", "F2", "U", "U'", "U2", "L", "L'", "L2", "B", "B'", "B2", "D", "D'", "D2", "R", "R'", "R2"};
  int movesDisplayed;
  
 void setup() {
  /*
   * ###---ASSIGN NUMBERS TO MOVES:---###
   * f=1
   * f'=2
   * f2=3
   * u=4
   * u'=5
   * u2=6
   * l=7
   * l'=8
   * l2=9
   * b=10
   * b'=11
   * b2=12
   * d=13
   * d'=14
   * d2=15
   * r=16
   * r'=17
   * r2=18
   */
  Serial.begin(115200);
}

void loop() {
 generateScramble(28);
 translateAndDisplay();

}
void translateAndDisplay() {
  for (int movesTranslated = 0; movesTranslated <= scrambleLength;) {
//    Serial.print(moves[numericScramble[movesTranslated]]);
    Serial.print(numericScramble[movesTranslated]);4 
    movesTranslated++;
  }
}
void generateScramble(int scrambleLength) {
  for (int movesGenerated = 0; movesGenerated <= scrambleLength;) {
    pickMove();
    if (isSuccessful==1) {
      currentMove = numericScramble[movesGenerated];    
      movesGenerated++;
      lastMove = numericScramble[movesGenerated - 1];
      secondLastMove = numericScramble[movesGenerated - 2];
    }
    else {
      
    }
  }
}
void pickMove() {
  currentMove = random(1, 18);
    if(currentMove==1) { //Basically, the following code is a very clunky way of validating wether the next move can be added to the scramble or not:
      if (lastMove==1 || lastMove==2 || lastMove==3 || secondLastMove==1 || secondLastMove==2 || secondLastMove==3) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==2) {
      if (lastMove==1 || lastMove==2 || lastMove==3 || secondLastMove==1 || secondLastMove==2 || secondLastMove==3) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==3) {
      if (lastMove==1 || lastMove==2 || lastMove==3 || secondLastMove==1 || secondLastMove==2 || secondLastMove==3) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==4) {
      if (lastMove==4 || lastMove==5 || lastMove==6 || secondLastMove==4 || secondLastMove==5 || secondLastMove==6) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==5) {
      if (lastMove==4 || lastMove==5 || lastMove==6 || secondLastMove==4 || secondLastMove==5 || secondLastMove==6) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==6) {
      if (lastMove==4 || lastMove==5 || lastMove==6 || secondLastMove==4 || secondLastMove==5 || secondLastMove==6) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==7) {
      if (lastMove==7 || lastMove==8 || lastMove==9 || secondLastMove==7 || secondLastMove==8 || secondLastMove==9) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==8) {
      if (lastMove==7 || lastMove==8 || lastMove==9 || secondLastMove==7 || secondLastMove==8 || secondLastMove==9) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==9) {
      if (lastMove==7 || lastMove==8 || lastMove==9 || secondLastMove==7 || secondLastMove==8 || secondLastMove==9) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==10) {
      if (lastMove==10 || lastMove==11 || lastMove==12 || secondLastMove==10 || secondLastMove==11 || secondLastMove==12) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==11) {
      if (lastMove==10 || lastMove==11 || lastMove==12 || secondLastMove==10 || secondLastMove==11 || secondLastMove==12) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  } 
    if(currentMove==12) {
      if (lastMove==10 || lastMove==11 || lastMove==12 || secondLastMove==10 || secondLastMove==11 || secondLastMove==12) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==13) {
      if (lastMove==13 || lastMove==14 || lastMove==15 || secondLastMove==13 || secondLastMove==14 || secondLastMove==15) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==14) {
      if (lastMove==13 || lastMove==14 || lastMove==15 || secondLastMove==13 || secondLastMove==14 || secondLastMove==15) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==15) {
      if (lastMove==13 || lastMove==14 || lastMove==15 || secondLastMove==13 || secondLastMove==14 || secondLastMove==15) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==16) {
      if (lastMove==16 || lastMove==17 || lastMove==18 || secondLastMove==16 || secondLastMove==17 || secondLastMove==18) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==17) {
      if (lastMove==16 || lastMove==17 || lastMove==18 || secondLastMove==16 || secondLastMove==17 || secondLastMove==18) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
    if(currentMove==18) {
      if (lastMove==16 || lastMove==17 || lastMove==18 || secondLastMove==16 || secondLastMove==17 || secondLastMove==18) {
        isSuccessful = 0;
      }
      else {
        isSuccessful = 1;
      }
  }
}

https://pastebin.com/v9FY4Cpt

Please post the code in line, using code tags. Most users won't bother to download code from elsewhere.

Fixed. :slight_smile:

It is very hard to do it the way you are trying. Give up now.

Yes, you can catch a few obviously stupid things like F followed by F’ or U2 followed by U2, but it would be hard to catch, say L R U U’ R’ L’.

You get the idea.

Since God’s number is 21, we want published scrambles to be no longer of comparable lenth.

So what to do…

Take a virtual cube and apply, say, 10000 random selected moves, without prejudice. There will be the kind of things you are trying to avoid, but who cares, after so many moves you’ll have a well scrambled cube.

Then solve the virtual cube with, say, Kociemba’s algorithm. I forget whether it will be optimal, but it will be short, and provide a good scramble recipe.

I haven’t tried to fit any solver onto a microprocessor. Lego solving robots fit onto the Mindstorm controller, but they are clumsy and take like 60 moves, far too many for serious use.

High speed robots either are, or involve, a “real” computer to generate the list of moves after being provided the scrambled cube as input data.

a7

In this routine I see at least three places where you are likely to be violating array bounds, and not a single check.

I'm not sure I fully understand your answer, but I do not mean to create something that scrambles or solves a physical cube, I just want to generate a random scramble algorithm that I can carry out with my own hands.

I understand and have described as well as I can possible how to do that.

Scramble a virtual cube. You need a data structure representing the cube. Apply random moves to it. A crap ton of them.

Solve the cube. You need to write or steal a solver. I suggest Kociemba because it has been implemented widely.

Apply the solution K comes up with to a real, unscrambled cube. It then be scrambled.

Otherwise you isn’t just as well hand it to your sister and let her mess it up for you. :wink:

a7

In this routine I see at least three places where you are likely to be violating array bounds, and not a single check.

Sorry I am not very experienced with C++ , could you expand a bit more on your answer, maybe show an example?

Never mind, sorry, I see you are asking about your coding errors that @jremington spotted.

no

a7

Sorry, I just realized too that I should've used block quotes.

Well I am wrong, or maybe less than right.

Here is some interesting stuff

if it’s good enough for them…

And this is plausible, the maths make sense - it twists individual cubies randomly, but not so as to move the cube state into unsolvable groups, see #5

That might fit on an Arduino, it's tired and I'm getting late, so not for me to sat just now.

a7

There are three axes: UD, LR, and FB. As long as you cycle among the three I don't think you can generate a move that will undo a previous move.

  Repeat 10 times to get 30 moves:
  {
   On the UD axis, randomly pick U or D.
   Randomly pick 90°, 180°, or -90°.

   On the LR axis, randomly pick L or R.
   Randomly pick 90°, 180°, or -90°.

   On the FB axis, randomly pick F or B.
   Randomly pick 90°, 180°, or -90°.
  }
1 Like

I rewrote my code and used this concept instead:

There are three axes: UD, LR, and FB. As long as you cycle among the three I don't think you can generate a move that will undo a previous move.

  Repeat 10 times to get 30 moves:
  {
   On the UD axis, randomly pick U or D.
   Randomly pick 90°, 180°, or -90°.

   On the LR axis, randomly pick L or R.
   Randomly pick 90°, 180°, or -90°.

   On the FB axis, randomly pick F or B.
   Randomly pick 90°, 180°, or -90°.
  }

I now have a working prototype and it runs beautifully! it requires a slightly longer than the average scramble (4-6 moves extra), but don't mind that. I will post the code here for anyone interested soon after I finish polishing it up. Thanks johnwasser!

You said "a randomly generated sequence of moves about 28". If 30 moves is "4-6 moves extra" did you mean to say: "a randomly generated sequence of moves about 25"?

You can get 27 moves by repeating 9 times.
You can get 24 moves by repeating 8 times.

To do what, by what criteria?

I submit that doing it your way would need many more moves in order to scramble a cube.

a7

First of all, when I said

but don't mind that.

I meant to write "But I don't mind that" so sorry if I sounded really passive-aggressive lol.

And yes, my mistake, that is what I should've said:

I didn't use my way, I used the way suggested by johnwasser.

Here is the code for anyone who is interested (Everything runs once in setup() for now - this is intended to be moved down to loop() or it's own seperate function when later used with appropriate hardware):

 

int UandD;
int FandB;
int RandL;
const int scrambleLength = 28;
char *generatedScramble[scrambleLength];
int numericScramble[scrambleLength];
char *threebythreemoves[] = {"U","U'","U2","D","D'","D2","F","F'","F2","B","B'","B2","R","R'","R2","L","L'","L2"};
int movesGenerated;
int movesTranslated;
int movesDisplayed;
void setup() {
  Serial.begin(9600);
  Serial.println("____Start____");

  for(movesGenerated==0; movesGenerated <= scrambleLength;) {
    randomSeed(analogRead(A0));
    UandD = random(0,6);
    Serial.print("U or D = ");
    Serial.println(UandD);
    numericScramble[movesGenerated] = UandD;
    movesGenerated++;

    randomSeed(analogRead(A0));
    FandB = random(6,12);
    Serial.print("F or B = ");
    Serial.println(FandB);
    numericScramble[movesGenerated] = FandB;
    movesGenerated++;

    randomSeed(analogRead(A0));
    RandL = random(12,18);
    Serial.print("R or L = ");
    Serial.println(RandL);
    numericScramble[movesGenerated] = RandL;
    movesGenerated++;
  }

  Serial.println("Generated scramble is:");
  for(movesTranslated==0; movesTranslated <= scrambleLength; movesTranslated++) {
    generatedScramble[movesTranslated] = threebythreemoves[numericScramble[movesTranslated]];
  }
  for(movesDisplayed==0; movesDisplayed <= scrambleLength; movesDisplayed++) {
    Serial.print(generatedScramble[movesDisplayed]);
    Serial.print(" ");
  }
  Serial.print("\n");

  Serial.println("____End____");
}

void loop() {

}

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