commit aa171dee7c0bafd8a53f8c8a9d253e48349322bc
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Mar 30 00:41:37 2020 +0100

    More support for actual patterns!

diff --git a/src/main/java/org/distorted/dialog/RubikDialogPatternView.java b/src/main/java/org/distorted/dialog/RubikDialogPatternView.java
index 79ea37bb..62072455 100644
--- a/src/main/java/org/distorted/dialog/RubikDialogPatternView.java
+++ b/src/main/java/org/distorted/dialog/RubikDialogPatternView.java
@@ -150,9 +150,11 @@ public class RubikDialogPatternView extends FrameLayout implements AdapterView.O
         public void onClick(View view)
           {
           RubikStatePattern state = (RubikStatePattern) RubikState.PATT.getStateClass();
-          state.setPattern(mTab, category, ii);
+          state.setPattern(act, mTab, category, ii);
           int[] sizes = RubikObjectList.CUBE.getSizes();
-          act.changeObject(RubikObjectList.CUBE,sizes[mTab]);
+          RubikPattern pattern = RubikPattern.getInstance();
+          String moves = pattern.getMoves(mTab, category, ii);
+          act.changeObject(RubikObjectList.CUBE,sizes[mTab],moves);
           mDialog.rememberCategories();
           mDialog.dismiss();
           }
diff --git a/src/main/java/org/distorted/magic/RubikActivity.java b/src/main/java/org/distorted/magic/RubikActivity.java
index bea57a18..6b28be51 100644
--- a/src/main/java/org/distorted/magic/RubikActivity.java
+++ b/src/main/java/org/distorted/magic/RubikActivity.java
@@ -31,6 +31,7 @@ import org.distorted.dialog.RubikDialogEffects;
 import org.distorted.effect.BaseEffect;
 import org.distorted.library.main.DistortedLibrary;
 
+import org.distorted.object.RubikObject;
 import org.distorted.scores.RubikScores;
 import org.distorted.scores.RubikScoresDownloader;
 import org.distorted.object.RubikObjectList;
@@ -101,7 +102,7 @@ public class RubikActivity extends AppCompatActivity
         if( sizeIndex>=0 && sizeIndex<sizes.length )
           {
           success = true;
-          view.getRenderer().createObject( obj, size );
+          view.getRenderer().changeObject( obj, size, null );
           }
 
         }
@@ -112,7 +113,7 @@ public class RubikActivity extends AppCompatActivity
         int s = RubikStatePlay.DEF_SIZE;
 
         play.setObjectAndSize(obj,s);
-        view.getRenderer().createObject(obj,s);
+        view.getRenderer().changeObject(obj,s, null);
         }
       }
     
@@ -175,14 +176,23 @@ public class RubikActivity extends AppCompatActivity
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void changeObject(RubikObjectList object, int size)
+    public RubikObject getObject()
+      {
+      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
+      RubikRenderer renderer = view.getRenderer();
+      return renderer.getObject();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void changeObject(RubikObjectList object, int size, String moves)
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
       RubikRenderer renderer = view.getRenderer();
 
       if( renderer.canDrag() )
         {
-        renderer.createObject(object,size);
+        renderer.changeObject(object,size,moves);
         }
       }
 
diff --git a/src/main/java/org/distorted/magic/RubikRenderer.java b/src/main/java/org/distorted/magic/RubikRenderer.java
index aa038263..de4e72b6 100644
--- a/src/main/java/org/distorted/magic/RubikRenderer.java
+++ b/src/main/java/org/distorted/magic/RubikRenderer.java
@@ -61,6 +61,7 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
     private RubikObject mOldObject, mNewObject;
     private int mScreenWidth, mScreenHeight;
     private SharedPreferences mPreferences;
