Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObjectSolved.java @ aacf5e27

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

    
16
///////////////////////////////////////////////////////////////////////////////////////////////////
17

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

    
28
  private int[][] mSolvedQuats;
29
  private int[][] mQuatMult;
30
  private int[] mFaceMap;
31
  private int[][] mScramble;
32
  private int[] mColors;
33

    
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35
// remember about the double cover or unit quaternions!
36

    
37
  private int mulQuat(int q1, int q2)
38
    {
39
    Static4D result = QuatHelper.quatMultiply(mObjectQuats[q1],mObjectQuats[q2]);
40

    
41
    float rX = result.get0();
42
    float rY = result.get1();
43
    float rZ = result.get2();
44
    float rW = result.get3();
45

    
46
    final float MAX_ERROR = 0.1f;
47
    float dX,dY,dZ,dW;
48

    
49
    for(int i=0; i<mNumQuats; i++)
50
      {
51
      dX = mObjectQuats[i].get0() - rX;
52
      dY = mObjectQuats[i].get1() - rY;
53
      dZ = mObjectQuats[i].get2() - rZ;
54
      dW = mObjectQuats[i].get3() - rW;
55

    
56
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
57
          dY<MAX_ERROR && dY>-MAX_ERROR &&
58
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
59
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
60

    
61
      dX = mObjectQuats[i].get0() + rX;
62
      dY = mObjectQuats[i].get1() + rY;
63
      dZ = mObjectQuats[i].get2() + rZ;
64
      dW = mObjectQuats[i].get3() + rW;
65

    
66
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
67
          dY<MAX_ERROR && dY>-MAX_ERROR &&
68
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
69
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
70
      }
71

    
72
    return -1;
73
    }
74

    
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76

    
77
  private int getMultQuat(int index1, int index2)
78
    {
79
    if( mQuatMult==null )
80
      {
81
      mQuatMult = new int[mNumQuats][mNumQuats];
82

    
83
      for(int i=0; i<mNumQuats; i++)
84
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
85
      }
86

    
87
    if( index1<mNumQuats && index2<mNumQuats )
88
      {
89
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2);
90
      return mQuatMult[index1][index2];
91
      }
92

    
93
    return -1;
94
    }
95

    
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97

    
98
  private int computeScramble(int quatNum, int centerNum)
