Project

General

Profile

Download (16.6 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / objects / TwistyCube.java @ 76c2bd07

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.objects;
21

    
22
import android.content.res.Resources;
23
import android.graphics.Canvas;
24
import android.graphics.Paint;
25

    
26
import org.distorted.library.main.DistortedEffects;
27
import org.distorted.library.main.DistortedTexture;
28
import org.distorted.library.mesh.MeshBase;
29
import org.distorted.library.mesh.MeshSquare;
30
import org.distorted.library.type.Static3D;
31
import org.distorted.library.type.Static4D;
32
import org.distorted.main.RubikSurfaceView;
33

    
34
import java.util.Random;
35

    
36
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
37

    
38
///////////////////////////////////////////////////////////////////////////////////////////////////
39

    
40
class TwistyCube extends TwistyObject
41
{
42
  static final float SQ2 = (float)Math.sqrt(2);
43

    
44
  // the three rotation axis of a RubikCube. Must be normalized.
45
  static final Static3D[] ROT_AXIS = new Static3D[]
46
         {
47
           new Static3D(1,0,0),
48
           new Static3D(0,1,0),
49
           new Static3D(0,0,1)
50
         };
51

    
52
  private static final int[] FACE_COLORS = new int[]
53
         {
54
           COLOR_YELLOW, COLOR_WHITE,
55
           COLOR_BLUE  , COLOR_GREEN,
56
           COLOR_RED   , COLOR_BROWN
57
         };
58

    
59
  // All legal rotation quats of a RubikCube of any size.
60
  // Here's how to compute this:
61
  // 1) compute how many rotations there are (RubikCube of any size = 24)
62
  // 2) take the AXIS, angles of rotation (90 in RubikCube's case) compute the basic quaternions
63
  // (i.e. rotations of 1 basic angle along each of the axis) and from there start semi-randomly
64
  // multiplying them and eventually you'll find all (24) legal rotations.
65
  // Example program in C, res/raw/compute_quats.c , is included.
66
  private static final Static4D[] QUATS = new Static4D[]
67
         {
68
         new Static4D(  0.0f,   0.0f,   0.0f,   1.0f),
69
         new Static4D(  1.0f,   0.0f,   0.0f,   0.0f),
70
         new Static4D(  0.0f,   1.0f,   0.0f,   0.0f),
71
         new Static4D(  0.0f,   0.0f,   1.0f,   0.0f),
72

    
73
         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
74
         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
75
         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
76
         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
77
         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
78
         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
79
         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
80
         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
81
         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
82
         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
83
         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
84
         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
85

    
86
         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
87
         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
88
         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
89
         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
90
         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
91
         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
92
         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
93
         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
94
         };
95

    
96
  private static MeshBase[] mMeshes;
97

    
98
///////////////////////////////////////////////////////////////////////////////////////////////////
99

    
100
  TwistyCube(int size, Static4D quat, DistortedTexture texture,
101
             MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
102
    {
103
    super(size, 60, quat, texture, mesh, effects, moves, ObjectList.CUBE, res, scrWidth);
104
    }
105

    
106
///////////////////////////////////////////////////////////////////////////////////////////////////
107

    
108
  MeshBase createCubitMesh(int cubit)
109
    {
110
    if( mMeshes==null )
111
      {
112
      mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
113
      }
114

    
115
    int ordinal= ObjectList.CUBE.ordinal();
116
    int index  = ObjectList.getSizeIndex(ordinal,getSize());
117

    
118
    if( mMeshes[index]==null )
119
      {
120
      mMeshes[index] = CubitFactory.getInstance().createCubeMesh(index);
121
      }
122

    
123
    return mMeshes[index].copy(true);
124
    }
125

    
126
///////////////////////////////////////////////////////////////////////////////////////////////////
127
// paint the square with upper-right corner at (left,top) and side length 'side' with texture
128
// for face 'face'.
129

    
130
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left)
131
    {
132
    float F =  0.5f;
133
    float R = 0.10f;
134
    float S = 0.10f;
135
    float[] vertices = { -F,-F, +F,-F, +F,+F, -F,+F};
136

    
137
    drawRoundedPolygon(canvas, paint, left, vertices, S, FACE_COLORS[face], R);
138
    }
139

    
140
///////////////////////////////////////////////////////////////////////////////////////////////////
141

    
142
  Static3D[] getCubitPositions(int size)
143
    {
144
    int numCubits = size>1 ? 6*size*size - 12*size + 8 : 1;
145
    Static3D[] tmp = new Static3D[numCubits];
146

    
147
    float diff = 0.5f*(size-1);
148
    int currentPosition = 0;
149

    
150
    for(int x = 0; x<size; x++)
151
      for(int y = 0; y<size; y++)
152
        for(int z = 0; z<size; z++)
153
          if( x==0 || x==size-1 || y==0 || y==size-1 || z==0 || z==size-1 )
154
            {
155
            tmp[currentPosition++] = new Static3D(x-diff,y-diff,z-diff);
156
            }
157

    
158
    return tmp;
159
    }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

    
163
  Static4D[] getQuats()
164
    {
165
    return QUATS;
166
    }
167

    
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169

    
170
  boolean shouldResetTextureMaps()
171
    {
172
    return false;
173
    }
174

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

    
177
  int getNumFaces()
178
    {
179
    return FACE_COLORS.length;
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
  float getBasicStep()
185
    {
186
    return 1.0f;
187
    }
188

    
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

    
191
  int getNumStickerTypes()
192
    {
193
    return 1;
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  int getNumCubitFaces()
199
    {
200
    return FACE_COLORS.length;
201
    }
202

    
203
///////////////////////////////////////////////////////////////////////////////////////////////////
204

    
205
  float getScreenRatio()
206
    {
207
    return 0.5f;
208
    }
209

    
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211

    
212
  int getFaceColor(int cubit, int cubitface, int size)
213
    {
214
    float diff = CUBITS[cubit].mRotationRow[cubitface/2] - (cubitface%2==0 ? size-1:0);
215
    return diff*diff < 0.0001f ? cubitface : NUM_FACES;
216
    }
217

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219

    
220
  float returnMultiplier()
221
    {
222
    return getSize();
223
    }
224

    
225
///////////////////////////////////////////////////////////////////////////////////////////////////
226

    
227
  float[] getRowChances()
228
    {
229
    int size = getSize();
230
    float[] chances = new float[size];
231

    
232
    for(int i=0; i<size; i++)
233
      {
234
      chances[i] = (i+1.0f) / size;
235
      }
236

    
237
    return chances;
238
    }
239

    
240
///////////////////////////////////////////////////////////////////////////////////////////////////
241
// PUBLIC API
242

    
243
  public Static3D[] getRotationAxis()
244
    {
245
    return ROT_AXIS;
246
    }
247

    
248
///////////////////////////////////////////////////////////////////////////////////////////////////
249

    
250
  public int getBasicAngle()
251
    {
252
    return 4;
253
    }
254

    
255
///////////////////////////////////////////////////////////////////////////////////////////////////
256

    
257
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
258
    {
259
    int numAxis = ROTATION_AXIS.length;
260

    
261
    if( oldRotAxis == START_AXIS )
262
      {
263
      return rnd.nextInt(numAxis);
264
      }
265
    else
266
      {
267
      int newVector = rnd.nextInt(numAxis-1);
268
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
269
      }
270
    }
271

    
272
///////////////////////////////////////////////////////////////////////////////////////////////////
273

    
274
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
275
    {
276
    float rowFloat = rnd.nextFloat();
277

    
278
    for(int row=0; row<mRowChances.length; row++)
279
      {
280
      if( rowFloat<=mRowChances[row] ) return row;
281
      }
282

    
283
    return 0;
284
    }
285

    
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287

    
288
  public boolean isSolved()
289
    {
290
    int index = CUBITS[0].mQuatIndex;
291

    
292
    for(int i=1; i<NUM_CUBITS; i++)
293
      {
294
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
295
      }
296

    
297
    return true;
298
    }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
302
// then if it were rotated by quaternion 'quat'.
303
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
304
// middle squares get interchanged. No visible difference!
305
//
306
// So: this is true iff the cubit
307
// a) is a corner or edge and the quaternions are the same
308
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
309

    
310
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
311
    {
312
    if ( cubit.mQuatIndex == quatIndex ) return true;
313

    
314
    int belongsToHowManyFaces = 0;
315
    int size = getSize()-1;
316
    float row;
317
    final float MAX_ERROR = 0.01f;
318

    
319
    for(int i=0; i<NUM_AXIS; i++)
320
      {
321
      row = cubit.mRotationRow[i];
322
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
323
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
324
      }
325

    
326
    switch(belongsToHowManyFaces)
327
      {
328
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
329
      case 1 :                // cubit that lies inside one of the faces
330
               Static3D orig = cubit.getOrigPosition();
331
               Static4D quat1 = QUATS[quatIndex];
332
               Static4D quat2 = QUATS[cubit.mQuatIndex];
333

    
334
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
335
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
336
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
337

    
338
               float row1, row2, row3, row4;
339
               float ax,ay,az;
340
               Static3D axis;
341
               float x1 = rotated1.get0();
342
               float y1 = rotated1.get1();
343
               float z1 = rotated1.get2();
344
               float x2 = rotated2.get0();
345
               float y2 = rotated2.get1();
346
               float z2 = rotated2.get2();
347

    
348
               for(int i=0; i<NUM_AXIS; i++)
349
                 {
350
                 axis = ROTATION_AXIS[i];
351
                 ax = axis.get0();
352
                 ay = axis.get1();
353
                 az = axis.get2();
354

    
355
                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
356
                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
357
                 row3 = row1 - size;
358
                 row4 = row2 - size;
359

    
360
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
361
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
362
                   {
363
                   return true;
364
                   }
365
                 }
366
               return false;
367

    
368
      default: return false;  // edge or corner
369
      }
370
    }
371

    
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373
// order: Up --> Right --> Front --> Down --> Left --> Back
374
// (because the first implemented Solver - the two-phase Cube3 one - expects such order)
375
//
376
// Solved 3x3x3 Cube maps to "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB"
377
//
378
// s : size of the cube; let index = a*s + b    (i.e. a,b = row,column)
379
//
380
// Up    :   index --> b<s-1 ? (s-1)*(s+4b)+a : 6*s*s -13*s +8 +a
381
// Right :   index --> 6*s*s - 12*s + 7 - index
382
// Front :   index --> if b==0  : s*s - 1 - index
383
//                     if b==s-1: 6*s*s -11*s +6 - index
384
//                     else
385
//                         a==0: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-2) + s
386
//                         else: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-1-a)
387
// Down  :   index --> b==0 ? (s-1-a) : s*s + s-1 + 4*(b-1)*(s-1) - a
388
// Left  :   index --> (s-1-a)*s + b
389
// Back  :   index --> if b==s-1: s*(s-1-a)
390
//                     if b==0  : 5*s*s -12*s + 8 + (s-1-a)*s
391
//                     else
392
//                        if a==s-1: s*s + 4*(s-2-b)*(s-1)
393
//                        else     : s*s + 4*(s-2-b)*(s-1) + s + (s-2-a)*2
394

    
395
  public String retObjectString()
396
    {
397
    StringBuilder objectString = new StringBuilder();
398
    int size = getSize();
399
    int len = size*size;
400
    int cubitIndex, row, col, color,face;
401

    
402
    final int RIGHT= 0;
403
    final int LEFT = 1;
404
    final int UP   = 2;
405
    final int DOWN = 3;
406
    final int FRONT= 4;
407
    final int BACK = 5;
408

    
409
    // 'I' - interior, theoretically can happen
410
    final char[] FACE_NAMES = { 'R', 'L', 'U', 'D', 'F', 'B', 'I'};
411

    
412
    face = UP;
413

    
414
    for(int i=0; i<len; i++)
415
      {
416
      row = i/size;
417
      col = i%size;
418

    
419
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
420
      color = getCubitFaceColorIndex(cubitIndex,face);
421
      objectString.append(FACE_NAMES[color]);
422
      }
423

    
424
    face = RIGHT;
425

    
426
    for(int i=0; i<len; i++)
427
      {
428
      cubitIndex = 6*size*size - 12*size +7 - i;
429
      color = getCubitFaceColorIndex(cubitIndex,face);
430
      objectString.append(FACE_NAMES[color]);
431
      }
432

    
433
     face = FRONT;
434

    
435
    for(int i=0; i<len; i++)
436
      {
437
      row = i/size;
438
      col = i%size;
439

    
440
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
441
      else if( col==0 ) cubitIndex = size*size - 1 - i;
442
      else
443
        {
444
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
445
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
446
        }
447

    
448
      color = getCubitFaceColorIndex(cubitIndex,face);
449
      objectString.append(FACE_NAMES[color]);
450
      }
451

    
452
    face = DOWN;
453

    
454
    for(int i=0; i<len; i++)
455
      {
456
      row = i/size;
457
      col = i%size;
458

    
459
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
460
      color = getCubitFaceColorIndex(cubitIndex,face);
461
      objectString.append(FACE_NAMES[color]);
462
      }
463

    
464
    face = LEFT;
465

    
466
    for(int i=0; i<len; i++)
467
      {
468
      row = i/size;
469
      col = i%size;
470

    
471
      cubitIndex = (size-1-row)*size + col;
472
      color = getCubitFaceColorIndex(cubitIndex,face);
473
      objectString.append(FACE_NAMES[color]);
474
      }
475

    
476
    face = BACK;
477

    
478
    for(int i=0; i<len; i++)
479
      {
480
      row = i/size;
481
      col = i%size;
482

    
483
      if( col==size-1 ) cubitIndex = size*(size-1-row);
484
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
485
      else
486
        {
487
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
488
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
489
        }
490

    
491
      color = getCubitFaceColorIndex(cubitIndex,face);
492
      objectString.append(FACE_NAMES[color]);
493
      }
494

    
495
    return objectString.toString();
496
    }
497
}
(11-11/19)