• Welcome to the Speedsolving.com, home of the web's largest puzzle community!
    You are currently viewing our forum as a guest which gives you limited access to join discussions and access our other features.

    Registration is fast, simple and absolutely free so please, join our community of 35,000+ people from around the world today!

    If you are already a member, simply login to hide this message and begin participating in the community!

Simple PHP scrambler

mrCage

Member
Joined
Jun 17, 2006
Messages
654
Hi :)

As part of my new FMC website i wrote a PHP scrambler, so that i can generate scrambles automatically and insert to the database, one scramble for each week. I do not prefill the database with scrambles. I just generate new ones when the current last scramble is expired on accessing the site. Anyhow i just wanted to show the code for the scrambler - not the complete website :p

Code:
function makeScramble($length)
 {
  /*
  $faces = array('U','D,'F','B','R','L')
  $taken = array(0,0,0,0,0,0,0);
  $numturns = array(1,1,1,1,1,1,1,2,2);
  $axis = array(1,1,1,2,2,3,3);
  $modifiers = array('',','\'','2');
  */
  $faces[1] = 'U';
  $faces[2] = 'D';
  $faces[3] = 'F';
  $faces[4] = 'B';
  $faces[5] = 'R';
  $faces[6] = 'L';
  $taken[1] = 0;
  $taken[2] = 0;
  $taken[3] = 0;
  $taken[4] = 0;
  $taken[5] = 0;
  $taken[6] = 0;
  $numturns[1] = 1;
  $numturns[2] = 1;
  $numturns[3] = 1;
  $numturns[4] = 1;
  $numturns[5] = 1;
  $numturns[6] = 1;
  $numturns[7] = 2;
  $numturns[8] = 2;
  $modifiers[1] = '';
  $modifiers[2] = '2';
  $modifiers[3] = '\'';
  $axis[1] = 1;
  $axis[2] = 1;
  $axis[3] = 2;
  $axis[4] = 2;
  $axis[5] = 3;
  $axis[6] = 3;
 
  $prevaxis = -1;
  $scramble='';
  $totturns = 0;
  do {
  //select current axis to work on
   do {
    $curraxis = rand(1,3);
   } while ($curraxis == $prevaxis);
   $prevaxis = $curraxis; //update previous axis
   //select number of turns on this axis
   if ($totturns<($length-1)) {
    $num = $numturns[rand(1,8)];
   } else {
    $num = 1;
   } 
   for ($j=1; $j<=$num; $j++) {
    do {
     $faceindex=rand(1,6);
     $modifierindex=rand(1,3);
    } while (($axis[$faceindex] != $curraxis) || ($taken[$faceindex] == 1));
    $taken[$faceindex] = 1;
    $turn = $faces[$faceindex].$modifiers[$modifierindex]." ";
    $scramble = $scramble.$turn;
    $totturns++;
   }
   for ($j=1; $j<=6; $j++) {
    $taken[$j] = 0;
   }
  } while ($totturns<$length);
  return $scramble;
 }
 
$mylength = 30;
$myscramble = makeScramble($mylength);
...
(applet and/or database code ??)
...
...
Note that it does generate valid scrambles without sequences that can eb simplified - like U' D2 U. This is done through simple axis control. I first select a random axis and do either 1 or 2 turns on this axis (1 or 2 randomly). Then select a DIFFERENT axis and 1 or 2 turns again. If i already have length-1 turns in the scramble a single turn is generated for the last axis. This is also how the official .js WCA scrambler works. Feel free to use my scrambler for any website that may make use of it :) I know the code is not very elegant, but it works!!

It should be easy to translate the code to other programming languages ...

Also it is not hard to extend to bigger cubes. Note how i get bias to doing a single turn on same axis at a time, through the numturns array. This may be adjusted as needed.

- Per
 
Last edited:

tim

Member
Joined
Nov 22, 2006
Messages
1,692
Location
Karlsruhe, Germany
WCA
2007HABE01
YouTube
cin9247
Code:
do {
    $curraxis = rand(1,3);
} while ($curraxis == $prevaxis);
Can't you write that in a different (much cleaner) way?
I'm not good at PHP, but i think you could use an array - prefilled with all numbers - and subtract $prevaxis from the array. And then let PHP give you a random element out of this list.

Anyway, nice work :).

btw. the dollar sign before every variable looks so ugly and is completely unnecassary...
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
Hi :)

After trying to do it in some other way, this code took me about 2 hrs including debugging. The arrays may be initialised much more beautifully - i know. To avoid move redundancy i need to use some kind of loop, it's unavoidable. Exactly how to do that is up to the coder to find out. There's many things in the code that are not "beautiful". But it works fast, and that's the most important thing for me :) My code is inspired by Jaap's javscript scrambler :)

One last thing - the dollar sign ($) is necessary if php is gonna treat it as a variable/constant. Or else it will think it is a function. And yes, the dollars is one thing i do not like in php:cool: It would be nicer to write variable declarations explicitly like in pascal.

- Per
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
It would be nicer to write variable declarations explicitly like in pascal.
Or the other way around, function definitions, else it is a variable/constant.

