Project

General

Profile

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

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

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[] FACE_COLORS = new int[]
51
         {
52
           COLOR_YELLOW, COLOR_WHITE,
53
           COLOR_BLUE  , COLOR_GREEN,
54
           COLOR_RED   , COLOR_ORANGE
55
         };
56

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

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

    
84
         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
85
         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
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
         };
93

    
94
  private static final double[][] VERTICES = new double[][]
95
          {
96
              { 0.5, 0.5, 0.5 },
97
              { 0.5, 0.5,-0.5 },
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
          };
105

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

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

    
121
  private static MeshBase[] mMeshes;
122

    
123
///////////////////////////////////////////////////////////////////////////////////////////////////
124

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

    
131
///////////////////////////////////////////////////////////////////////////////////////////////////
132

    
133
  double[][] getVertices(int cubitType)
134
    {
135
    return VERTICES;
136
    }
137

    
138
///////////////////////////////////////////////////////////////////////////////////////////////////
139

    
140
  int[][] getVertIndexes(int cubitType)
141
    {
142
    return VERT_INDEXES;
143
    }
144

    
145
///////////////////////////////////////////////////////////////////////////////////////////////////
146
// in the sense of shape, there's always only 1 cubit type.
147

    
148
  int getNumCubitTypes(int numLayers)
149
    {
150
    return 1;
151
    }
152

    
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154

    
155
  MeshBase createCubitMesh(int cubit, int numLayers)
156
    {
157
    if( mMeshes==null )
158
      {
159
      FactoryCubit factory = FactoryCubit.getInstance();
160
      factory.clear();
161
      mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
162
      }
163

    
164
    int ordinal= ObjectList.CUBE.ordinal();
165
    int index  = ObjectList.getSizeIndex(ordinal,getNumLayers());
166

    
167
    if( mMeshes[index]==null )
168
      {
169
      int extraI, extraV, num;
170

    
171
      switch(numLayers)
172
        {
173
        case 2 : num = 6; extraI = 2; extraV = 2; break;
174
        case 3 : num = 5; extraI = 2; extraV = 2; break;
175
        case 4 : num = 5; extraI = 1; extraV = 2; break;
176
        default: num = 5; extraI = 1; extraV = 0; break;
177
        }
178

    
179
      float[][] bands     = new float[][] { {0.038f,35,0.5f,0.7f,num,extraI,extraV} };
180
      int[] bandIndexes   = new int[] { 0,0,0,0,0,0};
181
      float[][] corners   = new float[][] { {0.036f,0.12f} };
182
      int[] cornerIndexes = new int[] { 0,0,0,0,0,0,0,0 };
183
      float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
184
      int[] centerIndexes = new int[] { 0,0,0,0,0,0,0,0 };
185

    
186
      FactoryCubit factory = FactoryCubit.getInstance();
187

    
188
      factory.createNewFaceTransform(VERTICES,VERT_INDEXES);
189
      mMeshes[index] = factory.createRoundedSolid(VERTICES, VERT_INDEXES,
190
                                                  bands, bandIndexes,
191
                                                  corners, cornerIndexes,
192
                                                  centers, centerIndexes,
193
                                                  getNumCubitFaces() );
194
      }
195

    
196
    return mMeshes[index].copy(true);
197
    }
198

    
199
///////////////////////////////////////////////////////////////////////////////////////////////////
200

    
201
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
202
    {
203
    float R = 0.10f;
204
    float S = 0.08f;
205

    
206
    FactorySticker factory = FactorySticker.getInstance();
207
    factory.drawRoundedPolygon(canvas, paint, left, top, STICKERS[0], S, FACE_COLORS[face], R);
208
    }
209

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

    
212
  float[][] getCubitPositions(int numLayers)
213
    {
214
    int numCubits = numLayers>1 ? 6*numLayers*numLayers - 12*numLayers + 8 : 1;
215
    float[][] tmp = new float[numCubits][];
216

    
217
    float diff = 0.5f*(numLayers-1);
218
    int currentPosition = 0;
219

    
220
    for(int x = 0; x<numLayers; x++)
221
      for(int y = 0; y<numLayers; y++)
222
        for(int z = 0; z<numLayers; z++)
223
          if( x==0 || x==numLayers-1 || y==0 || y==numLayers-1 || z==0 || z==numLayers-1 )
224
            {
225
            tmp[currentPosition++] = new float[] {x-diff,y-diff,z-diff};
226
            }
227

    
228
    return tmp;
229
    }