+    private String mNextMoves;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -92,14 +93,14 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private void createObjectNow(RubikObjectList object, int size)
+   private void createObjectNow(RubikObjectList object, int size, String moves)
      {
      boolean firstTime = (mNewObject==null);
 
      if( mOldObject!=null ) mOldObject.releaseResources();
      mOldObject = mNewObject;
 
-     mNewObject = object.create(size, mView.getQuatCurrent(), mView.getQuatAccumulated());
+     mNewObject = object.create(size, mView.getQuatCurrent(), mView.getQuatAccumulated(), moves);
      mNewObject.createTexture();
      mView.setMovement(object.getObjectMovementClass());
 
@@ -161,13 +162,14 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   void createObject(RubikObjectList object, int size)
+   void changeObject(RubikObjectList object, int size, String moves)
      {
-     if( (object!=mNextObject || mNextSize!=size) && size>0 )
+     if( size>0 )
        {
        mChangeObject = true;
        mNextObject = object;
        mNextSize   = size;
+       mNextMoves  = moves;
        }
      }
 
@@ -192,13 +194,6 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
        }
      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void setCanRotate(boolean can)
-     {
-     mCanRotate = can;
-     }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    boolean canRotate()
@@ -307,8 +302,27 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
        mCanDrag      = false;
        mCanRotate    = false;
        mCanUI        = false;
-       createObjectNow(mNextObject, mNextSize);
-       doEffectNow( BaseEffect.Type.SIZECHANGE );
+
+       if( mNewObject==null )
+         {
+         createObjectNow(mNextObject, mNextSize, mNextMoves);
+         doEffectNow( BaseEffect.Type.SIZECHANGE );
+         }
+       else
+         {
+         RubikObjectList list = mNewObject.getObjectList();
+         int size = mNewObject.getSize();
+
+         if (list!=mNextObject || mNextSize!=size)
+           {
+           createObjectNow(mNextObject, mNextSize, mNextMoves);
+           doEffectNow( BaseEffect.Type.SIZECHANGE );
+           }
+         else
+           {
+           mNewObject.initializeObject(mNextMoves);
+           }
+         }
        }
 
      if( mSolveObject )
diff --git a/src/main/java/org/distorted/object/RubikCube.java b/src/main/java/org/distorted/object/RubikCube.java
index ddb0a545..40451737 100644
--- a/src/main/java/org/distorted/object/RubikCube.java
+++ b/src/main/java/org/distorted/object/RubikCube.java
@@ -78,9 +78,9 @@ class RubikCube extends RubikObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  RubikCube(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects)
+  RubikCube(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects, String moves)
     {
-    super(size, 60, quatCur,quatAcc,texture,mesh,effects);
+    super(size, 60, quatCur,quatAcc,texture,mesh,effects,moves, RubikObjectList.CUBE);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/object/RubikObject.java b/src/main/java/org/distorted/object/RubikObject.java
index 39693027..f372afdb 100644
--- a/src/main/java/org/distorted/object/RubikObject.java
+++ b/src/main/java/org/distorted/object/RubikObject.java
@@ -60,6 +60,7 @@ public abstract class RubikObject extends DistortedNode
   private Static4D mQuatAccumulated;
   private Cubit[] mCubits;
   private int mSize;
+  private RubikObjectList mList;
 
   float mStart, mStep;
 
@@ -73,12 +74,14 @@ public abstract class RubikObject extends DistortedNode
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  RubikObject(int size, int fov, Static4D quatCur, Static4D quatAcc, DistortedTexture nodeTexture, MeshRectangles nodeMesh, DistortedEffects nodeEffects)
+  RubikObject(int size, int fov, Static4D quatCur, Static4D quatAcc, DistortedTexture nodeTexture,
+              MeshRectangles nodeMesh, DistortedEffects nodeEffects, String moves, RubikObjectList list)
     {
     super(nodeTexture,nodeEffects,nodeMesh);
 
     resizeFBO(NODE_FBO_SIZE, NODE_FBO_SIZE);
 
+    mList = list;
     mOrigPos = getCubitPositions(size);
 
     LEGAL_QUATS = getLegalQuats();
@@ -122,6 +125,8 @@ public abstract class RubikObject extends DistortedNode
       attach(mCubits[i].mNode);
       }
 
+    setupPosition(moves);
+
     setProjection(fov, 0.1f);
     }
 
