commit 0812242b3558b90b85857613adedfea928ea4d62
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Jun 16 13:29:53 2021 +0200

    Improve scrambling of the Minx'es and the Redi, which in full scramble mode were frequently leaving large corners unscrambled.

diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index 8b193323..5d3e7813 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -621,9 +621,9 @@ else
 
           FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
           crashlytics.setCustomKey("GLSL Version"  , shading );
-          crashlytics.setCustomKey("GLversion"     , version );
+          crashlytics.setCustomKey("GL version"    , version );
           crashlytics.setCustomKey("GL Vendor "    , vendor  );
-          crashlytics.setCustomKey("GLSLrenderer"  , renderer);
+          crashlytics.setCustomKey("GLSL renderer" , renderer);
           crashlytics.recordException(ex);
           }
         }
diff --git a/src/main/java/org/distorted/objects/TwistyMinx.java b/src/main/java/org/distorted/objects/TwistyMinx.java
index 7134bd67..255007db 100644
--- a/src/main/java/org/distorted/objects/TwistyMinx.java
+++ b/src/main/java/org/distorted/objects/TwistyMinx.java
@@ -326,6 +326,10 @@ abstract class TwistyMinx extends TwistyObject
     mBasicCornerV[2] = new Static4D(              0,        -0.500f,    0.0f, 0.0f );
     }
 
