Project

General

Profile

Download (20.8 KB) Statistics
| Branch: | Revision:

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObjectSolved.java @ 7e8750c9

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2023 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.objectlib.main;
11

    
12
import org.distorted.library.helpers.QuatHelper;
13
import org.distorted.library.type.Static3D;
14
import org.distorted.library.type.Static4D;
15
import org.distorted.objectlib.helpers.ObjectShape;
16

    
17
import java.util.ArrayList;
18

    
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
class TwistyObjectSolved
22
  {
23
  private final TwistyObject mParent;
24
  private final Static4D[] mObjectQuats;
25
  private final int mNumQuats;
26
  private final float[][] mOrigPos;
27
  private final int mNumCubits;
28
  private final int mFunctionIndex;
29
  private final int[] mTmpQuats;
30

    
31
  private int[][] mSolvedQuats;
32
  private int[][] mQuatMult;
33
  private int[] mFaceMap;
34
  private int[][] mScramble;
35
  private int[] mColors;
36

    
37
  private static class SurfaceInfo
38
    {
39
    float[] surface;
40
    int[] indices;
41

    
42
    SurfaceInfo(float[] s) { surface = s; }
43
    void setIndices(int[] i) { indices = i; }
44
    };
45

    
46
  private int[][] mSurfaceTable;
47
  private int[][] mFaceColorTable;
48
  private int[][] mCubitFaceToSurfaceMap;
49
  private int[][] mCubitFaceColor;
50

    
51
///////////////////////////////////////////////////////////////////////////////////////////////////
52
// remember about the double cover or unit quaternions!
53

    
54
  private int mulQuat(int q1, int q2)
55
    {
56
    Static4D result = QuatHelper.quatMultiply(mObjectQuats[q1],mObjectQuats[q2]);
57

    
58
    float rX = result.get0();
59
    float rY = result.get1();
60
    float rZ = result.get2();
61
    float rW = result.get3();
62

    
63
    final float MAX_ERROR = 0.1f;
64
    float dX,dY,dZ,dW;
65

    
66
    for(int i=0; i<mNumQuats; i++)
67
      {
68
      dX = mObjectQuats[i].get0() - rX;
69
      dY = mObjectQuats[i].get1() - rY;
70
      dZ = mObjectQuats[i].get2() - rZ;
71
      dW = mObjectQuats[i].get3() - rW;
72

    
73
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
74
          dY<MAX_ERROR && dY>-MAX_ERROR &&
75
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
76
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
77

    
78
      dX = mObjectQuats[i].get0() + rX;
79
      dY = mObjectQuats[i].get1() + rY;
80
      dZ = mObjectQuats[i].get2() + rZ;
81
      dW = mObjectQuats[i].get3() + rW;
82

    
83
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
84
          dY<MAX_ERROR && dY>-MAX_ERROR &&
85
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
86
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
87
      }
88

    
89
    return -1;
90
    }
91

    
92
///////////////////////////////////////////////////////////////////////////////////////////////////
93

    
94
  private int getMultQuat(int index1, int index2)
95
    {
96
    if( mQuatMult==null )
97
      {
98
      mQuatMult = new int[mNumQuats][mNumQuats];
99

    
100
      for(int i=0; i<mNumQuats; i++)
101
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
102
      }
103

    
104
    if( index1<mNumQuats && index2<mNumQuats )
105
      {
106
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2);
107
      return mQuatMult[index1][index2];
108
      }
109

    
110
    return -1;
111
    }
112

    
113
///////////////////////////////////////////////////////////////////////////////////////////////////
114

    
115
  private int computeScramble(int quatNum, int centerNum)
