Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObjectSolved.java @ 6777e712

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[][] mTmpFaceColorTable;
48
  private int[][] mCubitFaceToSurfaceMap;
49
  private int[][] mCubitFaceToPuzzleFaceMap;
50
  private int[] mPuzzleFaceColor;
51

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

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

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

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

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

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

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

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

    
90
    return -1;
91
    }
92

    
93
///////////////////////////////////////////////////////////////////////////////////////////////////
94

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

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

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

    
111
    return -1;
112
    }
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

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

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

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

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

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

    
140
    return -1;
141
    }
142

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

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

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

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

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

    
172
    return nonBlackIndex;
173
    }
174

    
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176

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

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

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

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

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

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

    
215
    return null;
216
    }
217

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

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

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

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

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

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

    
248
    return true;
249
    }
250

    
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252

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

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

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

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

    
273
        return s;
274
        }
275

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

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

    
285
        return s;
286
        }
287
      }
288

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

    
293
///////////////////////////////////////////////////////////////////////////////////////////////////
294

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

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

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

    
307
///////////////////////////////////////////////////////////////////////////////////////////////////
308

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

    
315
    return ret;
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319

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

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

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

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

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

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

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

    
360
      for(int f=0; f<numFaces; f++)
361
        {
362
        mCubitFaceToPuzzleFaceMap[c][f] = mParent.getDefaultCubitFaceColor(c,f);
363

    
364
        //android.util.Log.e("D", "cubit "+c+" face "+f+" puzzle face: "+ mCubitFaceToPuzzleFaceMap[c][f]);
365

    
366
        if( !mParent.faceIsOuter(c,f) ) continue;
367

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

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

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

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

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

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

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

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

    
400
          for(int q=0; q<mNumQuats; q++)
401
            {
402
            float[] ts = multiplySurface(surface, mObjectQuats[q]);
403
            int ind = adjoinNewSurface(tmpSurfaces,ts);
404
            indices[q] = ind;
405
            }
406

    
407
          si.setIndices(indices);
408
          }
409
        }
410
      }
411

    
412
    int size = tmpSurfaces.size();
413
    mSurfaceTable = new int[size][];
414

    
415
    for(int s=0; s<size; s++)
416
      {
417
      SurfaceInfo si = tmpSurfaces.get(s);
418

    
419
      if( si.indices == null )
420
        {
421
        float[] surface = si.surface;
422
        int[] indices = new int[mNumQuats];
423

    
424
        for(int q=0; q<mNumQuats; q++)
425
          {
426
          float[] ts = multiplySurface(surface, mObjectQuats[q]);
427
          int ind = adjoinNewSurface(tmpSurfaces,ts);
428
          indices[q] = ind;
429
          }
430

    
431
        si.setIndices(indices);
432
        }
433

    
434
      mSurfaceTable[s] = si.indices;
435
      }
436
    }
437

    
438
///////////////////////////////////////////////////////////////////////////////////////////////////
439

    
440
  private void debugSurfaceTable(ArrayList<SurfaceInfo> surfaces)
441
    {
442
    int size = surfaces.size();
443
    android.util.Log.e("D", "COMPUTE size: "+size);
444

    
445
    for(int s=0; s<size; s++)
446
      {
447
      StringBuilder sb = new StringBuilder();
448
      sb.append("surface ");
449
      sb.append(s);
450
      sb.append(" : ");
451

    
452
      SurfaceInfo si = surfaces.get(s);
453
      float[] sur = si.surface;
454

    
455
      sb.append(sur[0]);
456
      sb.append(' ');
457
      sb.append(sur[1]);
458
      sb.append(' ');
459
      sb.append(sur[2]);
460
      sb.append(' ');
461
      sb.append(sur[3]);
462
      sb.append(" indices:");
463

    
464
      for(int q=0; q<mNumQuats; q++)
465
        {
466
        sb.append(' ');
467
        sb.append(mSurfaceTable[s][q]);
468
        }
469

    
470
      android.util.Log.d("D", sb.toString() );
471
      }
472
    }
473

    
474
///////////////////////////////////////////////////////////////////////////////////////////////////
475

    
476
  private boolean isSolved0(TwistyObjectCubit[] cubits)
