Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyCube.java @ 2adf1263

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.helpers.FactoryCubit;
27
import org.distorted.helpers.FactorySticker;
28
import org.distorted.library.main.DistortedEffects;
29
import org.distorted.library.main.DistortedTexture;
30
import org.distorted.library.mesh.MeshBase;
31
import org.distorted.library.mesh.MeshSquare;
32
import org.distorted.library.type.Static3D;
33
import org.distorted.library.type.Static4D;
34
import org.distorted.main.R;
35

    
36
import java.util.Random;
37

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

    
40
class TwistyCube extends TwistyObject
41
{
42
  // the three rotation axis of a RubikCube. Must be normalized.
43
  static final Static3D[] ROT_AXIS = new Static3D[]
44
         {
45
           new Static3D(1,0,0),
46
           new Static3D(0,1,0),
47
           new Static3D(0,0,1)
48
         };
49

    
50
  private static final int[] BASIC_ANGLE = new int[] { 4,4,4 };
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_ORANGE
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 final double[][] VERTICES = new double[][]
97
          {
98
              { 0.5, 0.5, 0.5 },
99
              { 0.5, 0.5,-0.5 },
100
              { 0.5,-0.5, 0.5 },
101
              { 0.5,-0.5,-0.5 },
102
              {-0.5, 0.5, 0.5 },
103
              {-0.5, 0.5,-0.5 },
104
              {-0.5,-0.5, 0.5 },
105
              {-0.5,-0.5,-0.5 },
106
          };
107

    
108
  private static final int[][] VERT_INDEXES = new int[][]
109
          {
110
              {2,3,1,0},   // counterclockwise!
111
              {7,6,4,5},
112
              {4,0,1,5},
113
              {7,3,2,6},
114
              {6,2,0,4},
115
              {3,7,5,1}
116
          };
117

    
118
  private static final float[][] STICKERS = new float[][]
119
          {
120
              { -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f }
121
          };
122

    
123
  private static MeshBase[] mMeshes;
124

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

    
127
  TwistyCube(int size, Static4D quat, DistortedTexture texture,
128
             MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
129
    {
130
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.CUBE, res, scrWidth);
131
    }
132

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134

    
135
  MeshBase createCubitMesh(int cubit, int numLayers)
136
    {
137
    if( mMeshes==null )
138
      {
139
      FactoryCubit factory = FactoryCubit.getInstance();
140
      factory.clear();
141
      mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
142
      }
143

    
144
    int ordinal= ObjectList.CUBE.ordinal();
145
    int index  = ObjectList.getSizeIndex(ordinal,getNumLayers());
146

    
147
    if( mMeshes[index]==null )
148
      {
149
      int extraI, extraV, num;
150
      float height;
151

    
152
      switch(numLayers)
153
        {
154
        case 2 : num = 6; extraI = 2; extraV = 2; height = 0.045f; break;
155
        case 3 : num = 5; extraI = 2; extraV = 2; height = 0.045f; break;
156
        case 4 : num = 5; extraI = 1; extraV = 1; height = 0.045f; break;
157
        default: num = 5; extraI = 0; extraV = 0; height = 0.045f; break;
158
        }
159

    
160
      float[][] bands     = new float[][] { {height,35,0.5f,0.7f,num,extraI,extraV} };
161
      int[] bandIndexes   = new int[] { 0,0,0,0,0,0};
162
      float[][] corners   = new float[][] { {0.036f,0.12f} };
163
      int[] cornerIndexes = new int[] { 0,0,0,0,0,0,0,0 };
164
      float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
165
      int[] centerIndexes = new int[] { 0,0,0,0,0,0,0,0 };
166

    
167
      FactoryCubit factory = FactoryCubit.getInstance();
168

    
169
      factory.createNewFaceTransform(VERTICES,VERT_INDEXES);
170
      mMeshes[index] = factory.createRoundedSolid(VERTICES, VERT_INDEXES,
171
                                                  bands, bandIndexes,
172
                                                  corners, cornerIndexes,
173
                                                  centers, centerIndexes,
174
                                                  getNumCubitFaces() );
175
      }
176

    
177
    return mMeshes[index].copy(true);
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
183
    {
184
    float R = 0.10f;
185
    float S = 0.08f;
186

    
187
    FactorySticker factory = FactorySticker.getInstance();
188
    factory.drawRoundedPolygon(canvas, paint, left, top, STICKERS[0], S, FACE_COLORS[face], R);
189
    }
190

    
191
///////////////////////////////////////////////////////////////////////////////////////////////////
192

    
193
  float[][] getCubitPositions(int numLayers)
194
    {
195
    int numCubits = numLayers>1 ? 6*numLayers*numLayers - 12*numLayers + 8 : 1;
196
    float[][] tmp = new float[numCubits][];
197

    
198
    float diff = 0.5f*(numLayers-1);
199
    int currentPosition = 0;
200

    
201
    for(int x = 0; x<numLayers; x++)
202
      for(int y = 0; y<numLayers; y++)
203
        for(int z = 0; z<numLayers; z++)
204
          if( x==0 || x==numLayers-1 || y==0 || y==numLayers-1 || z==0 || z==numLayers-1 )
205
            {
206
            tmp[currentPosition++] = new float[] {x-diff,y-diff,z-diff};
207
            }
208

    
209
    return tmp;
210
    }
211

    
212
///////////////////////////////////////////////////////////////////////////////////////////////////
213

    
214
  Static4D[] getQuats()
215
    {
216
    return QUATS;
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220

    
221
  boolean shouldResetTextureMaps()
222
    {
223
    return false;
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227

    
228
  int getNumFaces()
229
    {
230
    return FACE_COLORS.length;
231
    }
232

    
233
///////////////////////////////////////////////////////////////////////////////////////////////////
234

    
235
  float[][] getCuts(int numLayers)
236
    {
237
    float[][] cuts = new float[3][numLayers-1];
238

    
239
    for(int i=0; i<numLayers-1; i++)
240
      {
241
      float cut = (2-numLayers)*0.5f + i;
242
      cuts[0][i] = cut;
243
      cuts[1][i] = cut;
244
      cuts[2][i] = cut;
245
      }
246

    
247
    return cuts;
248
    }
249

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

    
252
  int getNumStickerTypes(int numLayers)
253
    {
254
    return STICKERS.length;
255
    }
256

    
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258

    
259
  int getNumCubitFaces()
260
    {
261
    return FACE_COLORS.length;
262
    }
263

    
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265

    
266
  float getScreenRatio()
267
    {
268
    return 0.5f;
269
    }
270

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

    
273
  int getFaceColor(int cubit, int cubitface, int size)
274
    {
275
    return CUBITS[cubit].mRotationRow[cubitface/2] == (cubitface%2==0 ? (1<<(size-1)):1) ? cubitface : NUM_FACES;
276
    }
277

    
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279

    
280
  float returnMultiplier()
281
    {
282
    return getNumLayers();
283
    }
284

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286
// PUBLIC API
287

    
288
  public Static3D[] getRotationAxis()
289
    {
290
    return ROT_AXIS;
291
    }
292

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

    
295
  public int[] getBasicAngle()
296
    {
297
    return BASIC_ANGLE;
298
    }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301

    
302
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
303
    {
304
    if( curr==0 )
305
      {
306
      scramble[curr][0] = rnd.nextInt(NUM_AXIS);
307
      }
308
    else
309
      {
310
      int newVector = rnd.nextInt(NUM_AXIS-1);
311
      scramble[curr][0] = (newVector>=scramble[curr-1][0] ? newVector+1 : newVector);
312

    
313
      // All three axis must be present among every four consecutive rotations.
314
      // Otherwise in case of odd-sized cubes we can get four consecutive rotations
315
      // that collapse to a NOP
316
      // (X,midLayer,180)->(Y,midLayer,180)->(X,midLayer,180)->(Y,midLayer,180) = NOP
317
      if( curr>=3 && scramble[curr-1][0]==scramble[curr-3][0] )
318
        {
319
        for( int ax=0; ax<NUM_AXIS; ax++)
320
          {
321
          if( scramble[curr-1][0]!=ax && scramble[curr-2][0]!=ax )
322
            {
323
            scramble[curr][0] = ax;
324
            break;
325
            }
326
          }
327
        }
328
      }
329

    
330
    float rowFloat = rnd.nextFloat();
331
    int numLayers = getNumLayers();
332

    
333
    for(int row=0; row<numLayers; row++)
334
      {
335
      if( rowFloat*numLayers <= row+1 )
336
        {
337
        scramble[curr][1] = row;
338
        break;
339
        }
340
      }
341

    
342
    switch( rnd.nextInt(4) )
343
      {
344
      case 0: scramble[curr][2] = -2; break;
345
      case 1: scramble[curr][2] = -1; break;
346
      case 2: scramble[curr][2] =  1; break;
347
      case 3: scramble[curr][2] =  2; break;
348
      }
349
    }
350

    
351
///////////////////////////////////////////////////////////////////////////////////////////////////
352

    
353
  public boolean isSolved()
354
    {
355
    int index = CUBITS[0].mQuatIndex;
356

    
357
    for(int i=1; i<NUM_CUBITS; i++)
358
      {
359
      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
360
      }
361

    
362
    return true;
363
    }
364

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

    
388
  public String retObjectString()
389
    {
390
    StringBuilder objectString = new StringBuilder();
391
    int layers = getNumLayers();
392
    int len = layers*layers;
393
    int cubitIndex, row, col, color,face;
394

    
395
    final int RIGHT= 0;
396
    final int LEFT = 1;
397
    final int UP   = 2;
398
    final int DOWN = 3;
399
    final int FRONT= 4;
400
    final int BACK = 5;
401

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

    
405
    face = UP;
406

    
407
    for(int i=0; i<len; i++)
408
      {
409
      row = i/layers;
410
      col = i%layers;
411

    
412
      cubitIndex = col<layers-1 ? (layers-1)*(layers+4*col) + row : 6*layers*layers - 13*layers + 8 + row;
413
      color = getCubitFaceColorIndex(cubitIndex,face);
414
      objectString.append(FACE_NAMES[color]);
415
      }
416

    
417
    face = RIGHT;
418

    
419
    for(int i=0; i<len; i++)
420
      {
421
      cubitIndex = 6*layers*layers - 12*layers +7 - i;
422
      color = getCubitFaceColorIndex(cubitIndex,face);
423
      objectString.append(FACE_NAMES[color]);
424
      }
425

    
426
    face = FRONT;
427

    
428
    for(int i=0; i<len; i++)
429
      {
430
      row = i/layers;
431
      col = i%layers;
432

    
433
      if( col==layers-1 ) cubitIndex = 6*layers*layers - 11*layers + 6 -i;
434
      else if(   col==0 ) cubitIndex = layers*layers - 1 - i;
435
      else
436
        {
437
        if( row==0 ) cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-2) + layers;
438
        else         cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-1-row);
439
        }
440

    
441
      color = getCubitFaceColorIndex(cubitIndex,face);
442
      objectString.append(FACE_NAMES[color]);
443
      }
444

    
445
    face = DOWN;
446

    
447
    for(int i=0; i<len; i++)
448
      {
449
      row = i/layers;
450
      col = i%layers;
451

    
452
      cubitIndex = col==0 ? layers-1-row : layers*layers + layers-1 + 4*(col-1)*(layers-1) - row;
453
      color = getCubitFaceColorIndex(cubitIndex,face);
454
      objectString.append(FACE_NAMES[color]);
455
      }
456

    
457
    face = LEFT;
458

    
459
    for(int i=0; i<len; i++)
460
      {
461
      row = i/layers;
462
      col = i%layers;
463

    
464
      cubitIndex = (layers-1-row)*layers + col;
465
      color = getCubitFaceColorIndex(cubitIndex,face);
466
      objectString.append(FACE_NAMES[color]);
467
      }
468

    
469
    face = BACK;
470

    
471
    for(int i=0; i<len; i++)
472
      {
473
      row = i/layers;
474
      col = i%layers;
475

    
476
      if( col==layers-1 ) cubitIndex = layers*(layers-1-row);
477
      else if(   col==0 ) cubitIndex = 5*layers*layers - 12*layers + 8 + (layers-1-row)*layers;
478
      else
479
        {
480
        if( row==layers-1 ) cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1);
481
        else                cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1) + layers + 2*(layers-2-row);
482
        }
483

    
484
      color = getCubitFaceColorIndex(cubitIndex,face);
485
      objectString.append(FACE_NAMES[color]);
486
      }
487

    
488
    return objectString.toString();
489
    }
490

    
491
///////////////////////////////////////////////////////////////////////////////////////////////////
492

    
493
  public int getObjectName(int numLayers)
494
    {
495
    switch(numLayers)
496
      {
497
      case 2: return R.string.cube2;
498
      case 3: return R.string.cube3;
499
      case 4: return R.string.cube4;
500
      case 5: return R.string.cube5;
501
      }
502
    return R.string.cube3;
503
    }
504

    
505
///////////////////////////////////////////////////////////////////////////////////////////////////
506

    
507
  public int getInventor(int numLayers)
508
    {
509
    switch(numLayers)
510
      {
511
      case 2: return R.string.cube2_inventor;
512
      case 3: return R.string.cube3_inventor;
513
      case 4: return R.string.cube4_inventor;
514
      case 5: return R.string.cube5_inventor;
515
      }
516
    return R.string.cube3_inventor;
517
    }
518

    
519
///////////////////////////////////////////////////////////////////////////////////////////////////
520

    
521
  public int getComplexity(int numLayers)
522
    {
523
    switch(numLayers)
524
      {
525
      case 2: return 4;
526
      case 3: return 6;
527
      case 4: return 8;
528
      case 5: return 10;
529
      }
530
    return 6;
531
    }
532
}
(22-22/41)