commit 7c969a6dbdc87d39009b45d402a80ee50294dfc5
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sun Aug 16 19:34:26 2020 +0100

    Adjust randomizing new rotations so that:
    
    1) it works for basicAngle=5 (Megaminx) (so now basicAngle=2,3,4,5 supported)
    2) it leaves the decision as to what can be the next rotation to the Object class, as in case of certain Objects (the Dino, or the Helicopter, the Megaminx) the next rotation doesn't have to 'intersect' the old rotation always when oldRotAxis != newRotAxis (that's so simple only in case of the Cube and - only partly - the Pyraminx!)

diff --git a/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java b/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java
index b2fea9a5..6ef8e1fd 100644
--- a/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java
+++ b/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java
@@ -63,15 +63,16 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
       }
     }
 
+  public static final int START_AXIS = -2;
+  public static final int STOP_AXIS  = -1;
+
   private RubikPreRender mPreRender;
   private int mEffectReturned;
   private int mNumDoubleScramblesLeft, mNumScramblesLeft;
-  private int mLastVector;
+  private int mLastRotAxis, mLastRow;
   private long mDurationSingleTurn;
   private Random mRnd;
-  private int mNumAxis;
   private int mBasicAngle;
-  private float[] mRowChances;
 
   RubikObject mObject;
   Effect[] mNodeEffects;