477
    {
478
    if( mSolvedQuats[0][0]==0 ) return isSolvedCentersOnly(cubits);
479

    
480
    for( int[] solvedQuat : mSolvedQuats )
481
      {
482
      int numCubits = solvedQuat[0];
483
      int firstCubit= solvedQuat[1];
484
      int quat = cubits[firstCubit].mQuatIndex;
485

    
486
      for( int cubit=2; cubit<=numCubits; cubit++ )
487
        {
488
        int c = solvedQuat[cubit];
489
        if( quat != cubits[c].mQuatIndex ) return false;
490
        }
491
      }
492

    
493
    int cubit= mSolvedQuats[0][1];
494
    int quat0= cubits[cubit].mQuatIndex;
495
    int numGroups = mSolvedQuats.length;
496

    
497
    for(int group=1; group<numGroups; group++)
498
      {
499
      int firstCubit= mSolvedQuats[group][1];
500
      int currQuat  = cubits[firstCubit].mQuatIndex;
501

    
502
      if( quat0==currQuat ) continue;
503

    
504
      boolean isGood= false;
505
      int numEntries= mSolvedQuats[group].length;
506
      int numCubits = mSolvedQuats[group][0];
507

    
508
      for(int q=numCubits+1; q<numEntries; q++)
509
        {
510
        int quat = mSolvedQuats[group][q];
511

    
512
        if( quat0 == getMultQuat(currQuat,quat) )
513
          {
514
          isGood = true;
515
          break;
516
          }
517
        }
518

    
519
      if( !isGood ) return false;
520
      }
521

    
522
    return true;
523
    }
524

    
525
///////////////////////////////////////////////////////////////////////////////////////////////////
526
// Dino4 uses this. It is solved if and only if groups of cubits
527
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
528
// or
529
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
530
// are all the same color.
531

    
532
  private boolean isSolved1(TwistyObjectCubit[] cubits)
533
    {
534
    if( mScramble==null )
535
      {
536
      mScramble = new int[mNumQuats][mNumCubits];
537
      mColors   = new int[mNumCubits];
538

    
539
      for(int q=0; q<mNumQuats; q++)
540
        for(int c=0; c<mNumCubits; c++) mScramble[q][c] = computeScramble(q,c);
541
      }
542

    
543
    if( mFaceMap==null )
544
      {
545
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
546
      }
547

    
548
    for(int c=0; c<mNumCubits; c++)
549
      {
550
      int index = mScramble[cubits[c].mQuatIndex][c];
551
      mColors[index] = mFaceMap[c];
552
      }
553

    
554
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
555
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
556
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
557

    
558
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
559
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
560
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
561

    
562
    return false;
563
    }
564

    
565
///////////////////////////////////////////////////////////////////////////////////////////////////
566

    
567
  private int surfaceColor(int filled, int surface)
568
    {
569
    for(int s=0; s<filled; s++)
570
      if( mTmpFaceColorTable[s][0] == surface ) return mTmpFaceColorTable[s][1];
571

    
572
    return 0;
573
    }
574

    
575
///////////////////////////////////////////////////////////////////////////////////////////////////
576

    
577
  private boolean isSolved2(TwistyObjectCubit[] cubits)
578
    {
579
    int filled = 0;
580
    int max = mTmpFaceColorTable.length;
581

    
582
    for(int c=0; c<mNumCubits; c++)
583
      {
584
      int numFaces = mCubitFaceToSurfaceMap[c].length;
585
      int quat = cubits[c].mQuatIndex;
586

    
587
      for(int f=0; f<numFaces; f++)
588
        {
589
        int initSurface = mCubitFaceToSurfaceMap[c][f];
590

    
591
        if( initSurface>=0 )
592
          {
593
          int puzzleFace = mCubitFaceToPuzzleFaceMap[c][f];
594
          int currSurface= mSurfaceTable[initSurface][quat];
595
          int currColor  = puzzleFace>=0 ? mPuzzleFaceColor[puzzleFace] : 0;
596
          int prevColor  = surfaceColor(filled,currSurface);
597

    
598
//android.util.Log.e("D", "cubit "+c+" face "+f+" puzzleFace: "+puzzleFace+" currColor: "+currColor+" prevColor: "+prevColor);
599

    
600
          if( prevColor==0 )
601
            {
602
            if( filled>=max )
603
              {
604
              //android.util.Log.e("D", "false1");
605
              return false;
606
              }
607
            mTmpFaceColorTable[filled][0] = currSurface;
608
            mTmpFaceColorTable[filled][1] = currColor;
609
            prevColor = currColor;
610
            filled++;
611
            }
612

    
613
          if( prevColor!=currColor )
614
            {
615
            //android.util.Log.e("D", "false2");
616
            return false;
617
            }
618
          }
619
        }
620
      }
621

    
622
    // android.util.Log.e("D", "true");
623
    return true;
624
    }