99
    {
100
    float MAXDIFF = 0.01f;
101
    float[] center= mOrigPos[centerNum];
102
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
103
    Static4D result = QuatHelper.rotateVectorByQuat(sc,mObjectQuats[quatNum]);
104

    
105
    float x = result.get0();
106
    float y = result.get1();
107
    float z = result.get2();
108

    
109
    for(int c=0; c<mNumCubits; c++)
110
      {
111
      float[] cent = mOrigPos[c];
112

    
113
      float qx = cent[0] - x;
114
      float qy = cent[1] - y;
115
      float qz = cent[2] - z;
116

    
117
      if( qx>-MAXDIFF && qx<MAXDIFF &&
118
          qy>-MAXDIFF && qy<MAXDIFF &&
119
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
120
      }
121

    
122
    return -1;
123
    }
124

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126
// This is used to build internal data structures for the generic 'isSolved()'
127
//
128
// if this is an internal cubit (all faces black): return -1
129
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
130
// Color index, i.e. the index into the 'FACE_COLORS' table.
131
// else (edge or corner cubit, more than one non-black face): return -2.
132

    
133
  private int retCubitSolvedStatus(int cubit, int[] numLayers, int numCubitFaces, int[][] cubitFaceColors)
134
    {
135
    int numNonBlack=0, nonBlackIndex=-1, stiShape, cubColor;
136
    int variant = mParent.getCubitVariant(cubit,numLayers);
137

    
138
    for(int face=0; face<numCubitFaces; face++)
139
      {
140
      stiShape = mParent.getVariantStickerShape(variant,face);
141
      int numFaces = cubitFaceColors[cubit].length;
142
      cubColor = face<numFaces ? cubitFaceColors[cubit][face] : -1;
143

    
144
      if( stiShape>=0 && cubColor>=0 )
145
        {
146
        numNonBlack++;
147
        nonBlackIndex = cubColor;
148
        }
149
      }
150

    
151
    if( numNonBlack==0 ) return -1;
152
    if( numNonBlack>=2 ) return -2;
153

    
154
    return nonBlackIndex;
155
    }
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158

    
159
  private int[] buildSolvedQuats(Static3D faceAx)
160
    {
161
    final float MAXD = 0.0001f;
162
    float x = faceAx.get0();
163
    float y = faceAx.get1();
164
    float z = faceAx.get2();
165
    float a,dx,dy,dz,qx,qy,qz;
166
    Static4D quat;
167
    int place = 0;
168

    
169
    for(int q=1; q<mNumQuats; q++)
170
      {
171
      quat = mObjectQuats[q];
172
      qx = quat.get0();
173
      qy = quat.get1();
174
      qz = quat.get2();
175

    
176
           if( x!=0.0f ) { a = qx/x; }
177
      else if( y!=0.0f ) { a = qy/y; }
178
      else               { a = qz/z; }
179

    
180
      dx = a*x-qx;
181
      dy = a*y-qy;
182
      dz = a*z-qz;
183

    
184
      if( dx>-MAXD && dx<MAXD && dy>-MAXD && dy<MAXD && dz>-MAXD && dz<MAXD )
185
        {
186
        mTmpQuats[place++] = q;
187
        }
188
      }
189

    
190
    if( place!=0 )
191
      {
192
      int[] ret = new int[place];
193
      System.arraycopy(mTmpQuats,0,ret,0,place);
194
      return ret;
195
      }
196

    
197
    return null;
198
    }
199

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

    
205
  private boolean isSolvedCentersOnly(TwistyObjectCubit[] cubits)
206
    {
207
    int numGroups = mSolvedQuats.length;
208

    
209
    for(int group=1; group<numGroups; group++)
210
      {
211
      int numEntries= mSolvedQuats[group].length;
212
      int numCubits = mSolvedQuats[group][0];
213
      int firstCubit= mSolvedQuats[group][1];
214
      int firstQuat = cubits[firstCubit].mQuatIndex;
215

    
216
      for(int cubit=2; cubit<=numCubits; cubit++)
217
        {
218
        int currCubit= mSolvedQuats[group][cubit];
219
        int currQuat = cubits[currCubit].mQuatIndex;
220
        boolean isGood= (firstQuat==currQuat);
221

    
222
        for(int q=numCubits+1; !isGood && q<numEntries; q++)
223
          {
224
          int quat = mSolvedQuats[group][q];
225
          if( firstQuat == getMultQuat(currQuat,quat) ) isGood = true;
226
          }
227

    
228
        if( !isGood ) return false;
229
        }
230
      }
231

    
232
    return true;
233
    }
234

    
235
///////////////////////////////////////////////////////////////////////////////////////////////////
236

    
237
  private boolean isSolved0(TwistyObjectCubit[] cubits)
238
    {
239
    if( mSolvedQuats[0][0]==0 ) return isSolvedCentersOnly(cubits);
240

    
241
    for( int[] solvedQuat : mSolvedQuats )
242
      {
243
      int numCubits = solvedQuat[0];
244
      int firstCubit= solvedQuat[1];
245
      int quat = cubits[firstCubit].mQuatIndex;
246

    
247
      for( int cubit=2; cubit<=numCubits; cubit++ )
248
        {
249
        int c = solvedQuat[cubit];
250
        if( quat != cubits[c].mQuatIndex ) return false;
251
        }
252
      }
253

    
254
    int cubit= mSolvedQuats[0][1];
255
    int quat0= cubits[cubit].mQuatIndex;
256
    int numGroups = mSolvedQuats.length;
257

    
258
    for(int group=1; group<numGroups; group++)
259
      {
260
      int firstCubit= mSolvedQuats[group][1];
261
      int currQuat  = cubits[firstCubit].mQuatIndex;
262

    
263
      if( quat0==currQuat ) continue;
264

    
265
      boolean isGood= false;
266
      int numEntries= mSolvedQuats[group].length;
267
      int numCubits = mSolvedQuats[group][0];
268

    
269
      for(int q=numCubits+1; q<numEntries; q++)
270
        {
271
        int quat = mSolvedQuats[group][q];
272

    
273
        if( quat0 == getMultQuat(currQuat,quat) )
274
          {
275
          isGood = true;
276
          break;
277
          }
278
        }
279

    
280
      if( !isGood ) return false;
281
      }
282

    
283
    return true;
284
    }
285

    
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287
// Dino4 uses this. It is solved if and only if groups of cubits
288
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
289
// or
290
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
291
// are all the same color.
292

    
293
  private boolean isSolved1(TwistyObjectCubit[] cubits)
294
    {
295
    if( mScramble==null )
296
      {
297
      mScramble = new int[mNumQuats][mNumCubits];
298
      mColors   = new int[mNumCubits];
299

    
300
      for(int q=0; q<mNumQuats; q++)
301
        for(int c=0; c<mNumCubits; c++) mScramble[q][c] = computeScramble(q,c);
302
      }
303

    
304
    if( mFaceMap==null )
305
      {
306
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
307
      }
308

    
309
    for(int c=0; c<mNumCubits; c++)
310
      {
311
      int index = mScramble[cubits[c].mQuatIndex][c];
312
      mColors[index] = mFaceMap[c];
313
      }
314

    
315
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
316
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
317
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
318

    
319
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
320
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
321
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
322

    
323
    return false;
324
    }
325

    
326
///////////////////////////////////////////////////////////////////////////////////////////////////
327
// Called from TwistyObject
328
///////////////////////////////////////////////////////////////////////////////////////////////////
329

    
330
  int[][] getSolvedQuats(int[] numLayers, int numCubitFaces, int[][] cubitFaceColors)
331
    {
332
    int[] groups = new int[mNumCubits];
333
    int numGroups = 1;
334
    int numFirst  = 0;
335

    
336
    for(int cubit=0; cubit<mNumCubits; cubit++)
337
      {
338
      groups[cubit] = retCubitSolvedStatus(cubit,numLayers,numCubitFaces,cubitFaceColors);
339
      if( groups[cubit]>=0 ) numGroups++;
340
      else                   numFirst++;
341
      }
342

    
343
    int firstIndex = 1;
344
    int groupIndex = 1;
345
    int[][] solvedQuats = new int[numGroups][];
346
    solvedQuats[0] = new int[1+numFirst];
347
    solvedQuats[0][0] = numFirst;
348
    Static3D[] axis = mParent.getFaceAxis();
349

    
350
    for(int cubit=0; cubit<mNumCubits; cubit++)
351
      {
352
      int group = groups[cubit];
353

    
354
      if( group<0 )
355
        {
356
        solvedQuats[0][firstIndex] = cubit;
357
        firstIndex++;
358
        }
359
      else
360
        {
361
        int[] quats = buildSolvedQuats(axis[group]);
362
        int len = quats==null ? 0 : quats.length;
363
        solvedQuats[groupIndex] = new int[2+len];
364
        solvedQuats[groupIndex][0] = 1;
365
        solvedQuats[groupIndex][1] = cubit;
366
        for(int i=0; i<len; i++) solvedQuats[groupIndex][i+2] = quats[i];
367
        groupIndex++;
368
        }
369
      }
370
/*
371
    String dbg = "SOLVED GROUPS:\n";
372

    
373
    for(int g=0; g<numGroups; g++)
374
      {
375
      int len = solvedQuats[g].length;
376
      for(int i=0; i<len; i++) dbg += (" "+solvedQuats[g][i]);
377
      dbg+="\n";
378
      }
379

    
380
    android.util.Log.e("D", dbg);
381
*/
382
    return solvedQuats;
383
    }
384

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386

    
387
  void setupSolvedQuats(int[][] quats)
388
    {
389
    mSolvedQuats = quats;
390
    }
391

    
392
///////////////////////////////////////////////////////////////////////////////////////////////////
393

    
394
  TwistyObjectSolved(TwistyObject parent, float[][] orig, int index)
395
    {
396
    mParent = parent;
397
    mObjectQuats = mParent.mObjectQuats;
398
    mNumQuats = mObjectQuats.length;
399
    mOrigPos = orig;
400
    mNumCubits = orig.length;
401
    mFunctionIndex = index;
402
    mTmpQuats = new int[mNumQuats];
403
    }
404

    
405
///////////////////////////////////////////////////////////////////////////////////////////////////
406

    
407
  boolean isSolved(TwistyObjectCubit[] cubits)
408
    {
409
    if( mFunctionIndex==0 ) return isSolved0(cubits);
410
    if( mFunctionIndex==1 ) return isSolved1(cubits);
411

    
412
    return false;
413
    }
414
  }
(11-11/11)