230

    
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232

    
233
  Static4D[] getQuats()
234
    {
235
    return QUATS;
236
    }
237

    
238
///////////////////////////////////////////////////////////////////////////////////////////////////
239

    
240
  boolean shouldResetTextureMaps()
241
    {
242
    return false;
243
    }
244

    
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246

    
247
  int getNumFaces()
248
    {
249
    return FACE_COLORS.length;
250
    }
251

    
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253

    
254
  float[] getCuts(int numLayers)
255
    {
256
    float[] cuts = new float[numLayers-1];
257

    
258
    for(int i=0; i<numLayers-1; i++)
259
      {
260
      cuts[i] = (2-numLayers)*0.5f + i;
261
      }
262

    
263
    return cuts;
264
    }
265

    
266
///////////////////////////////////////////////////////////////////////////////////////////////////
267

    
268
  int getNumStickerTypes(int numLayers)
269
    {
270
    return STICKERS.length;
271
    }
272

    
273
///////////////////////////////////////////////////////////////////////////////////////////////////
274

    
275
  int getNumCubitFaces()
276
    {
277
    return FACE_COLORS.length;
278
    }
279

    
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281

    
282
  float getScreenRatio()
283
    {
284
    return 0.5f;
285
    }
286

    
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288

    
289
  int getFaceColor(int cubit, int cubitface, int size)
290
    {
291
    return CUBITS[cubit].mRotationRow[cubitface/2] == (cubitface%2==0 ? (1<<(size-1)):1) ? cubitface : NUM_FACES;
292
    }
293

    
294
///////////////////////////////////////////////////////////////////////////////////////////////////
295

    
296
  float returnMultiplier()
297
    {
298
    return getNumLayers();
299
    }
300

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302

    
303
  float[] getRowChances(int numLayers)
304
    {
305
    float[] chances = new float[numLayers];
306

    
307
    for(int i=0; i<numLayers; i++)
308
      {
309
      chances[i] = (i+1.0f) / numLayers;
310
      }
311

    
312
    return chances;
313
    }
314

    
315
///////////////////////////////////////////////////////////////////////////////////////////////////
316
// PUBLIC API
317

    
318
  public Static3D[] getRotationAxis()
319
    {
320
    return ROT_AXIS;
321
    }
322

    
323
///////////////////////////////////////////////////////////////////////////////////////////////////
324

    
325
  public int getBasicAngle()
326
    {
327
    return 4;
328
    }
329

    
330
///////////////////////////////////////////////////////////////////////////////////////////////////
331

    
332
  public void randomizeNewScramble(int[][] scramble, Random rnd, int num)
333
    {
334
    if( num==0 )
335
      {
336
      scramble[num][0] = rnd.nextInt(ROTATION_AXIS.length);
337
      }
338
    else
339
      {
340
      int newVector = rnd.nextInt(ROTATION_AXIS.length-1);
341
      scramble[num][0] = (newVector>=scramble[num-1][0] ? newVector+1 : newVector);
342
      }
343

    
344
    float rowFloat = rnd.nextFloat();
345

    
346
    for(int row=0; row<mRowChances.length; row++)
347
      {
348
      if( rowFloat<=mRowChances[row] )
349
        {
350
        scramble[num][1] = row;
351
        break;
352
        }
353
      }
354

    
355
    switch( rnd.nextInt(4) )
356
      {
357
      case 0: scramble[num][2] = -2; break;
358
      case 1: scramble[num][2] = -1; break;
359
      case 2: scramble[num][2] =  1; break;
360
      case 3: scramble[num][2] =  2; break;
361
      }
362
    }
363

    
364
///////////////////////////////////////////////////////////////////////////////////////////////////
365

    
366
  public boolean isSolved()
367
    {
368
    int index = CUBITS[0].mQuatIndex;
369

    
370
    for(int i=1; i<NUM_CUBITS; i++)
371
      {
372
      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
373
      }
374

    
375
    return true;
376
    }
