Project

General

Profile

« Previous | Next » 

Revision 05fa94d9

Added by Leszek Koltunski almost 5 years ago

RubikCube: detect if the cube is solved.

In order to do this correctly, we also needed to keep correcting each mQuatScramble quaternions after each quatMultiplication in order to avoid multiplication errors accumulating. This turns out to be easy, because each quaternion representing a legal combination of rotations of a RubikCube must have each of the 4 to its components be equal to one of only 7 possible floats.

View differences:

src/main/java/org/distorted/magic/RubikCube.java
53 53
    private static final Static3D VectY = new Static3D(0,1,0);
54 54
    private static final Static3D VectZ = new Static3D(0,0,1);
55 55

  
56
    private static final float SQ2 = 0.5f*((float)Math.sqrt(2));
57
    private static final float[] LEGAL = { 0.0f , 0.5f , -0.5f , 1.0f , -1.0f , SQ2 , -SQ2 };
58

  
56 59
    public static final int VECTX = 0;  //
57 60
    public static final int VECTY = 1;  // don't change this
58 61
    public static final int VECTZ = 2;  //
......
185 188
            }
186 189
      }
187 190

  
191
///////////////////////////////////////////////////////////////////////////////////////////////////
192

  
193
    boolean isSolved()
194
      {
195
      Static4D q = mQuatScramble[0][0][0];
196

  
197
      float x = q.get1();
198
      float y = q.get2();
199
      float z = q.get3();
200
      float w = q.get4();
201

  
202
      for(int i = 0; i< mSize; i++)
203
        for(int j = 0; j< mSize; j++)
204
          for(int k = 0; k< mSize; k++)
205
            {
206
            if( i==0 || i==mSize-1 || j==0 || j==mSize-1 || k==0 || k==mSize-1 )
207
              {
208
              q = mQuatScramble[i][j][k];
209

  
210
              if( q.get1()!=x || q.get2()!=y || q.get3()!=z || q.get4()!=w )
211
                {
212
                return false;
213
                }
214
              }
215
            }
216

  
217
      return true;
218
      }
219

  
188 220
///////////////////////////////////////////////////////////////////////////////////////////////////
189 221

  
190 222
    public void apply(Effect effect, int position)
......
498 530

  
499 531
                mRotationAngle[x][y][z].removeAll();
500 532
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
533
                normalizeScrambleQuat(x,y,z);
501 534
                modifyCurrentPosition(x,y,z,quat);
502 535
                }
503 536
              }
......
505 538
      mRotationAngleStatic.set1(0);
506 539
      }
507 540

  
541
///////////////////////////////////////////////////////////////////////////////////////////////////
542
// All legal rotation quats must have all four of their components equal to either
543
// 0, 1, -1, 0.5, -0.5 or +-sqrt(2)/2.
544
//
545
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
546
// correct the value of the 'scramble' quat to what it should be.
547
//
548
// We also have to remember that the group of unit quaternions is a double-cover of rotations
549
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
550

  
551
    private void normalizeScrambleQuat(int i, int j, int k)
552
      {
553
      Static4D quat = mQuatScramble[i][j][k];
554

  
555
      float x = quat.get1();
556
      float y = quat.get2();
557
      float z = quat.get3();
558
      float w = quat.get4();
559
      float diff;
560

  
561
      for(int legal=0; legal<LEGAL.length; legal++)
562
        {
563
        diff = x-LEGAL[legal];
564
        if( diff*diff<0.01f ) x = LEGAL[legal];
565
        diff = y-LEGAL[legal];
566
        if( diff*diff<0.01f ) y = LEGAL[legal];
567
        diff = z-LEGAL[legal];
568
        if( diff*diff<0.01f ) z = LEGAL[legal];
569
        diff = w-LEGAL[legal];
570
        if( diff*diff<0.01f ) w = LEGAL[legal];
571
        }
572

  
573
      if( w<0 )
574
        {
575
        w = -w;
576
        z = -z;
577
        y = -y;
578
        x = -x;
579
        }
580
      else if( w==0 )
581
        {
582
        if( z<0 )
583
          {
584
          z = -z;
585
          y = -y;
586
          x = -x;
587
          }
588
        else if( z==0 )
589
          {
590
          if( y<0 )
591
            {
592
            y = -y;
593
            x = -x;
594
            }
595
          else if( y==0 )
596
            {
597
            if( x<0 )
598
              {
599
              x = -x;
600
              }
601
            }
602
          }
603
        }
604

  
605
      mQuatScramble[i][j][k].set(x,y,z,w);
606
      }
607

  
508 608
///////////////////////////////////////////////////////////////////////////////////////////////////
509 609

  
510 610
    private float getSinkStrength()
src/main/java/org/distorted/magic/RubikRenderer.java
47 47
    private int mNextCubeSize, mScrambleCubeNum;