116
    {
117
    float MAXDIFF = 0.01f;
118
    float[] center= mOrigPos[centerNum];
119
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
120
    Static4D result = QuatHelper.rotateVectorByQuat(sc,mObjectQuats[quatNum]);
121

    
122
    float x = result.get0();
123
    float y = result.get1();
124
    float z = result.get2();
125

    
126
    for(int c=0; c<mNumCubits; c++)
127
      {
128
      float[] cent = mOrigPos[c];
129

    
130
      float qx = cent[0] - x;
131
      float qy = cent[1] - y;
132
      float qz = cent[2] - z;
133

    
134
      if( qx>-MAXDIFF && qx<MAXDIFF &&
135
          qy>-MAXDIFF && qy<MAXDIFF &&
136
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
137
      }
138

    
139
    return -1;
140
    }
141

    
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143
// This is used to build internal data structures for the generic 'isSolved()'
144
//
145
// if this is an internal cubit (all faces black): return -1
146
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
147
// Color index, i.e. the index into the 'FACE_COLORS' table.
148
// else (edge or corner cubit, more than one non-black face): return -2.
149

    
150
  private int retCubitSolvedStatus(int cubit, int[] numLayers, int numCubitFaces, int[][] cubitFaceColors)
151
    {
152
    int numNonBlack=0, nonBlackIndex=-1, stiShape, cubColor;
153
    int variant = mParent.getCubitVariant(cubit,numLayers);
154

    
155
    for(int face=0; face<numCubitFaces; face++)
156
      {
157
      stiShape = mParent.getVariantStickerShape(variant,face);
158
      int numFaces = cubitFaceColors[cubit].length;
159
      cubColor = face<numFaces ? cubitFaceColors[cubit][face] : -1;
160

    
161
      if( stiShape>=0 && cubColor>=0 )
162
        {
163
        numNonBlack++;
164
        nonBlackIndex = cubColor;
165
        }
166
      }
167

    
168
    if( numNonBlack==0 ) return -1;
169
    if( numNonBlack>=2 ) return -2;
170

    
171
    return nonBlackIndex;
172
    }
173

    
174
///////////////////////////////////////////////////////////////////////////////////////////////////
175

    
176
  private int[] buildSolvedQuats(Static3D faceAx)
177
    {
178
    final float MAXD = 0.0001f;
179
    float x = faceAx.get0();
180
    float y = faceAx.get1();
181
    float z = faceAx.get2();
182
    float a,dx,dy,dz,qx,qy,qz;
183
    Static4D quat;
184
    int place = 0;
185

    
186
    for(int q=1; q<mNumQuats; q++)
187
      {
188
      quat = mObjectQuats[q];
189
      qx = quat.get0();
190
      qy = quat.get1();
191
      qz = quat.get2();
192

    
193
           if( x!=0.0f ) { a = qx/x; }
194
      else if( y!=0.0f ) { a = qy/y; }
195
      else               { a = qz/z; }
196

    
197
      dx = a*x-qx;
198
      dy = a*y-qy;
199
      dz = a*z-qz;
200

    
201
      if( dx>-MAXD && dx<MAXD && dy>-MAXD && dy<MAXD && dz>-MAXD && dz<MAXD )
202
        {
203
        mTmpQuats[place++] = q;
204
        }
205
      }
206

    
207
    if( place!=0 )
208
      {
209
      int[] ret = new int[place];
210
      System.arraycopy(mTmpQuats,0,ret,0,place);
211
      return ret;
212
      }
213

    
214
    return null;
215
    }
216

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218
// special SolvedQuats for the case where there are no corner of edge cubits.
219
// first row {0} - means there are no corners or edges.
220
// each next defines all cubits of a singe face (numCubits, firstCubit, cubit1,..,cubitN-1, quat0,..., quatM
221

    
222
  private boolean isSolvedCentersOnly(TwistyObjectCubit[] cubits)
223
    {
224
    int numGroups = mSolvedQuats.length;
225

    
226
    for(int group=1; group<numGroups; group++)
227
      {
228
      int[] gq = mSolvedQuats[group];
229
      int numEntries= gq.length;
230
      int numCubits = gq[0];
231
      int firstCubit= gq[1];
232
      int firstQuat = cubits[firstCubit].mQuatIndex;
233

    
234
      for(int cubit=2; cubit<=numCubits; cubit++)
235
        {
236
        int currCubit= gq[cubit];
237
        int currQuat = cubits[currCubit].mQuatIndex;
238
        boolean isGood= (firstQuat==currQuat);
239

    
240
        for(int q=numCubits+1; !isGood && q<numEntries; q++)
241
          if( firstQuat == getMultQuat(currQuat,gq[q]) ) isGood = true;
242

    
243
        if( !isGood ) return false;
244
        }
245
      }
246

    
247
    return true;
248
    }
249

    
250
///////////////////////////////////////////////////////////////////////////////////////////////////
251

    
252
  private int surfaceExists(ArrayList<SurfaceInfo> list, float[] newSurface)
253
    {
254
    final float MAX_ERROR = 0.01f;
255
    int size = list.size();
256
    float dnx,dny,dnz,dnw;
257

    
258
    for(int s=0; s<size; s++)
259
      {
260
      SurfaceInfo si = list.get(s);
261
      float[] surface = si.surface;
262

    
263
      dnx = newSurface[0] - surface[0];
264
      dny = newSurface[1] - surface[1];
265
      dnz = newSurface[2] - surface[2];
266
      dnw = newSurface[3] - surface[3];
267

    
268
      if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR )
269
        {
270
        //android.util.Log.d("D", "1 Surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" exists already at "+s);
271

    
272
        return s;
273
        }
274

    
275
      dnx = newSurface[0] + surface[0];
276
      dny = newSurface[1] + surface[1];
277
      dnz = newSurface[2] + surface[2];
278
      dnw = newSurface[3] + surface[3];
279

    
280
      if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR )
281
        {
282
        //android.util.Log.d("D", "2 Surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" exists already at "+s);
283

    
284
        return s;
285
        }
286
      }