377

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

    
401
  public String retObjectString()
402
    {
403
    StringBuilder objectString = new StringBuilder();
404
    int layers = getNumLayers();
405
    int len = layers*layers;
406
    int cubitIndex, row, col, color,face;
407

    
408
    final int RIGHT= 0;
409
    final int LEFT = 1;
410
    final int UP   = 2;
411
    final int DOWN = 3;
412
    final int FRONT= 4;
413
    final int BACK = 5;
414

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

    
418
    face = UP;
419

    
420
    for(int i=0; i<len; i++)
421
      {
422
      row = i/layers;
423
      col = i%layers;
424

    
425
      cubitIndex = col<layers-1 ? (layers-1)*(layers+4*col) + row : 6*layers*layers - 13*layers + 8 + row;
426
      color = getCubitFaceColorIndex(cubitIndex,face);
427
      objectString.append(FACE_NAMES[color]);
428
      }
429

    
430
    face = RIGHT;
431

    
432
    for(int i=0; i<len; i++)
433
      {
434
      cubitIndex = 6*layers*layers - 12*layers +7 - i;
435
      color = getCubitFaceColorIndex(cubitIndex,face);
436
      objectString.append(FACE_NAMES[color]);
437
      }
438

    
439
    face = FRONT;
440

    
441
    for(int i=0; i<len; i++)
442
      {
443
      row = i/layers;
444
      col = i%layers;
445

    
446
      if( col==layers-1 ) cubitIndex = 6*layers*layers - 11*layers + 6 -i;
447
      else if(   col==0 ) cubitIndex = layers*layers - 1 - i;
448
      else
449
        {
450
        if( row==0 ) cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-2) + layers;
451
        else         cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-1-row);
452
        }
453

    
454
      color = getCubitFaceColorIndex(cubitIndex,face);
455
      objectString.append(FACE_NAMES[color]);
456
      }
457

    
458
    face = DOWN;
459

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

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

    
470
    face = LEFT;
471

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

    
477
      cubitIndex = (layers-1-row)*layers + col;
478
      color = getCubitFaceColorIndex(cubitIndex,face);
479
      objectString.append(FACE_NAMES[color]);
480
      }
481

    
482
    face = BACK;
483

    
484
    for(int i=0; i<len; i++)
485
      {
486
      row = i/layers;
487
      col = i%layers;
488

    
489
      if( col==layers-1 ) cubitIndex = layers*(layers-1-row);
490
      else if(   col==0 ) cubitIndex = 5*layers*layers - 12*layers + 8 + (layers-1-row)*layers;
491
      else
492
        {
493
        if( row==layers-1 ) cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1);
494
        else                cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1) + layers + 2*(layers-2-row);
495
        }
496

    
497
      color = getCubitFaceColorIndex(cubitIndex,face);
498
      objectString.append(FACE_NAMES[color]);
499
      }
500

    
501
    return objectString.toString();
502
    }
503

    
504
///////////////////////////////////////////////////////////////////////////////////////////////////
505

    
506
  public int getObjectName(int numLayers)
507
    {
508
    switch(numLayers)
509
      {
510
      case 2: return R.string.cube2;
511
      case 3: return R.string.cube3;
512
      case 4: return R.string.cube4;
513
      case 5: return R.string.cube5;
514
      }
515
    return R.string.cube3;
516
    }
517

    
518
///////////////////////////////////////////////////////////////////////////////////////////////////
519

    
520
  public int getInventor(int numLayers)
521
    {
522
    switch(numLayers)
523
      {
524
      case 2: return R.string.cube2_inventor;
525
      case 3: return R.string.cube3_inventor;
526
      case 4: return R.string.cube4_inventor;
527
      case 5: return R.string.cube5_inventor;
528
      }
529
    return R.string.cube3_inventor;
530
    }
531

    
532
///////////////////////////////////////////////////////////////////////////////////////////////////
533

    
534
  public int getComplexity(int numLayers)
535
    {
536
    switch(numLayers)
537
      {
538
      case 2: return 4;
539
      case 3: return 6;
540
      case 4: return 8;
541
      case 5: return 10;
542
      }
543
    return 6;
544
    }
545
}
(19-19/33)