Tim, how can that be hard to understand? That loop is the same in any high level language. Keep asking for a random until it is not equal to the last one you had.
Hi :)

I have been teaching both pascal and java to students. Indeed one of the most difficult things for them is to get to grips with conditional looping. Where the loop is "looping" a not predefined number of times - like the typical for statement. While - do is a bit easier to understand than the do-while version. The latter is very similar to the pascal repeat - until construction.

To comment on Tim's suggestion further it is possible to "delete" prevaxis from the array temporarily. But coding this fully is a lot more lines of code than how i chose to do it. Yest another option is to simply go to next free if a random selction picks the same. There's almost always more than one way to skin a chicken :p

- Per
 

tim

Member
Joined
Nov 22, 2006
Messages
1,692
Location
Karlsruhe, Germany
WCA
2007HABE01
YouTube
cin9247
Tim, how can that be hard to understand? That loop is the same in any high level language. Keep asking for a random until it is not equal to the last one you had.
Indeed i do understand what's going on. It's fine to use a loop if you can't expect the results of your random function (e.g. floats).

But if you have just 3(!) values, it's not necessary (and looks indeed ugly).

What about (pseudocode):
Code:
array = (1,2,3)
array.delete(lastValue)
newValue = array[rand(array.length)]
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
A niter way to write the same loop is:

Code:
do {} while ($prevaxis == ($curraxis = rand(1,3)));
Emty loop, all is in the condition =)
Yes yes this is possible but i do not like to execute code as part of a condition evaluation. I'm quite a bit of a coding classicist i guess.

Another ugly coding construction is

Code:
while (true) {
  ..
  if ( ...) break;
  ..
}
- Per
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
Hi Stefan :)

Yes that works since there is always just 1 taken axis in this case. One little catch though. If $prevaxis was 1 or 2 and the increment was 2 or 1 then the $curraxis would first evaluate to 3. Then mod it with 3 gives 0. The axes are in the range 1-3 not 0-2. So yes the fix is trivial, set up a new axis range like $axis = array(0,0,1,1,2,2).

After making some code work i generally do no bother to improve it unless there would be a big benefit (in speed!) Feel free to check the speed difference on say 10 000 scrambles with this little tweak :eek:

- Per
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
Hi :)

Here is the final version of the scrambler:

Code:
[FONT=Courier New]function makeScramble($length)[/FONT]
[FONT=Courier New]{[/FONT]
[FONT=Courier New][COLOR=seagreen]//initialisation[/COLOR][/FONT]
[FONT=Courier New]$faces = array('U','D','F','B','R','L');[/FONT]
[FONT=Courier New]$taken = array(0,0,0,0,0,0);[/FONT]
[FONT=Courier New]$numturns = array(1,1,1,1,1,1,2,2);[/FONT]
[FONT=Courier New]$axis = array(0,0,1,1,2,2);[/FONT]
[FONT=Courier New]$modifiers = array('','2','\'');[/FONT]
[FONT=Courier New]$curraxis = rand(0,2); [COLOR=seagreen]//small fix to allow a scramble to start with ANY axis[/COLOR][/FONT]
[FONT=Courier New]$scramble='';[/FONT]
[FONT=Courier New]$totturns = 0;[/FONT]
[FONT=Courier New][COLOR=seagreen]//generate a scramble[/COLOR][/FONT]
[FONT=Courier New]do {[/FONT]
[FONT=Courier New][COLOR=seagreen]//select current axis to work on[/COLOR][/FONT]
[FONT=Courier New] $curraxis = ($curraxis + rand (1,2)) % 3; [COLOR=seagreen]//thx to Kenneth!![/COLOR][/FONT]
[FONT=Courier New] [COLOR=seagreen]//$prevaxis = $curraxis; //update previous axis[/COLOR][/FONT]
[FONT=Courier New] [COLOR=seagreen]//select number of turns on this axis[/COLOR][/FONT]
[FONT=Courier New] if ($totturns<($length-1)) {[/FONT]
[FONT=Courier New]  $num = $numturns[rand(0,7)];[/FONT]
[FONT=Courier New] } else {[/FONT]
[FONT=Courier New]  $num = 1;[/FONT]
[FONT=Courier New] } [/FONT]
[FONT=Courier New] [COLOR=seagreen]//generate the turns on this axis[/COLOR] [/FONT]
[FONT=Courier New] for ($j=1; $j<=$num; $j++) {[/FONT]
[FONT=Courier New]  do {[/FONT]
[FONT=Courier New]   $faceindex=rand(0,5);[/FONT]
[FONT=Courier New]   $modifierindex=rand(0,2);[/FONT]
[FONT=Courier New]  } while (($axis[$faceindex] != $curraxis) || ($taken[$faceindex] == 1));[/FONT]
[FONT=Courier New]  $taken[$faceindex] = 1;[/FONT]
[FONT=Courier New]  [COLOR=seagreen]//generate the corresponding turn and add it to scramble[/COLOR][/FONT]
[FONT=Courier New]  $turn = $faces[$faceindex].$modifiers[$modifierindex]." ";[/FONT]
[FONT=Courier New]  $scramble = $scramble.$turn;[/FONT]
[FONT=Courier New]  $totturns++;[/FONT]
[FONT=Courier New] }[/FONT]
[FONT=Courier New] [COLOR=seagreen]// free the faces[/COLOR][/FONT]
[FONT=Courier New] for ($j=0; $j<=5; $j++) {[/FONT]
[FONT=Courier New]  $taken[$j] = 0;[/FONT]
[FONT=Courier New] }[/FONT]
[FONT=Courier New]} while ($totturns<$length);[/FONT]
[FONT=Courier New]return $scramble;[/FONT]
[FONT=Courier New]}[/FONT]
[FONT=Courier New]...[/FONT]
[FONT=Courier New]$mylength=30;[/FONT]
[FONT=Courier New]$myscramble=makeScramble($mylength);[/FONT]
[FONT=Courier New]...[/FONT]
This is much neater code:)
I will soon post a PHP 4by and 5by scrambler, using r and R style turns only. lower case means turning the 2 OUTER layers.