@@ -214,6 +219,107 @@ public abstract class RubikObject extends DistortedNode
     rotationAngle.add(mRotationAngleFinal);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+	private boolean scheduleMultiRotation(int rotV, int rotRC, int rotA)
+	  {
+	  /*
+		int index, tmp;
+
+		if( numRotations>0 )
+		  {
+			if( rotVector!=rotV ) return false;
+
+			index=0;
+			tmp=rotRC;
+
+			while( tmp!=0 )
+			  {
+				if( (tmp&0x1)==1 && rotAngle[index]!=0 ) return false;
+				index++;
+				tmp/=2;
+			  }
+		  }
+
+		index=0;
+		tmp=rotRC;
+		rotVector = rotV;
+
+		while( tmp!=0 )
+		  {
+			if( (tmp&0x1)==1 )
+			  {
+				rotAngle[index] = rotA*90;
+				rotSpeed[index] = (int)(rotS/RubikWorld.hardwareSpeed);
+
+				if( rotSpeed[index]<=0 && rotS>0 ) rotSpeed[index] = 1;
+				if( rotSpeed[index]>=0 && rotS<0 ) rotSpeed[index] =-1;
+
+				numRotations++;
+			  }
+
+			index++;
+			tmp/=2;
+		  }
+    */
+		return true;
+	  }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  private void setupPosition(String moves)
+    {
+    android.util.Log.e("object", "initializing: "+moves);
+/*
+    int index,tmp, a1,a2,a3, rv, rc, ra;
+
+		numRotations=0;
+		initializeVertices(mSize);
+
+		int len=moves.length()/4;
+
+		for(int i=0; i<len; i++)
+		  {
+			a1=moves.charAt(4*i+1)-'0';
+			a2=moves.charAt(4*i+2)-'0';
+			a3=moves.charAt(4*i+3)-'0';
+
+			rv = (10*a1+a2)/32;
+			rc = (10*a1+a2)%32;
+			ra = 2-a3;
+
+			rotVector = rv;
+
+			tmp=rc;
+			index=0;
+
+			while( tmp!=0 )
+			  {
+				if( (tmp&0x1)==1 )
+				  {
+					rotAngle[index] = ra*90;
+					RubikWorld.setIdentity(rotMat[index]);
+
+					switch(rotVector)
+					  {
+					  case VECTX: RubikWorld.postRotate( rotMat[index], (float)((ra*90)%360), 1f,0f,0f); break;
+					  case VECTY: RubikWorld.postRotate( rotMat[index], (float)((ra*90)%360), 0f,1f,0f); break;
+					  case VECTZ: RubikWorld.postRotate( rotMat[index], (float)((ra*90)%360), 0f,0f,1f); break;
+					  }
+
+					transposeTiles(index);
+					rotAngle[index]=0;
+				  }
+
+				tmp/=2;
+				index++;
+			  }
+	  	}
+ */
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Clamp all rotated positions to one of those original ones to avoid accumulating errors.
 
@@ -461,6 +567,51 @@ public abstract class RubikObject extends DistortedNode
      mRotationAngleStatic.set0(0);
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean makeMove(String move)
+    {
+    int a1=move.charAt(1)-'0';
+		int a2=move.charAt(2)-'0';
+		int a3=move.charAt(3)-'0';
+
+    int rotVector = (10*a1+a2)/32; // 0 --> VECTX, 1--> VECTY, 2 --> VECTZ
+    int rotBitmap = (10*a1+a2)%32; // 01010 --> move the 2nd and 4th layer
+    int rotAngle  = 2-a3;          // counterclockwise by rotAngle*90 degrees
+
+		return scheduleMultiRotation(rotVector, rotBitmap, rotAngle);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean backMove(String move)
+    {
+    int a1=move.charAt(1)-'0';
+		int a2=move.charAt(2)-'0';
+		int a3=move.charAt(3)-'0';
+
+    int rotVector = (10*a1+a2)/32;
+    int rotBitmap = (10*a1+a2)%32;
+    int rotAngle  = a3-2;
+
+		return scheduleMultiRotation(rotVector, rotBitmap, rotAngle);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void initializeObject(String moves)
+    {
+    solve();
+    setupPosition(moves);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikObjectList getObjectList()
+    {
+    return mList;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   abstract float getScreenRatio();
diff --git a/src/main/java/org/distorted/object/RubikObjectList.java b/src/main/java/org/distorted/object/RubikObjectList.java
index c731f1d2..588aa3f6 100644
--- a/src/main/java/org/distorted/object/RubikObjectList.java
+++ b/src/main/java/org/distorted/object/RubikObjectList.java
@@ -245,7 +245,7 @@ public enum RubikObjectList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public RubikObject create(int size, Static4D quatCur, Static4D quatAcc)
+  public RubikObject create(int size, Static4D quatCur, Static4D quatAcc, String moves)
     {
     DistortedTexture texture = new DistortedTexture();
     DistortedEffects effects = new DistortedEffects();
@@ -253,8 +253,8 @@ public enum RubikObjectList
 
     switch(ordinal())
       {
-      case 0: return new RubikCube    (size, quatCur, quatAcc, texture, mesh, effects);
-      case 1: return new RubikPyraminx(size, quatCur, quatAcc, texture, mesh, effects);
+      case 0: return new RubikCube    (size, quatCur, quatAcc, texture, mesh, effects, moves);
+      case 1: return new RubikPyraminx(size, quatCur, quatAcc, texture, mesh, effects, moves);
       }
 
     return null;
diff --git a/src/main/java/org/distorted/object/RubikPyraminx.java b/src/main/java/org/distorted/object/RubikPyraminx.java
index 126fef72..64395f41 100644
--- a/src/main/java/org/distorted/object/RubikPyraminx.java
+++ b/src/main/java/org/distorted/object/RubikPyraminx.java
@@ -83,9 +83,9 @@ public class RubikPyraminx extends RubikObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  RubikPyraminx(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects)
+  RubikPyraminx(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects, String moves)
     {
-    super(size, 30, quatCur,quatAcc,texture,mesh,effects);
+    super(size, 30, quatCur,quatAcc,texture,mesh,effects,moves, RubikObjectList.PYRA);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/patterns/RubikPattern.java b/src/main/java/org/distorted/patterns/RubikPattern.java
index dcfdf321..01b9c453 100644
--- a/src/main/java/org/distorted/patterns/RubikPattern.java
+++ b/src/main/java/org/distorted/patterns/RubikPattern.java
@@ -19,6 +19,8 @@
 
 package org.distorted.patterns;
 
+import org.distorted.object.RubikObject;
+
 import java.util.Vector;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -111,38 +113,34 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    String retInitializationString(int pattern)
+    void makeMove(RubikObject object, int pattern)
       {
       if( pattern>=0 && pattern<numPatterns )
         {
         Pattern p = patterns.elementAt(pattern);
-        if( p!=null ) return p.retInitializationString();
+        if( p!=null ) p.makeMove(object);
         }
-
-      return "";
       }
 
   /////////////////////////////////////////////////////////////
 
-    String retNextMove(int pattern)
+    void backMove(RubikObject object, int pattern)
       {
       if( pattern>=0 && pattern<numPatterns )
         {
         Pattern p = patterns.elementAt(pattern);
-        if( p!=null ) return p.retNextMove();
+        if( p!=null ) p.backMove(object);
         }
-
-      return "";
       }
 
   /////////////////////////////////////////////////////////////
 
-    String retPrevMove(int pattern)
+    String getMoves(int pattern)
       {
       if( pattern>=0 && pattern<numPatterns )
         {
         Pattern p = patterns.elementAt(pattern);
-        if( p!=null ) return p.retPrevMove();
+        if( p!=null ) return p.getMoves();
         }
 
       return "";
@@ -191,43 +189,47 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    String retNextMove()
+    void makeMove(RubikObject object)
       {
       curMove++;
 
       if( curMove>numMove)
         {
         curMove= 0;
-        return retInitializationString();
+        object.initializeObject(moves.substring(0,4*curMove));
         }
       else
         {
-        curMove--;
-        return moves.substring(4*curMove-4,4*curMove);
+        if( !object.makeMove(moves.substring(4*curMove-4,4*curMove)) )
+          {
+          curMove--;
+          }
         }
       }
 
   /////////////////////////////////////////////////////////////
 
-    String retPrevMove()
+    void backMove(RubikObject object)
       {
       curMove--;
 
       if( curMove<0)
         {
         curMove=numMove;
-        return retInitializationString();
+        object.initializeObject(moves.substring(0,4*curMove));
         }
       else
         {
-        curMove++;
-        return moves.substring(4*curMove,4*curMove+4);
+        if( !object.backMove(moves.substring(4*curMove,4*curMove+4)) )
+          {
+          curMove++;
+          }
         }
       }
 
   /////////////////////////////////////////////////////////////
 
-    String retInitializationString()
+    String getMoves()
       {
       return moves.substring(0,4*curMove);
       }
@@ -382,38 +384,34 @@ public class RubikPattern
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public String retNextMove(int size, int cat, int pat)
+  public void makeMove(RubikObject object, int size, int cat, int pat)
     {
     if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
       {
       Category c = mCategories[size].elementAt(cat);
-      if( c!=null ) return c.retNextMove(pat);
+      if( c!=null ) c.makeMove(object,pat);
       }
-
-    return "";
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public String retPrevMove(int size, int cat, int pat)
+  public void backMove(RubikObject object, int size, int cat, int pat)
     {
     if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
       {
       Category c = mCategories[size].elementAt(cat);
-      if( c!=null ) return c.retPrevMove(pat);
+      if( c!=null ) c.backMove(object,pat);
       }
-
-    return "";
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public String retInitializationString(int size,int cat, int pat)
+  public String getMoves(int size, int cat, int pat)
     {
     if( size>=0 && size<NUM_CUBES && cat>=0 && cat< numCategories[size] )
       {
       Category c = mCategories[size].elementAt(cat);
-      if( c!=null ) return c.retInitializationString(pat);
+      if( c!=null ) return c.getMoves(pat);
       }
 
     return "";
diff --git a/src/main/java/org/distorted/uistate/RubikStatePattern.java b/src/main/java/org/distorted/uistate/RubikStatePattern.java
index 11e70d91..ce95d3d3 100644
--- a/src/main/java/org/distorted/uistate/RubikStatePattern.java
+++ b/src/main/java/org/distorted/uistate/RubikStatePattern.java
@@ -34,6 +34,7 @@ import android.widget.TextView;
 import org.distorted.dialog.RubikDialogPattern;
 import org.distorted.magic.R;
 import org.distorted.magic.RubikActivity;
+import org.distorted.object.RubikObject;
 import org.distorted.object.RubikObjectList;
 import org.distorted.patterns.RubikPattern;
 
@@ -48,7 +49,8 @@ public class RubikStatePattern extends RubikStateAbstract
   private Button mBackButton;
   private ImageButton mPrevButton, mNextButton;
   private TextView mMovesText;
-  private int mSize;
+  private int mNumMoves, mCurrMove;
+  private int mSizeIndex, mCategory, mPattern;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -62,13 +64,14 @@ public class RubikStatePattern extends RubikStateAbstract
   void leaveState(RubikActivity act)
     {
     RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
+    int s = RubikObjectList.CUBE.getSizes()[mSizeIndex];
 
-    if( !play.setObjectAndSize(RubikObjectList.CUBE, mSize) )
+    if( !play.setObjectAndSize(RubikObjectList.CUBE, s) )
       {
       int object= play.getObject();
       int size  = play.getSize();
 
-      act.changeObject(RubikObjectList.getObject(object),size);
+      act.changeObject(RubikObjectList.getObject(object),size,null);
       }
     }
 
@@ -82,12 +85,12 @@ public class RubikStatePattern extends RubikStateAbstract
 
     if( size>=RubikPattern.MIN_CUBE && size<=RubikPattern.MAX_CUBE && obj==RubikObjectList.CUBE.ordinal() )
       {
-      mSize = size;
+      mSizeIndex = RubikObjectList.getSizeIndex(obj,size);
       }
     else
       {
-      mSize = RubikStatePlay.DEF_SIZE;
-      act.changeObject(RubikObjectList.CUBE,mSize);
+      mSizeIndex = RubikObjectList.getSizeIndex(RubikObjectList.CUBE.ordinal(),RubikStatePlay.DEF_SIZE);
+      act.changeObject(RubikObjectList.CUBE,RubikStatePlay.DEF_SIZE,null);
       }
 
     FragmentManager mana = act.getSupportFragmentManager();
@@ -131,8 +134,7 @@ public class RubikStatePattern extends RubikStateAbstract
     {
     Bundle bundle = new Bundle();
     int object = RubikObjectList.CUBE.ordinal();
-    int sizeIndex = RubikObjectList.getSizeIndex(object,mSize);
-    bundle.putInt("tab", RubikObjectList.pack(object,sizeIndex) );
+    bundle.putInt("tab", RubikObjectList.pack(object,mSizeIndex) );
 
     RubikDialogPattern diag = new RubikDialogPattern();
     diag.setArguments(bundle);
@@ -190,7 +192,11 @@ public class RubikStatePattern extends RubikStateAbstract
       @Override
       public void onClick(View v)
         {
-        android.util.Log.e("patt", "prev button clicked!");
+        if( --mCurrMove< 0 ) mCurrMove=mNumMoves;
+
+        mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
+        RubikObject object = act.getObject();
+        RubikPattern.getInstance().backMove( object, mSizeIndex, mCategory, mPattern);
         }
       });
     }
@@ -211,7 +217,11 @@ public class RubikStatePattern extends RubikStateAbstract
       @Override
       public void onClick(View v)
         {
-        android.util.Log.e("patt", "next button clicked!");
+        if( ++mCurrMove> mNumMoves ) mCurrMove=0;
+
+        mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
+        RubikObject object = act.getObject();
+        RubikPattern.getInstance().makeMove( object, mSizeIndex, mCategory, mPattern);
         }
       });
     }
@@ -224,22 +234,30 @@ public class RubikStatePattern extends RubikStateAbstract
     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,2.0f);
 
     mMovesText = new TextView(act);
+    mMovesText.setTextSize(20);
     mMovesText.setLayoutParams(params);
     mMovesText.setPadding(padding,0,padding,0);
     mMovesText.setGravity(Gravity.CENTER);
 
-    mMovesText.setText("aaa");
+    mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void setPattern(int sizeIndex, int category, int pattern)
+  public void setPattern(final RubikActivity act, int sizeIndex, int category, int pattern)
     {
-    mSize = RubikObjectList.CUBE.getSizes()[sizeIndex];
+    mSizeIndex = sizeIndex;
+    mCategory  = category;
+    mPattern   = pattern;
 
     RubikPattern patt = RubikPattern.getInstance();
     String patternName = patt.getPatternName(sizeIndex,category,pattern);
     mText.setText(patternName);
+
+    mNumMoves = patt.getNumMoves(sizeIndex,category,pattern);
+    mCurrMove = patt.getCurMove(sizeIndex,category,pattern);
+
+    mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/uistate/RubikStatePlay.java b/src/main/java/org/distorted/uistate/RubikStatePlay.java
index 9c11acb3..e07b6355 100644
--- a/src/main/java/org/distorted/uistate/RubikStatePlay.java
+++ b/src/main/java/org/distorted/uistate/RubikStatePlay.java
@@ -197,7 +197,7 @@ public class RubikStatePlay extends RubikStateAbstract
             {
             mObject = obj;
             mSize   = sizes[size];
-            act.changeObject(list,sizes[size]);
+            act.changeObject(list,sizes[size],null);
             mPopup.dismiss();
             }
           });
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index d27d321f..a29451c8 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -41,4 +41,5 @@
     <string name="tm_placeholder">%1$02d:%2$02d</string>
     <string name="ap_placeholder">%1$s %2$s</string>
     <string name="ti_placeholder">%1$.1f seconds</string>
+    <string name="mo_placeholder">%1$d/%2$d</string>
 </resources>