287

    
288
    //android.util.Log.d("D", "Surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" doesnt exist yet");
289
    return -1;
290
    }
291

    
292
///////////////////////////////////////////////////////////////////////////////////////////////////
293

    
294
  private int adjoinNewSurface(ArrayList<SurfaceInfo> list, float[] newSurface)
295
    {
296
    int index = surfaceExists(list,newSurface);
297
    if( index>=0 ) return index;
298

    
299
    //android.util.Log.d("D", "Adding new surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" to "+list.size());
300

    
301
    SurfaceInfo si = new SurfaceInfo(newSurface);
302
    list.add(si);
303
    return list.size()-1;
304
    }
305

    
306
///////////////////////////////////////////////////////////////////////////////////////////////////
307

    
308
  private float[] multiplySurface( float[] surface, Static4D quat )
309
    {
310
    float[] ret = new float[4];
311
    QuatHelper.rotateVectorByQuat(ret,surface[0],surface[1],surface[2],0,quat);
312
    ret[3] = surface[3];
313

    
314
    return ret;
315
    }
316

    
317
///////////////////////////////////////////////////////////////////////////////////////////////////
318

    
319
  private void computeSurfaceTable()
320
    {
321
    int[] numLayers = mParent.getNumLayers();
322
    float[][] pos = mParent.getCubitPositions(numLayers);
323
    int numCubits = pos.length;
324
    int numVariants = mParent.getNumCubitVariants(numLayers);
325
    ObjectShape[] shapes = new ObjectShape[numVariants];
326
    mCubitFaceToSurfaceMap = new int[numCubits][];
327
    mCubitFaceColor = new int[numCubits][];
328

    
329
    for(int v=0; v<numVariants; v++) shapes[v] = mParent.getObjectShape(v);
330

    
331
    ArrayList<SurfaceInfo> tmpSurfaces = new ArrayList<>();
332

    
333
    for(int c=0; c<numCubits; c++)
334
      {
335
      int variant = mParent.getCubitVariant(c,numLayers);
336
      ObjectShape s = shapes[variant];
337
      int numFaces = s.getNumFaces();
338
      mCubitFaceColor[c] = new int[numFaces];
339
      mCubitFaceToSurfaceMap[c] = new int[numFaces];
340
      for(int f=0; f<numFaces; f++) mCubitFaceToSurfaceMap[c][f] = -1;
341

    
342
      Static4D cubitQuat = mParent.getCubitQuats(c,numLayers);
343

    
344
      float[] po = pos[c];
345
      int poslen = po.length/3;
346
      float px=0, py=0, pz=0;
347
      for(int p=0; p<poslen; p++)
348
        {
349
        px += po[3*p  ];
350
        py += po[3*p+1];
351
        pz += po[3*p+2];
352
        }
353
      px /= poslen;
354
      py /= poslen;
355
      pz /= poslen;
356

    
357
      //android.util.Log.e("D", "cubit "+c+" pos "+px+" "+py+" "+pz+" faces: "+numFaces);
358

    
359
      for(int f=0; f<numFaces; f++)
360
        {
361
        mCubitFaceColor[c][f] = mParent.getCubitFaceColor(c,f);
362

    
363
        if( !mParent.faceIsOuter(c,f) ) continue;
364

    
365
        //android.util.Log.e("D", "cubit "+c+" face "+f);
366

    
367
        float[] surface  = new float[4];
368
        float[] rotPoint = new float[4];
369
        float[] normal   = new float[4];
370
        float[] intPoint = new float[3];
371

    
372
        s.getFacePoint(f,intPoint);
373
        s.getFaceNormal(f,normal);
374

    
375
        QuatHelper.rotateVectorByQuat(rotPoint,intPoint[0],intPoint[1],intPoint[2],1,cubitQuat);
376
        QuatHelper.rotateVectorByQuat(surface,normal[0],normal[1],normal[2],0,cubitQuat);
377

    
378
        float x = rotPoint[0] + px;
379
        float y = rotPoint[1] + py;
380
        float z = rotPoint[2] + pz;
381
        surface[3] = x*surface[0] + y*surface[1] + z*surface[2];
382

    
383
        int index = surfaceExists(tmpSurfaces,surface);
384

    
385
        if( index>=0 )
386
          {
387
          mCubitFaceToSurfaceMap[c][f] = index;
388
          }
389
        else
390
          {
391
          SurfaceInfo si = new SurfaceInfo(surface);
392
          tmpSurfaces.add(si);
393
          mCubitFaceToSurfaceMap[c][f] = tmpSurfaces.size()-1;
394

    
395
          //android.util.Log.e("D", "added surface "+tmpSurfaces.size()+" cubit "+c+" face "+f+" variant "+variant);
396

    
397
          int[] indices = new int[mNumQuats];
398

    
399
          for(int q=0; q<mNumQuats; q++)
400
            {
401
            float[] ts = multiplySurface(surface, mObjectQuats[q]);
402
            int ind = adjoinNewSurface(tmpSurfaces,ts);
403
/*
404
            if( ind==6 || ind==5 )
405
              {
406
              android.util.Log.e("D","SURFACE "+ind+": "+ts[0]+" "+ts[1]+" "+ts[2]+" "+ts[3] );
407
              }
408
*/
409
            indices[q] = ind;
410
            }
411

    
412
          si.setIndices(indices);
413
          }
414
        }
415
      }
416

    
417
    int size = tmpSurfaces.size();
418
    mSurfaceTable = new int[size][];
419

    
420
    for(int s=0; s<size; s++)
421
      {
422
      SurfaceInfo si = tmpSurfaces.get(s);
423

    
424
      if( si.indices == null )
425
        {
426
        float[] surface = si.surface;
427
        int[] indices = new int[mNumQuats];
428

    
429
        for(int q=0; q<mNumQuats; q++)
430
          {
431
          float[] ts = multiplySurface(surface, mObjectQuats[q]);
432
          int ind = adjoinNewSurface(tmpSurfaces,ts);
433
          indices[q] = ind;
434
          }
435

    
436
        si.setIndices(indices);
437
        }
438

    
439
      mSurfaceTable[s] = si.indices;
440
      }
441
    }