+  private static int[][] mScrambleTable;
+  private static int[] mPossibleAxis, mPossibleLayers;
+  private static int[] mNumOccurences;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   TwistyMinx(int numLayers, int realSize, Static4D quat, DistortedTexture texture, MeshSquare mesh,
@@ -427,22 +431,92 @@ abstract class TwistyMinx extends TwistyObject
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
 
-  public Static3D[] getRotationAxis()
+  private void initializeScrambleTable(int[] first, int numLayers)
     {
-    return ROT_AXIS;
+    if( mScrambleTable ==null ) mScrambleTable = new int[NUM_AXIS][2];
+    if( mPossibleAxis  ==null ) mPossibleAxis  = new int[NUM_AXIS-1];
+    if( mPossibleLayers==null ) mPossibleLayers= new int[NUM_AXIS-1];
+    if( mNumOccurences ==null ) mNumOccurences = new int[NUM_AXIS-1];
+
+    for(int i=0; i<NUM_AXIS; i++)
+      for(int j=0; j<2; j++)
+        {
+        mScrambleTable[i][j] = 0;
+        }
+
+    int layer = convertRowIntoLayer(first[1],numLayers);
+
+    mScrambleTable[first[0]][layer] = 1;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int[] getBasicAngle()
+  private int convertRowIntoLayer(int row, int numLayers)
     {
-    return BASIC_ANGLE;
+    return row>(numLayers-1)/2 ? 1 : 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int convertLayerIntoRow(Random rnd, int layer, int numLayers)
+    {
+    int ran = numLayers>3 ? rnd.nextInt((numLayers-1)/2) : 0;
+    return layer==0 ? ran : numLayers-1-ran;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+  private int retNewRotationIndex(Random rnd, int nom, int[] oldRot)
+    {
+    int index=0, max=0;
+
+    for(int ax=0; ax<NUM_AXIS; ax++)
+      {
+      if( ax!=oldRot[0] )
+        {
+        mPossibleAxis[index] = ax;
+        boolean opposite = OPPOSITE_ROWS[oldRot[0]][ax];
+        boolean low = opposite^(oldRot[1]<nom);
+        mPossibleLayers[index] = low ? 0:1;
+        int tmp = mScrambleTable[mPossibleAxis[index]][mPossibleLayers[index]];
+        if( tmp>max ) max=tmp;
+        index++;
+        }
+      }
+
+    for(int ax=0; ax<NUM_AXIS-1; ax++)
+      {
+      if( ax==0 )
+        {
+        mNumOccurences[ax] = max-mScrambleTable[mPossibleAxis[ax]][mPossibleLayers[ax]];
+        }
+      else
+        {
+        mNumOccurences[ax] = max-mScrambleTable[mPossibleAxis[ax]][mPossibleLayers[ax]] + mNumOccurences[ax-1];
+        }
+      }
+
+    float random= rnd.nextFloat();
+    int total = mNumOccurences[NUM_AXIS-2];
+
+    for(int ax=0; ax<NUM_AXIS-1; ax++)
+      {
+      if( random*total <= mNumOccurences[ax] )
+        {
+        index=ax;
+        break;
+        }
+      }
+
+    mScrambleTable[mPossibleAxis[index]][mPossibleLayers[index]]++;
+
+    return index;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
   public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
     {
     int numLayers = getNumLayers();
@@ -451,17 +525,16 @@ abstract class TwistyMinx extends TwistyObject
 
     if( curr==0 )
       {
-      int lf  = rnd.nextInt(2);
+      int lf = rnd.nextInt(2);
       scramble[curr][0] = rnd.nextInt(NUM_AXIS);
       scramble[curr][1] = (lf==0 ? row : numLayers-1-row);
+      initializeScrambleTable(scramble[curr],numLayers);
       }
     else
       {
-      boolean opposite = OPPOSITE_ROWS[scramble[curr-1][0]][scramble[curr][0]];
-      boolean low = opposite^(scramble[curr-1][1]<nom);
-      int newVector = rnd.nextInt(NUM_AXIS-1);
-      scramble[curr][0] = (newVector>=scramble[curr-1][0] ? newVector+1 : newVector);
-      scramble[curr][1] = (low ? row : numLayers-1-row);
+      int index = retNewRotationIndex(rnd,nom,scramble[curr-1]);
+      scramble[curr][0] = mPossibleAxis[index];
+      scramble[curr][1] = convertLayerIntoRow(rnd, mPossibleLayers[index], numLayers);
       }
 
     switch( rnd.nextInt(4) )
@@ -473,6 +546,20 @@ abstract class TwistyMinx extends TwistyObject
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Static3D[] getRotationAxis()
+    {
+    return ROT_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[] getBasicAngle()
+    {
+    return BASIC_ANGLE;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // only needed for solvers - there are no Minx solvers ATM)
 
diff --git a/src/main/java/org/distorted/objects/TwistyRedi.java b/src/main/java/org/distorted/objects/TwistyRedi.java
index 67c5a470..17ae4a34 100644
--- a/src/main/java/org/distorted/objects/TwistyRedi.java
+++ b/src/main/java/org/distorted/objects/TwistyRedi.java
@@ -203,6 +203,9 @@ public class TwistyRedi extends TwistyObject
 
   private static MeshBase[] mMeshes;
 
+  private static int[][] mScrambleTable;
+  private static int[] mPossibleAxis, mPossibleLayers;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   TwistyRedi(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
@@ -419,6 +422,57 @@ public class TwistyRedi extends TwistyObject
     return BASIC_ANGLE;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void initializeScrambleTable(int[] first)
+    {
+    if( mScrambleTable ==null ) mScrambleTable = new int[NUM_AXIS][2];
+    if( mPossibleAxis  ==null ) mPossibleAxis  = new int[NUM_AXIS-1];
+    if( mPossibleLayers==null ) mPossibleLayers= new int[NUM_AXIS-1];
+
+    for(int i=0; i<NUM_AXIS; i++)
+      for(int j=0; j<2; j++)
+        {
+        mScrambleTable[i][j] = 0;
+        }
+
+    mScrambleTable[first[0]][first[1]/2] = 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int retNewRotationIndex(Random rnd, int[] oldRot)
+    {
+    int index=0, max=0;
+
+    for(int ax=0; ax<NUM_AXIS; ax++)
+      {
+      if( ax!=oldRot[0] )
+        {
+        mPossibleAxis[index] = ax;
+        mPossibleLayers[index] = oldRot[0]+ax==3 ? 1-oldRot[1]/2 : oldRot[1]/2;
+        int tmp = mScrambleTable[mPossibleAxis[index]][mPossibleLayers[index]];
+        if( tmp>max ) max=tmp;
+        index++;
+        }
+      }
+
+    int a0 = max-mScrambleTable[mPossibleAxis[0]][mPossibleLayers[0]];
+    int a1 = max-mScrambleTable[mPossibleAxis[1]][mPossibleLayers[1]];
+    int a2 = max-mScrambleTable[mPossibleAxis[2]][mPossibleLayers[2]];
+
+    float total = a0+a1+a2;
+    float random= rnd.nextFloat();
+
+         if( random*total < a0    ) index=0;
+    else if( random*total < a0+a1 ) index=1;
+    else                            index=2;
+
+    mScrambleTable[mPossibleAxis[index]][mPossibleLayers[index]]++;
+
+    return index;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
@@ -427,12 +481,13 @@ public class TwistyRedi extends TwistyObject
       {
       scramble[curr][0] = rnd.nextInt(NUM_AXIS);
       scramble[curr][1] = rnd.nextFloat()<=0.5f ? 0:2;
+      initializeScrambleTable(scramble[curr]);
       }
     else
       {
-      int newVector = rnd.nextInt(NUM_AXIS -1);
-      scramble[curr][0] = (newVector>=scramble[curr-1][0] ? newVector+1 : newVector);
-      scramble[curr][1] = (scramble[curr-1][0]+scramble[curr][0]==3 ? 2-scramble[curr-1][1] : scramble[curr-1][1]);
+      int index = retNewRotationIndex(rnd,scramble[curr-1]);
+      scramble[curr][0] = mPossibleAxis[index];
+      scramble[curr][1] = 2*mPossibleLayers[index];
       }
 
     switch( rnd.nextInt(2) )
@@ -443,9 +498,7 @@ public class TwistyRedi extends TwistyObject
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// The Redi is solved if and only if:
-//
-// ??
+// The Redi is solved if and only if all cubits are rotated with the same quat.
 
   public boolean isSolved()
     {