48 48
    private long mRotationFinishedID;
49 49
    private long[] mEffectID;
50
    private boolean mFinishRotation, mRemoveRotation, mFinishDragCurrent, mFinishDragAccumulated;
50
    private boolean mFinishRotation, mRemoveRotation, mSetQuatCurrent, mSetQuatAccumulated;
51 51
    private boolean mSizeChangeCube, mSolveCube, mScrambleCube;
52 52
    private boolean mCanRotate, mCanDrag, mCanUI;
53 53
    private RubikCube mOldCube, mNewCube;
......
68 68
      mScreenWidth = mScreenHeight = 0;
69 69
      mScrambleCubeNum = 0;
70 70

  
71
      mFinishRotation        = false;
72
      mRemoveRotation        = false;
73
      mFinishDragCurrent     = false;
74
      mFinishDragAccumulated = false;
75
      mSizeChangeCube        = false;
76
      mSolveCube             = false;
77
      mScrambleCube          = false;
71
      mFinishRotation     = false;
72
      mRemoveRotation     = false;
73
      mSetQuatCurrent     = false;
74
      mSetQuatAccumulated = false;
75
      mSizeChangeCube     = true;
76
      mSolveCube          = false;
77
      mScrambleCube       = false;
78 78

  
79 79
      mCanRotate   = true;
80 80
      mCanDrag     = true;
......
83 83
      mEffectID = new long[BaseEffect.Type.LENGTH];
84 84

  
85 85
      mMesh= new MeshFlat(20,20);
86
      mNextCubeSize =RubikActivity.getSize();
86
      mNextCubeSize = RubikActivity.getSize();
87 87
      }
88 88

  
89 89
///////////////////////////////////////////////////////////////////////////////////////////////////
......
95 95
      {
96 96
      mScreen.render( System.currentTimeMillis() );
97 97

  
98
      if( mFinishDragCurrent )
98
      if( mSetQuatCurrent )
99 99
        {
100
        mFinishDragCurrent = false;
100
        mSetQuatCurrent = false;
101 101
        mView.setQuatCurrent();
102 102
        }
103 103

  
104
      if( mFinishDragAccumulated )
104
      if( mSetQuatAccumulated )
105 105
        {
106
        mFinishDragAccumulated = false;
106
        mSetQuatAccumulated = false;
107 107
        mView.setQuatAccumulated();
108 108
        }
109 109

  
......
118 118
        {
119 119
        mRemoveRotation=false;
120 120
        mNewCube.removeRotationNow();
121

  
122
        if( mNewCube.isSolved() )
123
          {
124
          android.util.Log.e("renderer", "CUBE IS SOLVED NOW");
125
          }
126

  
121 127
        mCanRotate = true;
122 128
        }
123 129

  
......
127 133
        mCanDrag        = false;
128 134
        mCanRotate      = false;
129 135
        createCubeNow(mNextCubeSize);
130
        doEffectNow(0);
136
        doEffectNow( BaseEffect.Type.SIZECHANGE );
131 137
        }
132 138

  
133 139
      if( mSolveCube )
......
136 142
        mCanDrag     = false;
137 143
        mCanRotate   = false;
138 144
        mCanUI       = false;
139
        doEffectNow(1);
145
        doEffectNow( BaseEffect.Type.SOLVE );
140 146
        }
141 147

  
142 148
      if( mScrambleCube )
......
145 151
        mCanDrag      = false;
146 152
        mCanRotate    = false;
147 153
        mCanUI        = false;
148
        doEffectNow(2);
154
        doEffectNow( BaseEffect.Type.SCRAMBLE );
149 155
        }
150 156
      }
151 157

  
......
296 302
///////////////////////////////////////////////////////////////////////////////////////////////////
297 303
// do all 'adjustable' effects (SizeChange, Solve, Scramble)
298 304

  
299
   private void doEffectNow(int index)
305
   private void doEffectNow(BaseEffect.Type type)
300 306
     {
301
     mEffectID[index] = BaseEffect.Type.getType(index).startEffect(this);
307
     int index = type.ordinal();
308

  
309
     mEffectID[index] = type.startEffect(this);
302 310

  
303 311
     if( mEffectID[index] == -1 )
304 312
       {
......
361 369

  
362 370
   void setQuatCurrentOnNextRender()
363 371
     {
364
     mFinishDragCurrent = true;
372
     mSetQuatCurrent = true;
365 373
     }
366 374

  
367 375
///////////////////////////////////////////////////////////////////////////////////////////////////
368 376

  
369 377
   void setQuatAccumulatedOnNextRender()
370 378
     {
371
     mFinishDragAccumulated = true;
379
     mSetQuatAccumulated = true;
372 380
     }
373 381
}

Also available in: Unified diff