commit aa26ba7f0202b97d2ad0800236dc4314c366385b
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Aug 31 23:52:07 2021 +0200

    Convert the 4 Minx'es to the new scrambling paradigm.

diff --git a/src/main/java/org/distorted/objects/TwistyMinx.java b/src/main/java/org/distorted/objects/TwistyMinx.java
index 7b39a6ad..ee167917 100644
--- a/src/main/java/org/distorted/objects/TwistyMinx.java
+++ b/src/main/java/org/distorted/objects/TwistyMinx.java
@@ -21,6 +21,7 @@ package org.distorted.objects;
 
 import android.content.res.Resources;
 
+import org.distorted.helpers.ScrambleState;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshSquare;
@@ -216,16 +217,6 @@ abstract class TwistyMinx extends TwistyObject
         36, 34, 56, 32, 43, 21, 48, 28, 42, 23
       };
 
-  static final boolean[][] OPPOSITE_ROWS =
-      {
-          {false,  true, false,  true, false, false},
-          { true, false, false,  true,  true,  true},
-          {false, false, false,  true, false,  true},
-          { true,  true,  true, false, false,  true},
-          {false,  true, false, false, false,  true},
-          {false,  true,  true,  true,  true, false}
-      };
-
   // the quadruple ( corner1, corner2, face1, face2 ) defining an edge.
   // In fact the 2 corners already define it, the faces only provide easy
   // way to get to know the colors. Order: arbitrary. Face1 arbitrarily on