- Per
 
Last edited:

Kenneth

Not Alot
Joined
Aug 10, 2007
Messages
1,693
Location
Kauparve, Hejde, Gotland, Sweden
WCA
2005GUST01
YouTube
Kubbuth
//select current axis to work on
$curraxis = ($prevaxis + rand (1,2)) % 3;
$prevaxis = $curraxis; //update previous axis

Like this instead:

$curraxis = ($curraxis + rand (1,2)) % 3;

You don't need to save the old value because you do not compare anymore :)
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
//select current axis to work on
$curraxis = ($prevaxis + rand (1,2)) % 3;
$prevaxis = $curraxis; //update previous axis

Like this instead:

$curraxis = ($curraxis + rand (1,2)) % 3;

You don't need to save the old value because you do not compare anymore :)
Umm that's true. Stefan you are a noob:p

Getting rid of variables is a good thing ...

- Per
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
Hi :)

Yes your scrambler overcomes the "axis" problem. But i don not fully understand how that code works. This part:
Code:
($i^1) != $turn
I'm probably just a bit bit slow today;) I know it's your axis trick somehow ...

- Per
 

Leo

Member
Joined
Dec 2, 2007
Messages
213
Bah I wish I understood all of this language and how to use it :/. Nice scrambler :)

All I know is the tiniest bit of html.
 

mrCage

Member
Joined
Jun 17, 2006
Messages
654
Hi :)

And here is the promised 4x4x4/5x5x5 scrambler:

Code:
<?php
 function make45Scramble($length) {
  $faces = array('U','u','d','D','F','f','b','B','R','r','l','L');
  $taken = array(0,0,0,0,0,0,0,0,0,0,0,0);
  $numturns = array(1,1,1,1,1,1,2,2,2,3);
  $axis = array(0,0,0,0,1,1,1,1,2,2,2,2);
  $modifiers = array('','2','\'');
  $curraxis = rand(0,2);
  $scramble='';
  $totturns = 0;
  do {
  //select current axis to work on
   $curraxis = ($curraxis + rand(1,2)) % 3;
   //select number of turns on this axis
   $num = $numturns[rand(0,9)];
   if ($num>($length-$totturns)) {$num=$length-$totturns;} 
   for ($j=1; $j<=$num; $j++) {
    do {
     $faceindex=rand(0,11);
     $modifierindex=rand(0,2);
    } while (($axis[$faceindex] != $curraxis) || ($taken[$faceindex] == 1));
    $taken[$faceindex] = 1;
    $turn = $faces[$faceindex].$modifiers[$modifierindex]." ";
    $scramble = $scramble.$turn;
    $totturns++;
   }
   for ($j=0; $j<=11; $j++) {
    $taken[$j] = 0;
   }
  } while ($totturns<$length);
  return $scramble;
 }
 
 $mylength=50;
 $myscramble=make45Scramble($mylength);
 echo "Test Mix = ".$myscramble;
?>
Lowercase letters is double outer layer turns. Uppercase letters are the normal single outermost layer turns. Uu' may look strange since this produces a single inner layer turns. But this is intentional. since this notation has no tokens for the inner layer turns. m,e and s may easily be added if needed :)

- Per
Some things may look strange but are intentional.
 

tim

Member
Joined
Nov 22, 2006
Messages
1,692
Location
Karlsruhe, Germany
WCA
2007HABE01
YouTube
cin9247
Here's our Ruby (requires random access for arrays, which is implemented in Rails) version of it:

Code:
def cube_scramble(turns)
    scramble = ''
    variants = ['', "'", '2']
    axis = rand turns.size
    scramble_length.times do
      axis = (axis + rand(turns.size - 1) + 1) % turns.size
      scramble += (scramble.empty? ? '' : ' ') + turns[axis].rand + variants.rand
    end
    scramble
end

#3x3x3 & 2x2x2
cube_scramble [%w{R L}, %w{F B}, %w{D U}]
#4x4x4 & 5x5x5
cube_scramble [%w{R L}, %w{F B}, %w{D U}, %w{r l}, %w{f b}, %w{d u}]
Not the shortest, fastest or whatever scrambler, so i'm open for improvements :).
 
Top