Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyCube.java @ f18e8fae

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
    final float R = TEXTURE_HEIGHT*0.10f;
133
    final float M = TEXTURE_HEIGHT*0.05f;
134

    
135
    paint.setColor(FACE_COLORS[face]);
136
    canvas.drawRoundRect( left+M, M, left+TEXTURE_HEIGHT-M, TEXTURE_HEIGHT-M, R, R, paint);
137
    }
138

    
139
///////////////////////////////////////////////////////////////////////////////////////////////////
140

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

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

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

    
157
    return tmp;
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

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

    
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168

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

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

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

    
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182

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

    
188
///////////////////////////////////////////////////////////////////////////////////////////////////
189

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

    
195
///////////////////////////////////////////////////////////////////////////////////////////////////
196

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

    
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203

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

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210

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

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218

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

    
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

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

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

    
236
    return chances;
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240
// PUBLIC API
241

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

    
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248

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

    
254
///////////////////////////////////////////////////////////////////////////////////////////////////
255

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

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

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

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

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

    
282
    return 0;
283
    }
284

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286

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

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

    
296
    return true;
297
    }
298

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
411
    face = UP;
412

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

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

    
423
    face = RIGHT;
424

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

    
432
     face = FRONT;
433

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

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

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

    
451
    face = DOWN;
452

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

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

    
463
    face = LEFT;
464

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

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

    
475
    face = BACK;
476

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

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

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

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