commit c0266cb192bf48584d1a4c25490dc56e928f6dc7
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Mar 6 01:29:28 2023 +0100

    Tablebase-based scrambling.

diff --git a/src/main/java/org/distorted/objectlib/effects/scramble/ScrambleEffect.java b/src/main/java/org/distorted/objectlib/effects/scramble/ScrambleEffect.java
index f57bb013..5d56f34f 100644
--- a/src/main/java/org/distorted/objectlib/effects/scramble/ScrambleEffect.java
+++ b/src/main/java/org/distorted/objectlib/effects/scramble/ScrambleEffect.java
@@ -95,14 +95,23 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
     for(int scramble=0; scramble<mNumScramblesLeft; scramble++)
       {
       mObject.randomizeNewScramble(mScrambles, mRnd, scramble, numScrambles);
-      axis  = mScrambles[scramble][0];
-      row   = mScrambles[scramble][1];
-      angle = mScrambles[scramble][2];
+      int[] s = mScrambles[scramble];
+      axis  = s[0];
+      row   = s[1];
+      angle = s[2];
       absAngle = (angle<0 ? -angle : angle);
       basicDegrees = 360/mBasicAngle[axis][row];
       totalDegrees += absAngle*basicDegrees;
       }
 
+    // tablebase scrambling can return a scramble which is shorter than requested,
+    // padding the end with three 0s. Take note of that.
+    for(int scramble=0; scramble<mNumScramblesLeft; scramble++)
+      {
+      int[] s = mScrambles[scramble];
+      if( s[0]==0 && s[1]==0 && s[2]==0 ) mNumScramblesLeft = scramble;
+      }
+
     mDurationPerDegree = duration/totalDegrees;
     mNumScrambles = 0;
 
diff --git a/src/main/java/org/distorted/objectlib/helpers/QuatGroupGenerator.java b/src/main/java/org/distorted/objectlib/helpers/QuatGroupGenerator.java
index 3612f4d7..a023436e 100644
--- a/src/main/java/org/distorted/objectlib/helpers/QuatGroupGenerator.java
+++ b/src/main/java/org/distorted/objectlib/helpers/QuatGroupGenerator.java
@@ -161,11 +161,6 @@ public class QuatGroupGenerator
       table[i] = new Static4D(mTable[i]);
       }
 
-    if( mInserted> ObjectControl.MAX_QUATS )
-      {
-      android.util.Log.e("D", "ERROR! too large quat group="+mInserted);
-      }
-
     return table;
     }
 
diff --git a/src/main/java/org/distorted/objectlib/main/InitAssets.java b/src/main/java/org/distorted/objectlib/main/InitAssets.java
index cbd10391..2c7b9fa9 100644
--- a/src/main/java/org/distorted/objectlib/main/InitAssets.java
+++ b/src/main/java/org/distorted/objectlib/main/InitAssets.java
@@ -9,6 +9,8 @@
 
 package org.distorted.objectlib.main;
 