442

    
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444

    
445
  private void debugSurfaceTable(ArrayList<SurfaceInfo> surfaces)
446
    {
447
    int size = surfaces.size();
448
    android.util.Log.e("D", "COMPUTE size: "+size);
449

    
450
    for(int s=0; s<size; s++)
451
      {
452
      StringBuilder sb = new StringBuilder();
453
      sb.append("surface ");
454
      sb.append(s);
455
      sb.append(" : ");
456

    
457
      SurfaceInfo si = surfaces.get(s);
458
      float[] sur = si.surface;
459

    
460
      sb.append(sur[0]);
461
      sb.append(' ');
462
      sb.append(sur[1]);
463
      sb.append(' ');
464
      sb.append(sur[2]);
465
      sb.append(' ');
466
      sb.append(sur[3]);
467
      sb.append(" indices:");
468

    
469
      for(int q=0; q<mNumQuats; q++)
470
        {
471
        sb.append(' ');
472
        sb.append(mSurfaceTable[s][q]);
473
        }
474

    
475
      android.util.Log.d("D", sb.toString() );
476
      }
477
    }
478

    
479
///////////////////////////////////////////////////////////////////////////////////////////////////
480

    
481
  private boolean isSolved0(TwistyObjectCubit[] cubits)
482
    {
483
    if( mSolvedQuats[0][0]==0 ) return isSolvedCentersOnly(cubits);
484

    
485
    for( int[] solvedQuat : mSolvedQuats )
486
      {
487
      int numCubits = solvedQuat[0];
488
      int firstCubit= solvedQuat[1];
489
      int quat = cubits[firstCubit].mQuatIndex;
490

    
491
      for( int cubit=2; cubit<=numCubits; cubit++ )
492
        {
493
        int c = solvedQuat[cubit];
494
        if( quat != cubits[c].mQuatIndex ) return false;
495
        }
496
      }
497

    
498
    int cubit= mSolvedQuats[0][1];
499
    int quat0= cubits[cubit].mQuatIndex;
500
    int numGroups = mSolvedQuats.length;
501

    
502
    for(int group=1; group<numGroups; group++)
503
      {
504
      int firstCubit= mSolvedQuats[group][1];
505
      int currQuat  = cubits[firstCubit].mQuatIndex;
506

    
507
      if( quat0==currQuat ) continue;
508

    
509
      boolean isGood= false;
510
      int numEntries= mSolvedQuats[group].length;
511
      int numCubits = mSolvedQuats[group][0];
512

    
513
      for(int q=numCubits+1; q<numEntries; q++)
514
        {
515
        int quat = mSolvedQuats[group][q];
516

    
517
        if( quat0 == getMultQuat(currQuat,quat) )
518
          {
519
          isGood = true;
520
          break;
521
          }
522
        }
523

    
524
      if( !isGood ) return false;
525
      }
526

    
527
    return true;
528
    }