@@ -326,9 +317,11 @@ 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;
+  private int mCurrState;
+  private int mIndexExcluded;
+  private ScrambleState[] mStates;
+  private int[][] mScrambleTable;
+  private int[] mNumOccurences;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -336,170 +329,233 @@ abstract class TwistyMinx extends TwistyObject
              DistortedEffects effects, int[][] moves, ObjectList obj, Resources res, int scrWidth)
     {
     super(numLayers, realSize, quat, texture, mesh, effects, moves, obj, res, scrWidth);
+
+    initializeScrambleStates(numLayers);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int[] getSolvedQuats(int cubit, int numLayers)
+  private int[] generateL(int numLayers, int index)
     {
-    int status = retCubitSolvedStatus(cubit,numLayers);
-    return status<0 ? null : buildSolvedQuats(MovementMinx.FACE_AXIS[mFaceMap[status]],QUATS);
+    int rows = (numLayers-1)/2;
+    int[] ret = new int[3*4*rows];
+
+    for(int i=0; i<rows; i++)
+      {
+      ret[12*i   ] = i;
+      ret[12*i+ 1] =-2;
+      ret[12*i+ 2] = index;
+      ret[12*i+ 3] = i;
+      ret[12*i+ 4] =-1;
+      ret[12*i+ 5] = index;
+      ret[12*i+ 6] = i;
+      ret[12*i+ 7] =+1;
+      ret[12*i+ 8] = index;
+      ret[12*i+ 9] = i;
+      ret[12*i+10] =+2;
+      ret[12*i+11] = index;
+      }
+
+    return ret;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  Static4D[] getQuats()
+  private int[] generateR(int numLayers, int index)
     {
-    return QUATS;
+    int rows = (numLayers-1)/2;
+    int[] ret = new int[3*4*rows];
+
+    for(int i=0; i<rows; i++)
+      {
+      int lay = rows+i+1;
+
+      ret[12*i   ] = lay;
+      ret[12*i+ 1] =-2;
+      ret[12*i+ 2] = index;
+      ret[12*i+ 3] = lay;
+      ret[12*i+ 4] =-1;
+      ret[12*i+ 5] = index;
+      ret[12*i+ 6] = lay;
+      ret[12*i+ 7] =+1;
+      ret[12*i+ 8] = index;
+      ret[12*i+ 9] = lay;
+      ret[12*i+10] =+2;
+      ret[12*i+11] = index;
+      }
+
+    return ret;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumFaces()
+  private int[] generateB(int numLayers, int index)
     {
-    return FACE_COLORS.length;
+    int rows = (numLayers-1);
+    int half = rows/2;
+    int[] ret = new int[3*4*rows];
+
+    for(int i=0; i<rows; i++)
+      {
+      int ind = i<half? index : index+1;
+      int lay = i<half? i : i+1;
+
+      ret[12*i   ] = lay;
+      ret[12*i+ 1] =-2;
+      ret[12*i+ 2] = ind;
+      ret[12*i+ 3] = lay;
+      ret[12*i+ 4] =-1;
+      ret[12*i+ 5] = ind;
+      ret[12*i+ 6] = lay;
+      ret[12*i+ 7] =+1;
+      ret[12*i+ 8] = ind;
+      ret[12*i+ 9] = lay;
+      ret[12*i+10] =+2;
+      ret[12*i+11] = ind;
+      }
+
+    return ret;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getSolvedFunctionIndex()
+  private void initializeScrambleStates(int numLayers)
     {
-    return 0;
+    int[] LEFT0 = generateL(numLayers,1);
+    int[] RIGH0 = generateR(numLayers,2);
+    int[] LEFT1 = generateL(numLayers,3);
+    int[] RIGH1 = generateR(numLayers,4);
+    int[] LEFT2 = generateL(numLayers,5);
+    int[] RIGH2 = generateR(numLayers,6);
+    int[] LEFT3 = generateL(numLayers,7);
+    int[] RIGH3 = generateR(numLayers,8);
+    int[] LEFT4 = generateL(numLayers,9);
+    int[] RIGH4 = generateR(numLayers,10);
+    int[] LEFT5 = generateL(numLayers,11);
+    int[] RIGH5 = generateR(numLayers,12);
+
+    int[] BOTH1 = generateB(numLayers,1);
+    int[] BOTH3 = generateB(numLayers,3);
+    int[] BOTH5 = generateB(numLayers,5);
+    int[] BOTH7 = generateB(numLayers,7);
+    int[] BOTH9 = generateB(numLayers,9);
+    int[] BOTH11= generateB(numLayers,11);
+
+    mStates = new ScrambleState[]
+      {
+      new ScrambleState( new int[][] { BOTH1,BOTH3,BOTH5,BOTH7,BOTH9,BOTH11 } ), // beg
+      new ScrambleState( new int[][] { {}   ,RIGH1,LEFT2,RIGH3,LEFT4,LEFT5  } ), // 0L
+      new ScrambleState( new int[][] { {}   ,LEFT1,RIGH2,LEFT3,RIGH4,RIGH5  } ), // 0R
+      new ScrambleState( new int[][] { RIGH0,{}   ,LEFT2,RIGH3,RIGH4,RIGH5  } ), // 1L
+      new ScrambleState( new int[][] { LEFT0,{}   ,RIGH2,LEFT3,LEFT4,LEFT5  } ), // 1R
+      new ScrambleState( new int[][] { LEFT0,LEFT1,{}   ,RIGH3,LEFT4,RIGH5  } ), // 2L
+      new ScrambleState( new int[][] { RIGH0,RIGH1,{}   ,LEFT3,RIGH4,LEFT5  } ), // 2R
+      new ScrambleState( new int[][] { RIGH0,RIGH1,RIGH2,{}   ,LEFT4,RIGH5  } ), // 3L
+      new ScrambleState( new int[][] { LEFT0,LEFT1,LEFT2,{}   ,RIGH4,LEFT5  } ), // 3R
+      new ScrambleState( new int[][] { LEFT0,RIGH1,LEFT2,LEFT3,{}   ,RIGH5  } ), // 4L
+      new ScrambleState( new int[][] { RIGH0,LEFT1,RIGH2,RIGH3,{}   ,LEFT5  } ), // 4R
+      new ScrambleState( new int[][] { LEFT0,RIGH1,RIGH2,RIGH3,RIGH4,{}     } ), // 5L
+      new ScrambleState( new int[][] { RIGH0,LEFT1,LEFT2,LEFT3,LEFT4,{}     } ), // 5R
+      };
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  boolean shouldResetTextureMaps()
+  int[] getSolvedQuats(int cubit, int numLayers)
     {
-    return false;
+    int status = retCubitSolvedStatus(cubit,numLayers);
+    return status<0 ? null : buildSolvedQuats(MovementMinx.FACE_AXIS[mFaceMap[status]],QUATS);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumCubitFaces()
+  Static4D[] getQuats()
     {
-    return FACES_PER_CUBIT;
+    return QUATS;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float returnMultiplier()
+  int getNumFaces()
     {
-    return 2.0f;
+    return FACE_COLORS.length;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void initializeScrambleTable(int[] first, int numLayers)
+  int getSolvedFunctionIndex()
     {
-    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;
+    return 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int convertRowIntoLayer(int row, int numLayers)
+  boolean shouldResetTextureMaps()
     {
-    return row>(numLayers-1)/2 ? 1 : 0;
+    return false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int convertLayerIntoRow(Random rnd, int layer, int numLayers)
+  int getNumCubitFaces()
     {
-    int ran = numLayers>3 ? rnd.nextInt((numLayers-1)/2) : 0;
-    return layer==0 ? ran : numLayers-1-ran;
+    return FACES_PER_CUBIT;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private boolean areOpposite(int oldAxis, int newAxis, int oldRow, int nom)
+  float returnMultiplier()
     {
-    return OPPOSITE_ROWS[oldAxis][newAxis]^(oldRow<nom);
+    return 2.0f;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int retNewRotationIndex(Random rnd, int nom, int[] oldRot)
+  private void initializeScrambling()
     {
-    int index=0, max=0;
+    int numLayers = getNumLayers();
 
-    for(int ax=0; ax<NUM_AXIS; ax++)
+    if( mScrambleTable ==null )
       {
-      if( ax!=oldRot[0] )
-        {
-        mPossibleAxis[index] = ax;
-        mPossibleLayers[index] = areOpposite(oldRot[0],ax,oldRot[1],nom) ? 0:1;
-        int tmp = mScrambleTable[mPossibleAxis[index]][mPossibleLayers[index]];
-        if( tmp>max ) max=tmp;
-        index++;
-        }
+      mScrambleTable = new int[NUM_AXIS][numLayers];
       }
-
-    for(int ax=0; ax<NUM_AXIS-1; ax++)
+    if( mNumOccurences ==null )
       {
-      int value = mScrambleTable[mPossibleAxis[ax]][mPossibleLayers[ax]];
-      mNumOccurences[ax] = max - value + (ax==0 ? 0 : mNumOccurences[ax-1]);
-      }
+      int max=0;
 
-    float random= rnd.nextFloat()*mNumOccurences[NUM_AXIS-2];
-
-    for(int ax=0; ax<NUM_AXIS-1; ax++)
-      {
-      if( random <= mNumOccurences[ax] )
+      for (ScrambleState mState : mStates)
         {
-        index=ax;
-        break;
+        int tmp = mState.getTotal(-1);
+        if (max < tmp) max = tmp;
         }
-      }
 
-    mScrambleTable[mPossibleAxis[index]][mPossibleLayers[index]]++;
+      mNumOccurences = new int[max];
+      }
 
-    return index;
+    for(int i=0; i<NUM_AXIS; i++)
+      for(int j=0; j<numLayers; j++) mScrambleTable[i][j] = 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
-  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
+  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int totalScrambles)
     {
-    int numLayers = getNumLayers();
-    int nom = (numLayers-1)/2;
-
     if( curr==0 )
       {
-      int lf = rnd.nextInt(2);
-      int row= rnd.nextInt(nom);
-      scramble[curr][0] = rnd.nextInt(NUM_AXIS);
-      scramble[curr][1] = (lf==0 ? row : numLayers-1-row);
-      initializeScrambleTable(scramble[curr],numLayers);
-      }
-    else
-      {
-      int index = retNewRotationIndex(rnd,nom,scramble[curr-1]);
-      scramble[curr][0] = mPossibleAxis[index];
-      scramble[curr][1] = convertLayerIntoRow(rnd, mPossibleLayers[index], numLayers);
+      mCurrState     = 0;
+      mIndexExcluded =-1;
+      initializeScrambling();
       }
 
-    switch( rnd.nextInt(4) )
-      {
-      case 0: scramble[curr][2] = -2; break;
-      case 1: scramble[curr][2] = -1; break;
-      case 2: scramble[curr][2] =  1; break;
-      case 3: scramble[curr][2] =  2; break;
-      }
+    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];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