625

    
626
///////////////////////////////////////////////////////////////////////////////////////////////////
627
// Called from TwistyObject
628
///////////////////////////////////////////////////////////////////////////////////////////////////
629

    
630
  int[][] getSolvedQuats(int[] numLayers, int numCubitFaces, int[][] cubitFaceColors)
631
    {
632
    int[] groups = new int[mNumCubits];
633
    int numGroups = 1;
634
    int numFirst  = 0;
635

    
636
    for(int cubit=0; cubit<mNumCubits; cubit++)
637
      {
638
      groups[cubit] = retCubitSolvedStatus(cubit,numLayers,numCubitFaces,cubitFaceColors);
639
      if( groups[cubit]>=0 ) numGroups++;
640
      else                   numFirst++;
641
      }
642

    
643
    int firstIndex = 1;
644
    int groupIndex = 1;
645
    int[][] solvedQuats = new int[numGroups][];
646
    solvedQuats[0] = new int[1+numFirst];
647
    solvedQuats[0][0] = numFirst;
648
    Static3D[] axis = mParent.getFaceAxis();
649

    
650
    for(int cubit=0; cubit<mNumCubits; cubit++)
651
      {
652
      int group = groups[cubit];
653

    
654
      if( group<0 )
655
        {
656
        solvedQuats[0][firstIndex] = cubit;
657
        firstIndex++;
658
        }
659
      else
660
        {
661
        int[] quats = buildSolvedQuats(axis[group]);
662
        int len = quats==null ? 0 : quats.length;
663
        solvedQuats[groupIndex] = new int[2+len];
664
        solvedQuats[groupIndex][0] = 1;
665
        solvedQuats[groupIndex][1] = cubit;
666
        for(int i=0; i<len; i++) solvedQuats[groupIndex][i+2] = quats[i];
667
        groupIndex++;
668
        }
669
      }
670
/*
671
    String dbg = "SOLVED GROUPS:\n";
672

    
673
    for(int g=0; g<numGroups; g++)
674
      {
675
      int len = solvedQuats[g].length;
676
      for(int i=0; i<len; i++) dbg += (" "+solvedQuats[g][i]);
677
      dbg+="\n";
678
      }
679

    
680
    android.util.Log.e("D", dbg);
681
*/
682
    return solvedQuats;
683
    }
684

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

    
687
  void setupSolvedQuats(int[][] quats)
688
    {
689
    mSolvedQuats = quats;
690
    }
691

    
692
///////////////////////////////////////////////////////////////////////////////////////////////////
693

    
694
  TwistyObjectSolved(TwistyObject parent, float[][] orig, int index)
695
    {
696
    mParent = parent;
697
    mObjectQuats = mParent.mObjectQuats;
698
    mNumQuats = mObjectQuats.length;
699
    mOrigPos = orig;
700
    mNumCubits = orig.length;
701
    mFunctionIndex = index;
702
    mTmpQuats = new int[mNumQuats];
703

    
704
    if( mFunctionIndex==2 )
705
      {
706
      computeSurfaceTable();
707
      int numFaces = parent.getNumPuzzleFaces();
708
      mTmpFaceColorTable = new int[numFaces][2];
709
      }
710
    }
711

    
712
///////////////////////////////////////////////////////////////////////////////////////////////////
713

    
714
  void setPuzzleFaceColor(int[] color)
715
    {
716
    mPuzzleFaceColor = color;
717
    }
718

    
719
///////////////////////////////////////////////////////////////////////////////////////////////////
720

    
721
  boolean isSolved(TwistyObjectCubit[] cubits)
722
    {
723
    switch(mFunctionIndex)
724
      {
725
      case 0: return isSolved0(cubits);
726
      case 1: return isSolved1(cubits);
727
      case 2: return isSolved2(cubits);
728
      }
729

    
730
    return false;
731
    }
732
  }
(9-9/9)