Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyCube.java @ 596d62a4

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

    
184
      FactoryCubit factory = FactoryCubit.getInstance();
185

    
186
      factory.createNewFaceTransform(VERTICES,VERT_INDEXES);
187
      mMeshes[index] = factory.createRoundedSolid(VERTICES, VERT_INDEXES,
188
                                                  bands, bandIndexes,
189
                                                  corners, cornerIndexes,
190
                                                  getNumCubitFaces() );
191
      }
192

    
193
    return mMeshes[index].copy(true);
194
    }
195

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

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

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

    
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208

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

    
214
    float diff = 0.5f*(numLayers-1);
215
    int currentPosition = 0;
216

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

    
225
    return tmp;
226
    }
227

    
228
///////////////////////////////////////////////////////////////////////////////////////////////////
229

    
230
  Static4D[] getQuats()
231
    {
232
    return QUATS;
233
    }
234

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

    
237
  boolean shouldResetTextureMaps()
238
    {
239
    return false;
240
    }
241

    
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243

    
244
  int getNumFaces()
245
    {
246
    return FACE_COLORS.length;
247
    }
248

    
249
///////////////////////////////////////////////////////////////////////////////////////////////////
250

    
251
  float[] getCuts(int numLayers)
252
    {
253
    float[] cuts = new float[numLayers-1];
254

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

    
260
    return cuts;
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264

    
265
  int getNumStickerTypes(int numLayers)
266
    {
267
    return STICKERS.length;
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

    
272
  int getNumCubitFaces()
273
    {
274
    return FACE_COLORS.length;
275
    }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278

    
279
  float getScreenRatio()
280
    {
281
    return 0.5f;
282
    }
283

    
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285

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

    
291
///////////////////////////////////////////////////////////////////////////////////////////////////
292

    
293
  float returnMultiplier()
294
    {
295
    return getNumLayers();
296
    }
297

    
298
///////////////////////////////////////////////////////////////////////////////////////////////////
299

    
300
  float[] getRowChances(int numLayers)
301
    {
302
    float[] chances = new float[numLayers];
303

    
304
    for(int i=0; i<numLayers; i++)
305
      {
306
      chances[i] = (i+1.0f) / numLayers;
307
      }
308

    
309
    return chances;
310
    }
311

    
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313
// PUBLIC API
314

    
315
  public Static3D[] getRotationAxis()
316
    {
317
    return ROT_AXIS;
318
    }
319

    
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321

    
322
  public int getBasicAngle()
323
    {
324
    return 4;
325
    }
326

    
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328

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

    
341
    float rowFloat = rnd.nextFloat();
342

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

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

    
361
///////////////////////////////////////////////////////////////////////////////////////////////////
362

    
363
  public boolean isSolved()
364
    {
365
    int index = CUBITS[0].mQuatIndex;
366

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

    
372
    return true;
373
    }
374

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

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

    
405
    final int RIGHT= 0;
406
    final int LEFT = 1;
407
    final int UP   = 2;
408
    final int DOWN = 3;
409
    final int FRONT= 4;
410
    final int BACK = 5;
411

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

    
415
    face = UP;
416

    
417
    for(int i=0; i<len; i++)
418
      {
419
      row = i/layers;
420
      col = i%layers;
421

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

    
427
    face = RIGHT;
428

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

    
436
    face = FRONT;
437

    
438
    for(int i=0; i<len; i++)
439
      {
440
      row = i/layers;
441
      col = i%layers;
442

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

    
451
      color = getCubitFaceColorIndex(cubitIndex,face);
452
      objectString.append(FACE_NAMES[color]);
453
      }
454

    
455
    face = DOWN;
456

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

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

    
467
    face = LEFT;
468

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

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

    
479
    face = BACK;
480

    
481
    for(int i=0; i<len; i++)
482
      {
483
      row = i/layers;
484
      col = i%layers;
485

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

    
494
      color = getCubitFaceColorIndex(cubitIndex,face);
495
      objectString.append(FACE_NAMES[color]);
496
      }
497

    
498
    return objectString.toString();
499
    }
500

    
501
///////////////////////////////////////////////////////////////////////////////////////////////////
502

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

    
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516

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

    
529
///////////////////////////////////////////////////////////////////////////////////////////////////
530

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