529

    
530
///////////////////////////////////////////////////////////////////////////////////////////////////
531
// Dino4 uses this. It is solved if and only if groups of cubits
532
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
533
// or
534
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
535
// are all the same color.
536

    
537
  private boolean isSolved1(TwistyObjectCubit[] cubits)
538
    {
539
    if( mScramble==null )
540
      {
541
      mScramble = new int[mNumQuats][mNumCubits];
542
      mColors   = new int[mNumCubits];
543

    
544
      for(int q=0; q<mNumQuats; q++)
545
        for(int c=0; c<mNumCubits; c++) mScramble[q][c] = computeScramble(q,c);
546
      }
547

    
548
    if( mFaceMap==null )
549
      {
550
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
551
      }
552

    
553
    for(int c=0; c<mNumCubits; c++)
554
      {
555
      int index = mScramble[cubits[c].mQuatIndex][c];
556
      mColors[index] = mFaceMap[c];
557
      }
558

    
559
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
560
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
561
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
562

    
563
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
564
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
565
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
566

    
567
    return false;
568
    }
569

    
570
///////////////////////////////////////////////////////////////////////////////////////////////////
571

    
572
  private int surfaceColor(int filled, int surface)
573
    {
574
    for(int s=0; s<filled; s++)
575
      if( mFaceColorTable[s][0] == surface ) return mFaceColorTable[s][1];
576

    
577
    return -1;
578
    }
579

    
580
///////////////////////////////////////////////////////////////////////////////////////////////////
581

    
582
  private boolean isSolved2(TwistyObjectCubit[] cubits)
583
    {
584
    int filled = 0;
585
    int max = mFaceColorTable.length;
586

    
587
    for(int c=0; c<mNumCubits; c++)
588
      {
589
      int numFaces = mCubitFaceToSurfaceMap[c].length;
590
      int quat = cubits[c].mQuatIndex;
591

    
592
      for(int f=0; f<numFaces; f++)
593
        {
594
        int initSurface = mCubitFaceToSurfaceMap[c][f];
595

    
596
        if( initSurface>=0 )
597
          {
598
          int currSurface = mSurfaceTable[initSurface][quat];
599
          int currColor = mCubitFaceColor[c][f];
600
          int prevColor = surfaceColor(filled,currSurface);
601

    
602
          if( prevColor<0 )
603
            {
604
            if( filled>=max ) return false;
605
            mFaceColorTable[filled][0] = currSurface;
606
            mFaceColorTable[filled][1] = currColor;
607
            prevColor = currColor;
608
            filled++;
609
            }
610

    
611
          if( prevColor!=currColor ) return false;
612
          }
613
        }
614
      }
615

    
616
    return true;
617
    }
