commit bbbfb6af58fa265a3d3658d1675cbcb61153e137
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Sep 1 22:02:47 2021 +0200

    Convert the Pyraminxes to the new scrambling paradigm.
    This leaves only the two Squares.

diff --git a/src/main/java/org/distorted/objects/TwistyPyraminx.java b/src/main/java/org/distorted/objects/TwistyPyraminx.java
index 3cf0965c..825fe8f6 100644
--- a/src/main/java/org/distorted/objects/TwistyPyraminx.java
+++ b/src/main/java/org/distorted/objects/TwistyPyraminx.java
@@ -23,6 +23,7 @@ import android.content.res.Resources;
 
 import org.distorted.helpers.ObjectShape;
 import org.distorted.helpers.ObjectSticker;
+import org.distorted.helpers.ScrambleState;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshSquare;
@@ -114,8 +115,6 @@ public class TwistyPyraminx extends TwistyObject
              { -0.4330127f, -0.25f, 0.4330127f, -0.25f, 0.0f, 0.5f }
           };
 
-  private static float[] mRowChances;
-
   private static final ObjectSticker[] mStickers;
 
   static
@@ -127,37 +126,64 @@ public class TwistyPyraminx extends TwistyObject
     mStickers[0] = new ObjectSticker(STICKERS[0],null,radii,stroke);
     }
 
+  private int mCurrState;
+  private int mIndexExcluded;
+  private ScrambleState[] mStates;
+  private int[][] mScrambleTable;
+  private int[] mNumOccurences;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   TwistyPyraminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
                  DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
     {
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
+
+    initializeScrambleStates(size);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int[] getSolvedQuats(int cubit, int numLayers)
+  private int[][] generateState(int start, int end)
     {
-    int status = retCubitSolvedStatus(cubit,numLayers);
-    return status<0 ? null : buildSolvedQuats(MovementPyraminx.FACE_AXIS[status],QUATS);
+    int len = end-start+1;
+    int[] tmp = new int[6*len];
+
+    for(int i=0; i<len; i++)
+      {
+      tmp[6*i  ] = start;
+      tmp[6*i+1] = -1;
+      tmp[6*i+2] = start;
+      tmp[6*i+3] = start;
+      tmp[6*i+4] = +1;
+      tmp[6*i+5] = start;
+
+      start++;
+      }
+
+    return new int[][] {tmp,tmp,tmp,tmp};
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private float[] getRowChances(int numLayers)
+  private void initializeScrambleStates(int numLayers)
     {
-    int total = numLayers*(numLayers+1)/2;
-    float running=0.0f;
-    float[] chances = new float[numLayers];
+    mStates = new ScrambleState[numLayers];
 
-    for(int i=0; i<numLayers; i++)
+    for(int i=0; i<numLayers-1; i++)
       {
-      running += (numLayers-i);
-      chances[i] = running / total;
+      mStates[i] = new ScrambleState( generateState(0,numLayers-1-i) );
       }
 
-    return chances;
+    mStates[numLayers-1] = new ScrambleState( generateState(1,numLayers-2) );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getSolvedQuats(int cubit, int numLayers)
+    {
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementPyraminx.FACE_AXIS[status],QUATS);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -389,71 +415,67 @@ public class TwistyPyraminx extends TwistyObject
     return getNumLayers()/(SQ6/3);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-
-  public Static3D[] getRotationAxis()
-    {
-    return ROT_AXIS;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int[] getBasicAngle()
-    {
-    return BASIC_ANGLE;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
+  private void initializeScrambling()
     {
     int numLayers = getNumLayers();
 
-    if( mRowChances==null ) mRowChances = getRowChances(numLayers);
-
-    if( curr==0 )
+    if( mScrambleTable ==null )
       {
-      scramble[curr][0] = rnd.nextInt(NUM_AXIS);
+      mScrambleTable = new int[NUM_AXIS][numLayers];
       }
-    else
+    if( mNumOccurences ==null )
       {
-      int newVector = rnd.nextInt(NUM_AXIS-1);
-      scramble[curr][0] = (newVector>=scramble[curr-1][0] ? newVector+1 : newVector);
-
-      // Correct the situation when we first rotate the largest layer, then a tip (which doesn't
-      // intersect anything besides the largest layer!) and then we try to rotate again along
-      // the same axis like 2 rotations before - which carries the risk we rotate the largest
-      // layer back to its spot again and the three moves end up being only a single tip rotation.
-      if( curr>=2 && scramble[curr-1][1]==(numLayers-1) && scramble[curr][0]==scramble[curr-2][0] )
+      int max=0;
+
+      for (ScrambleState mState : mStates)
         {
-        for(int ax=0; ax<NUM_AXIS; ax++)
-          {
-          if( scramble[curr-1][0]!=ax && scramble[curr-2][0]!=ax )
-            {
-            scramble[curr][0]=ax;
-            break;
-            }
-          }
+        int tmp = mState.getTotal(-1);
+        if (max < tmp) max = tmp;
         }
+
+      mNumOccurences = new int[max];
       }
 
-    float rowFloat = rnd.nextFloat();
+    for(int i=0; i<NUM_AXIS; i++)
+      for(int j=0; j<numLayers; j++) mScrambleTable[i][j] = 0;
+    }
 
-    for(int row=0; row<numLayers; row++)
-      {
-      if( rowFloat<=mRowChances[row] )
-        {
-        scramble[curr][1] = row;
-        break;
-        }
-      }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
 
-    switch( rnd.nextInt(2) )
+  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int totalScrambles)
+    {
+    if( curr==0 )
       {
-      case 0: scramble[curr][2] = -1; break;
-      case 1: scramble[curr][2] =  1; break;
+      mCurrState     = 0;
+      mIndexExcluded =-1;
+      initializeScrambling();
       }
+
+    int[] info= mStates[mCurrState].getRandom(rnd, mIndexExcluded, mScrambleTable, mNumOccurences);
+
+    scramble[curr][0] = info[0];
+    scramble[curr][1] = info[1];
+    scramble[curr][2] = info[2];
+
+    mCurrState     = info[3];
+    mIndexExcluded = info[0];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Static3D[] getRotationAxis()
+    {
+    return ROT_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[] getBasicAngle()
+    {
+    return BASIC_ANGLE;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