@@ -85,7 +86,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
   ScrambleEffect()
     {
     mRnd = new Random( System.currentTimeMillis() );
-    mLastVector = -2;
+    mLastRotAxis = START_AXIS;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -98,8 +99,9 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
 // the time a single quarter-turn takes!
 //
 // Only works for
+// basicAngle==5, i.e. something whose rotations are by  72 degrees (Megaminx) or
 // basicAngle==4, i.e. something whose rotations are by  90 degrees (RubikCube) or
-// basicAngle==3, i.e. something whose rotations are by 120 degrees (a Pyramix) or
+// basicAngle==3, i.e. something whose rotations are by 120 degrees (Pyramix) or
 // basicAngle==2, i.e. something whose rotations are by 180 degrees (e.g. a 3x2x1 'Bunny')
 
   private void createBaseEffects(int duration, int numScrambles)
@@ -110,9 +112,11 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
 
     if( mBasicAngle>=4 )
       {
+      int chance = mBasicAngle==4 ? 3:2;
+
       for(int i=0; i<numScrambles; i++)
         {
-        if( (mRnd.nextInt() % 3) == 0 )
+        if( (mRnd.nextInt() % chance) == 0 )
           {
           mNumDoubleScramblesLeft++;
           }
@@ -125,36 +129,17 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// only works if basicAngle<=4, i.e. won't work for something whose basic rotations are by less
-// than 90 degrees.
+// only works if basicAngle<=5
 
   private void addNewScramble()
     {
     if( mNumScramblesLeft>0 )
       {
-      if( mLastVector == -2 )
-        {
-        mLastVector = mRnd.nextInt(mNumAxis);
-        }
-      else
-        {
-        int newVector = mRnd.nextInt(mNumAxis-1);
-        mLastVector = (newVector>=mLastVector ? newVector+1 : newVector);
-        }
-
-      int row=0;
-      float rowFloat = mRnd.nextFloat();
-
-      for(int i=0; i<mRowChances.length; i++)
-        {
-        if( rowFloat<=mRowChances[i] )
-          {
-          row=i;
-          break;
-          }
-        }
+      int lastRotAxis = mLastRotAxis;
+      mLastRotAxis = mObject.randomizeNewRotAxis(mRnd,mLastRotAxis);
+      mLastRow = mObject.randomizeNewRow(mRnd,lastRotAxis,mLastRow,mLastRotAxis);
 
-      int rowBitmap  = (1<<row);
+      int rowBitmap  = (1<<mLastRow);
       int angle= randomizeAngle();
       int absAngle = (angle<0 ? -angle : angle);
       long durationMillis = absAngle*mDurationSingleTurn;
@@ -167,11 +152,11 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
         android.util.Log.e("effect", "ERROR: "+mNumDoubleScramblesLeft);
         }
 
-      mPreRender.addRotation(this, mLastVector, rowBitmap, angle*(360/mBasicAngle), durationMillis);
+      mPreRender.addRotation(this, mLastRotAxis, rowBitmap, angle*(360/mBasicAngle), durationMillis);
       }
     else
       {
-      mLastVector = -1;
+      mLastRotAxis = STOP_AXIS;
 
       if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
         {
@@ -181,7 +166,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// only works for basicAngle<=4.
+// only works for basicAngle<=5.
 
   private int randomizeAngle()
     {
@@ -318,11 +303,9 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
     {
     mObject     = pre.getObject();
     mPreRender  = pre;
-    mRowChances = mObject.getRowChances();
 
     mObject.solve();
 
-    mNumAxis    = mObject.getRotationAxis().length;
     mBasicAngle = mObject.getBasicAngle();
 
     int numScrambles = pre.getNumScrambles();
diff --git a/src/main/java/org/distorted/objects/RubikCube.java b/src/main/java/org/distorted/objects/RubikCube.java
index 5b126213..ea86bff2 100644
--- a/src/main/java/org/distorted/objects/RubikCube.java
+++ b/src/main/java/org/distorted/objects/RubikCube.java
@@ -36,6 +36,10 @@ import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import java.util.Random;
+
+import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class RubikCube extends RubikObject
@@ -314,6 +318,21 @@ class RubikCube extends RubikObject
     return getSize();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getRowChances()
+    {
+    int size = getSize();
+    float[] chances = new float[size];
+
+    for(int i=0; i<size; i++)
+      {
+      chances[i] = (i+1.0f) / size;
+      }
+
+    return chances;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
@@ -338,24 +357,40 @@ class RubikCube extends RubikObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public float[] getRowChances()
+  public float returnRotationFactor(float offset)
     {
-    int size = getSize();
-    float[] chances = new float[size];
+    return 1.0f;
+    }
 
-    for(int i=0; i<size; i++)
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
+    {
+    int numAxis = ROTATION_AXIS.length;
+
+    if( oldRotAxis == START_AXIS )
       {
-      chances[i] = (i+1.0f) / size;
+      return rnd.nextInt(numAxis);
+      }
+    else
+      {
+      int newVector = rnd.nextInt(numAxis-1);
+      return (newVector>=oldRotAxis ? newVector+1 : newVector);
       }
-
-    return chances;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public float returnRotationFactor(float offset)
+  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
     {
-    return 1.0f;
+    float rowFloat = rnd.nextFloat();
+
+    for(int row=0; row<mRowChances.length; row++)
+      {
+      if( rowFloat<=mRowChances[row] ) return row;
+      }
+
+    return 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/RubikDino.java b/src/main/java/org/distorted/objects/RubikDino.java
index e1e77cef..755c4514 100644
--- a/src/main/java/org/distorted/objects/RubikDino.java
+++ b/src/main/java/org/distorted/objects/RubikDino.java
@@ -42,6 +42,10 @@ import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import java.util.Random;
+
+import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class RubikDino extends RubikObject
@@ -338,6 +342,19 @@ public class RubikDino extends RubikObject
     return 2.0f;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getRowChances()
+    {
+    float[] chances = new float[3];
+
+    chances[0] = 0.5f;
+    chances[1] = 0.5f;
+    chances[2] = 1.0f;
+
+    return chances;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
@@ -369,15 +386,59 @@ public class RubikDino extends RubikObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public float[] getRowChances()
+  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
     {
-    float[] chances = new float[3];
+    int numAxis = ROTATION_AXIS.length;
 
-    chances[0] = 0.5f;
-    chances[1] = 0.5f;
-    chances[2] = 1.0f;
+    if( oldRotAxis == START_AXIS )
+      {
+      return rnd.nextInt(numAxis);
+      }
+    else
+      {
+      int newVector = rnd.nextInt(numAxis-1);
+      return (newVector>=oldRotAxis ? newVector+1 : newVector);
+      }
+    }
 
-    return chances;
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
+    {
+    float rowFloat = rnd.nextFloat();
+
+    switch(oldRotAxis)
+      {
+      case 0 : switch(newRotAxis)
+                 {
+                 case 1:
+                 case 2: return oldRow;
+                 case 3: return 2-oldRow;
+                 default: android.util.Log.e("dino", "error: oldRotAxis="+oldRotAxis+" newRotAxis:"+newRotAxis);
+                 }
+      case 1 : switch(newRotAxis)
+                 {
+                 case 0:
+                 case 3: return oldRow;
+                 case 2: return 2-oldRow;
+                 default: android.util.Log.e("dino", "error: oldRotAxis="+oldRotAxis+" newRotAxis:"+newRotAxis);
+                 }
+      case 2 : switch(newRotAxis)
+                 {
+                 case 0:
+                 case 3: return oldRow;
+                 case 1: return 2-oldRow;
+                 default: android.util.Log.e("dino", "error: oldRotAxis="+oldRotAxis+" newRotAxis:"+newRotAxis);
+                 }
+      case 3 : switch(newRotAxis)
+                 {
+                 case 1:
+                 case 2: return oldRow;
+                 case 0: return 2-oldRow;
+                 default: android.util.Log.e("dino", "error: oldRotAxis="+oldRotAxis+" newRotAxis:"+newRotAxis);
+                 }
+      default: return rowFloat<=0.5f ? 0:2;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/RubikObject.java b/src/main/java/org/distorted/objects/RubikObject.java
index 46699a06..cbbae876 100644
--- a/src/main/java/org/distorted/objects/RubikObject.java
+++ b/src/main/java/org/distorted/objects/RubikObject.java
@@ -49,6 +49,7 @@ import org.distorted.library.type.Static4D;
 import java.io.DataInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Random;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -90,6 +91,7 @@ public abstract class RubikObject extends DistortedNode
   private Static3D mObjectScale;
 
   float mStart, mStep;
+  float[] mRowChances;
 
   Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
   DistortedTexture mTexture;
@@ -124,6 +126,8 @@ public abstract class RubikObject extends DistortedNode
     mNodeScale= new Static3D(1,NODE_RATIO,1);
     mQuat = quat;
 
+    mRowChances = getRowChances();
+
     mRotationAngle= new Dynamic1D();
     mRotationAxis = new Static3D(1,0,0);
     mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
@@ -696,10 +700,13 @@ public abstract class RubikObject extends DistortedNode
   abstract void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side);
   abstract int getFaceColor(int cubit, int cubitface, int size);
   abstract float returnMultiplier();
+  abstract float[] getRowChances();
+
   public abstract Static3D[] getRotationAxis();
   public abstract int getBasicAngle();
   public abstract int computeRowFromOffset(float offset);
   public abstract float returnRotationFactor(float offset);
   public abstract String retObjectString();
-  public abstract float[] getRowChances();
+  public abstract int randomizeNewRotAxis(Random rnd, int oldRotAxis);
+  public abstract int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis);
   }
diff --git a/src/main/java/org/distorted/objects/RubikPyraminx.java b/src/main/java/org/distorted/objects/RubikPyraminx.java
index be480366..515ca126 100644
--- a/src/main/java/org/distorted/objects/RubikPyraminx.java
+++ b/src/main/java/org/distorted/objects/RubikPyraminx.java
@@ -38,6 +38,10 @@ import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import java.util.Random;
+
+import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class RubikPyraminx extends RubikObject
@@ -399,6 +403,24 @@ public class RubikPyraminx extends RubikObject
     return getSize()/0.82f;//(SQ3/2);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getRowChances()
+    {
+    int size = getSize();
+    int total = size*(size+1)/2;
+    float running=0.0f;
+    float[] chances = new float[size];
+
+    for(int i=0; i<size; i++)
+      {
+      running += (size-i);
+      chances[i] = running / total;
+      }
+
+    return chances;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
@@ -424,30 +446,43 @@ public class RubikPyraminx extends RubikObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public float[] getRowChances()
+  public float returnRotationFactor(float offset)
     {
     int size = getSize();
-    int total = size*(size+1)/2;
-    float running=0.0f;
-    float[] chances = new float[size];
+    int row  = (int)(size*offset/(SQ3/2));
 
-    for(int i=0; i<size; i++)
+    return ((float)size)/(size-row);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
+    {
+    int numAxis = ROTATION_AXIS.length;
+
+    if( oldRotAxis == START_AXIS )
       {
-      running += (size-i);
-      chances[i] = running / total;
+      return rnd.nextInt(numAxis);
+      }
+    else
+      {
+      int newVector = rnd.nextInt(numAxis-1);
+      return (newVector>=oldRotAxis ? newVector+1 : newVector);
       }
-
-    return chances;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public float returnRotationFactor(float offset)
+  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
     {
-    int size = getSize();
-    int row  = (int)(size*offset/(SQ3/2));
+    float rowFloat = rnd.nextFloat();
 
-    return ((float)size)/(size-row);
+    for(int row=0; row<mRowChances.length; row++)
+      {
+      if( rowFloat<=mRowChances[row] ) return row;
+      }
+
+    return 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