+import android.content.res.Resources;
+
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -16,15 +18,17 @@ import java.io.InputStream;
 
 public class InitAssets
   {
-  InputStream mJsonStream;
-  InputStream mMeshStream;
+  private final InputStream mJsonStream;
+  private final InputStream mMeshStream;
+  private final Resources mRes;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public InitAssets(InputStream jsonStream, InputStream meshStream)
+  public InitAssets(InputStream jsonStream, InputStream meshStream, Resources res)
     {
     mJsonStream = jsonStream;
     mMeshStream = meshStream;
+    mRes = res;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -48,6 +52,13 @@ public class InitAssets
     return mMeshStream;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Resources getResources()
+    {
+    return mRes;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void close()
diff --git a/src/main/java/org/distorted/objectlib/main/ObjectType.java b/src/main/java/org/distorted/objectlib/main/ObjectType.java
index ee8a7a7e..372badb6 100644
--- a/src/main/java/org/distorted/objectlib/main/ObjectType.java
+++ b/src/main/java/org/distorted/objectlib/main/ObjectType.java
@@ -44,7 +44,7 @@ public enum ObjectType
   MORP_3 ( TwistyMorphix.class        , 17, R.drawable.morp_3, true ,   60, new InitData(new int[] {3,3,3})),
   MORP_4 ( TwistyMorphix.class        , 24, R.drawable.morp_4, true ,   70, new InitData(new int[] {4,4,4})),
   JING_2 ( TwistyJing.class           , 11, R.drawable.jing_2, true,    50, new InitData(new int[] {2,2,2,2})),
-  PYRA_3 ( TwistyPyraminx.class       , 10, R.drawable.pyra_3, true,     0, new InitData(new int[] {3,3,3,3})),
+  PYRA_3 ( TwistyPyraminx.class       , 11, R.drawable.pyra_3, true,     0, new InitData(new int[] {3,3,3,3})),
 
   PYRA_4 ( TwistyPyraminx.class       , 17, R.drawable.pyra_4, true,    50, new InitData(new int[] {4,4,4,4})),
   PYRA_5 ( TwistyPyraminx.class       , 23, R.drawable.pyra_5, true,    70, new InitData(new int[] {5,5,5,5})),
diff --git a/src/main/java/org/distorted/objectlib/main/TwistyObject.java b/src/main/java/org/distorted/objectlib/main/TwistyObject.java
index 94ac7f51..6f27098a 100644
--- a/src/main/java/org/distorted/objectlib/main/TwistyObject.java
+++ b/src/main/java/org/distorted/objectlib/main/TwistyObject.java
@@ -15,6 +15,7 @@ import java.io.InputStream;
 import java.util.Random;
 
 import android.content.SharedPreferences;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -53,6 +54,8 @@ import org.distorted.objectlib.helpers.QuatGroupGenerator;
 import org.distorted.objectlib.scrambling.ObjectScrambler;
 import org.distorted.objectlib.json.JsonReader;
 import org.distorted.objectlib.scrambling.ScrambleEdgeGenerator;
+import org.distorted.objectlib.tablebases.ImplementedTablebasesList;
+import org.distorted.objectlib.tablebases.TablebasesAbstract;
 import org.distorted.objectlib.touchcontrol.*;
 
 import static org.distorted.objectlib.touchcontrol.TouchControl.*;
@@ -158,20 +161,19 @@ public abstract class TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  TwistyObject(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InitAssets assets)
+  TwistyObject(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InitAssets asset)
     {
     try
       {
-      InputStream jsonStream = assets!=null ? assets.getJsonStream(): null;
-      InputStream meshStream = assets!=null ? assets.getMeshStream(): null;
+      InputStream jsonStream = asset!=null ? asset.getJsonStream(): null;
       JsonReader reader = new JsonReader();
       reader.parseJsonFile(jsonStream);
       setReader(reader);
       mNumLayers = reader.getNumLayers();
       mSize      = reader.getSize();
       mInitData  = null;
-      initialize(meshState,iconMode,quat,move,scale,meshStream,true);
-      if( assets!=null ) assets.close();
+      initialize(meshState,iconMode,quat,move,scale,asset,true);
+      if( asset!=null ) asset.close();
       mError = false;
       mErrorString=null;
       }
@@ -184,14 +186,13 @@ public abstract class TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public TwistyObject(int meshState, int iconMode, float size, Static4D quat, Static3D move, float scale, InitData data, InitAssets assets)
+  public TwistyObject(int meshState, int iconMode, float size, Static4D quat, Static3D move, float scale, InitData data, InitAssets asset)
     {
     mNumLayers = data.getNumLayers();
     mSize      = size;
     mInitData  = data;
-    InputStream meshStream = assets!=null ? assets.getMeshStream() : null;
-    initialize(meshState,iconMode,quat,move,scale,meshStream,false);
-    if( assets!=null ) assets.close();
+    initialize(meshState,iconMode,quat,move,scale,asset,false);
+    if( asset!=null ) asset.close();
     mError = false;
     mErrorString = null;
     }
@@ -211,7 +212,7 @@ public abstract class TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void initialize(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InputStream meshStream, boolean fromJSON)
+  private void initialize(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InitAssets asset, boolean fromJSON)
     {
     mRotationState = STATE_NOTHING;
     mIconMode = iconMode;
@@ -244,7 +245,9 @@ public abstract class TwistyObject
     int[][] edges = getScrambleEdges();
     int[][] algorithms = getScrambleAlgorithms();
 
-    mScrambler = new ObjectScrambler(scramblingType,mNumAxis,mNumLayers,algorithms,edges);
+    Resources res = asset==null ? null : asset.getResources();
+    TablebasesAbstract tablebase = res!=null ? getTablebase(res) : null;
+    mScrambler = new ObjectScrambler(scramblingType,mNumAxis,mNumLayers,algorithms,edges,tablebase);
 
     boolean bandaged=false;
 
@@ -277,6 +280,7 @@ public abstract class TwistyObject
     MatrixEffectQuaternion quatEffect = new MatrixEffectQuaternion(mQuat, CENTER);
     MatrixEffectMove moveEffect = new MatrixEffectMove(move);
 
+    InputStream meshStream = asset!=null ? asset.getMeshStream(): null;
     boolean fromDMESH = (meshStream!=null && meshState==MESH_NICE);
     getQuatsAndShapes(fromDMESH,fromJSON);
     createMeshAndCubits(meshStream,meshState,fromDMESH);
@@ -294,6 +298,18 @@ public abstract class TwistyObject
     mNode = new DistortedNode(mTexture,mEffects,mMesh);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private TablebasesAbstract getTablebase(Resources res)
+    {
+    ObjectSignature signature = getSignature();
+    long[] array = signature.getArray();
+    int len = array.length;
+    int sig = (int)array[len-1];
+
+    return ImplementedTablebasesList.createPacked(res,sig);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void createQuaternionEffects()
diff --git a/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java b/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java
index b95c8b6e..365a7d7b 100644
--- a/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java
+++ b/src/main/java/org/distorted/objectlib/scrambling/ObjectScrambler.java
@@ -13,6 +13,7 @@ import java.util.ArrayList;
 import java.util.Random;
 
 import org.distorted.objectlib.helpers.ObjectSignature;
+import org.distorted.objectlib.tablebases.TablebasesAbstract;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -21,7 +22,6 @@ public class ObjectScrambler
   public static final int SCRAMBLING_ALGORITHMS = 0;
   public static final int SCRAMBLING_SQUARE1    = 1;
   public static final int SCRAMBLING_BANDAGED   = 2;
-  public static final int SCRAMBLING_TABLEBASES = 3;
 
   private final ScrambleState[] mStates;
   private final int mType;
@@ -56,9 +56,12 @@ public class ObjectScrambler
   private ArrayList<ScrambleStateBandagedCuboid> mStatesHistory;
   private BlacklistedSignatures mBlacklisted;
 
+  // type==3 (tablebases)
+  private TablebasesAbstract mTablebase;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public ObjectScrambler(int type, int numAxis, int[] numLayers, int[][] algorithms, int[][] edges)
+  public ObjectScrambler(int type, int numAxis, int[] numLayers, int[][] algorithms, int[][] edges, TablebasesAbstract tablebase)
     {
     mType       = type;
     mNumAxis    = numAxis;
@@ -84,6 +87,8 @@ public class ObjectScrambler
       mCornerQuat = new int[8];
       mLastRot = LAST_SL;
       }
+
+    mTablebase = tablebase;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -615,25 +620,23 @@ public class ObjectScrambler
     if( curr==0 ) initializeType2Scrambling(scramble,rnd,total,signature);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// TYPE 3   (tablebases)
-
-  private void randomizeNewScramble3(int[][] scramble, Random rnd, int curr, int total, ObjectSignature signature)
-    {
-    // TODO
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
   public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total, ObjectSignature signature)
     {
-    switch(mType)
+    if( mTablebase!=null )
+      {
+      if( curr==0 ) mTablebase.scramble(rnd,total,scramble);
+      }
+    else
       {
-      case SCRAMBLING_ALGORITHMS: randomizeNewScramble0(scramble, rnd, curr); break;
-      case SCRAMBLING_SQUARE1   : randomizeNewScramble1(scramble, rnd, curr); break;
-      case SCRAMBLING_BANDAGED  : randomizeNewScramble2(scramble, rnd, curr, total, signature); break;
-      case SCRAMBLING_TABLEBASES: randomizeNewScramble3(scramble, rnd, curr, total, signature); break;
+      switch(mType)
+        {
+        case SCRAMBLING_ALGORITHMS: randomizeNewScramble0(scramble, rnd, curr); break;
+        case SCRAMBLING_SQUARE1   : randomizeNewScramble1(scramble, rnd, curr); break;
+        case SCRAMBLING_BANDAGED  : randomizeNewScramble2(scramble, rnd, curr, total, signature); break;
+        }
       }
     }
   }
diff --git a/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java b/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java
index c11deb8f..292f4ec6 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java
@@ -11,7 +11,7 @@ package org.distorted.objectlib.tablebases;
 
 import android.content.res.Resources;
 
-import org.distorted.objectlib.main.ObjectType;
+import org.distorted.objectlib.main.ObjectSignatures;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -20,16 +20,16 @@ import java.lang.reflect.InvocationTargetException;
 
 public enum ImplementedTablebasesList
 {
-  PYRAMINX_DUO  (ObjectType.PDUO_2, TablebasesPyraminxDuo.class),
-  IVY_CUBE      (ObjectType.IVY_2 , TablebasesIvyCube.class),
-  CU_232        (ObjectType.CU_232, TablebasesCuboid232.class),
-  PYRA_3        (ObjectType.PYRA_3, TablebasesPyraminx.class),
-  DIAMOND       (ObjectType.DIAM_2, TablebasesSkewbDiamond.class),
+  PYRAMINX_DUO  (ObjectSignatures.PDUO_2, TablebasesPyraminxDuo.class),
+  IVY_CUBE      (ObjectSignatures.IVY_2 , TablebasesIvyCube.class),
+  CU_232        (ObjectSignatures.CU_232, TablebasesCuboid232.class),
+  PYRA_3        (ObjectSignatures.PYRA_3, TablebasesPyraminx.class),
+  DIAMOND       (ObjectSignatures.DIAM_2, TablebasesSkewbDiamond.class),
   ;
 
   public static final int NUM_OBJECTS = values().length;
 
-  private final ObjectType mType;
+  private final int mSignature;
   private final Class<? extends TablebasesAbstract> mClass;
 
   private static final ImplementedTablebasesList[] objects;
@@ -47,37 +47,37 @@ public enum ImplementedTablebasesList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static boolean hasTablebase(ObjectType type)
+  public static boolean hasTablebase(int signature)
     {
     for(int i=0; i<NUM_OBJECTS; i++)
-      if( objects[i].mType == type ) return true;
+      if( objects[i].mSignature == signature ) return true;
 
     return false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  ImplementedTablebasesList(ObjectType type, final Class<? extends TablebasesAbstract> clazz)
+  ImplementedTablebasesList(int sig, final Class<? extends TablebasesAbstract> clazz)
     {
-    mType = type;
+    mSignature = sig;
     mClass= clazz;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public ObjectType getType()
+  public int getSignature()
     {
-    return mType;
+    return mSignature;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static TablebasesAbstract createPacked(Resources res, ObjectType type)
+  public static TablebasesAbstract createPacked(Resources res, int signature)
     {
     Class<? extends TablebasesAbstract> clazz=null;
 
     for(int i=0; i<NUM_OBJECTS; i++)
-      if( objects[i].mType == type )
+      if( objects[i].mSignature == signature )
         {
         clazz = objects[i].mClass;
         break;
@@ -117,12 +117,12 @@ public enum ImplementedTablebasesList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static TablebasesAbstract createUnpacked(ObjectType type)
+  public static TablebasesAbstract createUnpacked(int signatures)
     {
     Class<? extends TablebasesAbstract> clazz=null;
 
     for(int i=0; i<NUM_OBJECTS; i++)
-      if( objects[i].mType == type )
+      if( objects[i].mSignature == signatures )
         {
         clazz = objects[i].mClass;
         break;
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
index 5257c174..864459af 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
@@ -27,7 +27,7 @@ import java.util.Random;
 public abstract class TablebasesAbstract
 {
   private final Static3D[] mAxis;
-  private final int mSize;
+  private final int mSize, mMinScramble;
   private final int[][] mAngles;
   private final int mNumAxis;
   private final int[] mNumLayers;
@@ -56,6 +56,7 @@ public abstract class TablebasesAbstract
 
   abstract boolean[][] getRotatable();
 
+  abstract int getMinScramble();
   abstract int getSize();
   abstract int[] getQuats(int index);
   abstract int getIndex(int[] quats);
@@ -65,6 +66,7 @@ public abstract class TablebasesAbstract
   public TablebasesAbstract()
     {
     mSize = getSize();
+    mMinScramble = getMinScramble();
     mAngles = getBasicAngles();
     mAxis = getRotationAxis();
     mNumAxis = mAxis.length;
@@ -122,6 +124,21 @@ public abstract class TablebasesAbstract
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int computeRowFromBitmap(int rowBitmap)
+    {
+    int index = 0;
+
+    while(index<32)
+      {
+      if( (rowBitmap&0x1) != 0 ) return index;
+      rowBitmap>>=1;
+      index++;
+      }
+    return 0;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int computeRow(float[] pos, int quat, int axisIndex)
@@ -323,19 +340,6 @@ public abstract class TablebasesAbstract
     moves.add(move);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addMove(int[] moves, int axis, int layer, int angle)
-    {
-    int maxAngle = mAngles[axis][layer];
-    angle = maxAngle-angle;
-    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
-
-    moves[0] = axis;
-    moves[1] = (1<<layer);
-    moves[2] = angle;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int[][] convertMoves(ArrayList<int[]> moves)
@@ -446,76 +450,33 @@ public abstract class TablebasesAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int[][] scramble(Random rnd, int depth)
+  public void scramble(Random rnd, int depth, int[][] scramble)
     {
-    if( !mInitialized ) return null;
+    if( !mInitialized ) return;
 
-    int[] data = new int[4];
-    int level=0;
-    int[][] moves = new int[depth][3];
-    int[] quats = getQuats(0);
-    int numQuats = quats.length;
-    int[] tmpQuats = new int[numQuats];
+    int solDepth = 0;
+    int scraLen = scramble.length;
+    if( depth>mMinScramble ) depth = mMinScramble;
 
-    while(level<depth)
+    while( solDepth<depth )
       {
-      boolean found = false;
-
-      data[0]=0;
-      data[1]=0;
-      data[2]=1;
-      data[3]=1;
-
-      int random = rnd.nextInt(mScalingFactor);
-      for(int i=0; i<random; i++) getNextAxisLayerAngleQuat(data);
-
-      for(int ax=0; ax<mNumAxis; ax++)
-        for(int cubit=0; cubit<mNumCubits; cubit++)
-          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
+      int randomPosition = rnd.nextInt(mSize-1);
+      int[][] sol = solution(randomPosition);
+      solDepth = sol.length;
 
-      for(int s=0; s<mScalingFactor && !found; s++)
+      if( solDepth>=depth )
         {
-        int ax    = data[0];
-        int layer = data[1];
-        int angle = data[2];
-        int quat  = data[3];
+        int num = Math.min(scraLen,solDepth);
 
-        if( mRotatable[ax][layer] )
+        for(int i=0; i<num; i++)
           {
-          int bitLayer = (1<<layer);
-          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
-
-          for(int cubit=0; cubit<mNumCubits; cubit++)
-            if( mRotRow[cubit][ax]==bitLayer )
-              {
-              int currQuat = tmpQuats[cubit];
-              int newQuat = getMultQuat(quat,currQuat);
-              tmpQuats[cubit] = newQuat;
-              }
-
-          int childIndex = getIndex(tmpQuats);
-          byte newLevel = mTablebase.retrievePacked(childIndex);
-
-          if( ((newLevel-level-1)%3) == 0 )
-            {
-            addMove(moves[level],ax,layer,angle);
-            level++;
-            quats = getQuats(childIndex);
-            found = true;
-            }
+          int[] source = sol[solDepth-1-i];
+          scramble[i][0] = source[0];
+          scramble[i][1] = computeRowFromBitmap(source[1]);
+          scramble[i][2] =-source[2];
           }
-
-        getNextAxisLayerAngleQuat(data);
-        }
-
-      if( !found )
-        {
-        android.util.Log.e("D", "scramble error: no move found!");
-        return null;
         }
       }
-
-    return moves;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesCuboid232.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesCuboid232.java
index 0ea3f145..71e049de 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesCuboid232.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesCuboid232.java
@@ -110,6 +110,13 @@ public class TablebasesCuboid232 extends TablebasesAbstract
     return 241920;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ int getMinScramble()
+    {
+    return 12;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void getCornerQuats(int[] output, int[] perm)
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesIvyCube.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesIvyCube.java
index c7744388..f8f674d2 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesIvyCube.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesIvyCube.java
@@ -112,6 +112,13 @@ public class TablebasesIvyCube extends TablebasesAbstract
     return (3*3*3*3)*(6*5*4*3);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ int getMinScramble()
+    {
+    return 7;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int findFirst(int[] table, int value)
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminx.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminx.java
index 041e341d..edd97753 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminx.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminx.java
@@ -112,6 +112,13 @@ public class TablebasesPyraminx extends TablebasesAbstract
     return 933120;  // see https://www.jaapsch.net/puzzles/pyraminx.htm
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ int getMinScramble()
+    {
+    return 9;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public static void getEdgePermutation(int[] output, int[] quats, int index)
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminxDuo.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminxDuo.java
index 29447eea..cc44f8b4 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminxDuo.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminxDuo.java
@@ -99,6 +99,13 @@ public class TablebasesPyraminxDuo extends TablebasesAbstract
     return 324;  // see https://www.jaapsch.net/puzzles/pyraduo.htm
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ int getMinScramble()
+    {
+    return 4;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int[] getQuats(int index)
diff --git a/src/main/java/org/distorted/objectlib/tablebases/TablebasesSkewbDiamond.java b/src/main/java/org/distorted/objectlib/tablebases/TablebasesSkewbDiamond.java
index c9d17a40..fc187a74 100644
--- a/src/main/java/org/distorted/objectlib/tablebases/TablebasesSkewbDiamond.java
+++ b/src/main/java/org/distorted/objectlib/tablebases/TablebasesSkewbDiamond.java
@@ -124,6 +124,13 @@ public class TablebasesSkewbDiamond extends TablebasesAbstract
     return 138240;  // see https://www.jaapsch.net/puzzles/diamond.htm
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ int getMinScramble()
+    {
+    return 8;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int computeCenterQuat(int originalLocation, int newLocation)