618

    
619
///////////////////////////////////////////////////////////////////////////////////////////////////
620
// Called from TwistyObject
621
///////////////////////////////////////////////////////////////////////////////////////////////////
622

    
623
  int[][] getSolvedQuats(int[] numLayers, int numCubitFaces, int[][] cubitFaceColors)
624
    {
625
    int[] groups = new int[mNumCubits];
626
    int numGroups = 1;
627
    int numFirst  = 0;
628

    
629
    for(int cubit=0; cubit<mNumCubits; cubit++)
630
      {
631
      groups[cubit] = retCubitSolvedStatus(cubit,numLayers,numCubitFaces,cubitFaceColors);
632
      if( groups[cubit]>=0 ) numGroups++;
633
      else                   numFirst++;
634
      }
635

    
636
    int firstIndex = 1;
637
    int groupIndex = 1;
638
    int[][] solvedQuats = new int[numGroups][];
639
    solvedQuats[0] = new int[1+numFirst];
640
    solvedQuats[0][0] = numFirst;
641
    Static3D[] axis = mParent.getFaceAxis();
642

    
643
    for(int cubit=0; cubit<mNumCubits; cubit++)
644
      {
645
      int group = groups[cubit];
646

    
647
      if( group<0 )
648
        {
649
        solvedQuats[0][firstIndex] = cubit;
650
        firstIndex++;
651
        }
652
      else
653
        {
654
        int[] quats = buildSolvedQuats(axis[group]);
655
        int len = quats==null ? 0 : quats.length;
656
        solvedQuats[groupIndex] = new int[2+len];
657
        solvedQuats[groupIndex][0] = 1;
658
        solvedQuats[groupIndex][1] = cubit;
659
        for(int i=0; i<len; i++) solvedQuats[groupIndex][i+2] = quats[i];
660
        groupIndex++;
661
        }
662
      }
663
/*
664
    String dbg = "SOLVED GROUPS:\n";
665

    
666
    for(int g=0; g<numGroups; g++)
667
      {
668
      int len = solvedQuats[g].length;
669
      for(int i=0; i<len; i++) dbg += (" "+solvedQuats[g][i]);
670
      dbg+="\n";
671
      }
672

    
673
    android.util.Log.e("D", dbg);
674
*/
675
    return solvedQuats;
676
    }
677

    
678
///////////////////////////////////////////////////////////////////////////////////////////////////
679

    
680
  void setupSolvedQuats(int[][] quats)
681
    {
682
    mSolvedQuats = quats;
683
    }
684

    
685
///////////////////////////////////////////////////////////////////////////////////////////////////
686

    
687
  TwistyObjectSolved(TwistyObject parent, float[][] orig, int index)
688
    {
689
    mParent = parent;
690
    mObjectQuats = mParent.mObjectQuats;
691
    mNumQuats = mObjectQuats.length;
692
    mOrigPos = orig;
693
    mNumCubits = orig.length;
694
    mFunctionIndex = index;
695
    mTmpQuats = new int[mNumQuats];
696

    
697
    if( mFunctionIndex==2 )
698
      {
699
      computeSurfaceTable();
700
      int numFaces = parent.getNumFaces();
701
      mFaceColorTable = new int[numFaces][2];
702
      }
703
    }
704

    
705
///////////////////////////////////////////////////////////////////////////////////////////////////
706

    
707
  boolean isSolved(TwistyObjectCubit[] cubits)
708
    {
709
    switch(mFunctionIndex)
710
      {
711
      case 0: return isSolved0(cubits);
712
      case 1: return isSolved1(cubits);
713
      case 2: return isSolved2(cubits);
714
      }
715

    
716
    return false;
717
    }
718
  }
(9-9/9)