Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyCube.java @ 7d8cc029

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
  MeshBase createCubitMesh(int cubit, int numLayers)
134
    {
135
    if( mMeshes==null )
136
      {
137
      FactoryCubit factory = FactoryCubit.getInstance();
138
      factory.clear();
139
      mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
140
      }
141

    
142
    int ordinal= ObjectList.CUBE.ordinal();
143
    int index  = ObjectList.getSizeIndex(ordinal,getNumLayers());
144

    
145
    if( mMeshes[index]==null )
146
      {
147
      int extraI, extraV, num;
148

    
149
      switch(numLayers)
150
        {
151
        case 2 : num = 6; extraI = 2; extraV = 2; break;
152
        case 3 : num = 5; extraI = 2; extraV = 2; break;
153
        case 4 : num = 5; extraI = 1; extraV = 2; break;
154
        default: num = 5; extraI = 1; extraV = 0; break;
155
        }
156

    
157
      float[][] bands     = new float[][] { {0.038f,35,0.5f,0.7f,num,extraI,extraV} };
158
      int[] bandIndexes   = new int[] { 0,0,0,0,0,0};
159
      float[][] corners   = new float[][] { {0.036f,0.12f} };
160
      int[] cornerIndexes = new int[] { 0,0,0,0,0,0,0,0 };
161
      float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
162
      int[] centerIndexes = new int[] { 0,0,0,0,0,0,0,0 };
163

    
164
      FactoryCubit factory = FactoryCubit.getInstance();
165

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

    
174
    return mMeshes[index].copy(true);
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178

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

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

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

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

    
195
    float diff = 0.5f*(numLayers-1);
196
    int currentPosition = 0;
197

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

    
206
    return tmp;
207
    }
208

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

    
211
  Static4D[] getQuats()
212
    {
213
    return QUATS;
214
    }
215

    
216
///////////////////////////////////////////////////////////////////////////////////////////////////
217

    
218
  boolean shouldResetTextureMaps()
219
    {
220
    return false;
221
    }
222

    
223
///////////////////////////////////////////////////////////////////////////////////////////////////
224

    
225
  int getNumFaces()
226
    {
227
    return FACE_COLORS.length;
228
    }
229

    
230
///////////////////////////////////////////////////////////////////////////////////////////////////
231

    
232
  float[] getCuts(int numLayers)
233
    {
234
    float[] cuts = new float[numLayers-1];
235

    
236
    for(int i=0; i<numLayers-1; i++)
237
      {
238
      cuts[i] = (2-numLayers)*0.5f + i;
239
      }
240

    
241
    return cuts;
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

    
246
  int getNumStickerTypes(int numLayers)
247
    {
248
    return STICKERS.length;
249
    }
250

    
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252

    
253
  int getNumCubitFaces()
254
    {
255
    return FACE_COLORS.length;
256
    }
257

    
258
///////////////////////////////////////////////////////////////////////////////////////////////////
259

    
260
  float getScreenRatio()
261
    {
262
    return 0.5f;
263
    }
264

    
265
///////////////////////////////////////////////////////////////////////////////////////////////////
266

    
267
  int getFaceColor(int cubit, int cubitface, int size)
268
    {
269
    return CUBITS[cubit].mRotationRow[cubitface/2] == (cubitface%2==0 ? (1<<(size-1)):1) ? cubitface : NUM_FACES;
270
    }
271

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

    
274
  float returnMultiplier()
275
    {
276
    return getNumLayers();
277
    }
278

    
279
///////////////////////////////////////////////////////////////////////////////////////////////////
280

    
281
  float[] getRowChances(int numLayers)
282
    {
283
    float[] chances = new float[numLayers];
284

    
285
    for(int i=0; i<numLayers; i++)
286
      {
287
      chances[i] = (i+1.0f) / numLayers;
288
      }
289

    
290
    return chances;
291
    }
292

    
293
///////////////////////////////////////////////////////////////////////////////////////////////////
294
// PUBLIC API
295

    
296
  public Static3D[] getRotationAxis()
297
    {
298
    return ROT_AXIS;
299
    }
300

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

    
303
  public int getBasicAngle()
304
    {
305
    return 4;
306
    }
307

    
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309

    
310
  public void randomizeNewScramble(int[][] scramble, Random rnd, int num)
311
    {
312
    if( num==0 )
313
      {
314
      scramble[num][0] = rnd.nextInt(ROTATION_AXIS.length);
315
      }
316
    else
317
      {
318
      int newVector = rnd.nextInt(ROTATION_AXIS.length-1);
319
      scramble[num][0] = (newVector>=scramble[num-1][0] ? newVector+1 : newVector);
320
      }
321

    
322
    float rowFloat = rnd.nextFloat();
323

    
324
    for(int row=0; row<mRowChances.length; row++)
325
      {
326
      if( rowFloat<=mRowChances[row] )
327
        {
328
        scramble[num][1] = row;
329
        break;
330
        }
331
      }
332

    
333
    switch( rnd.nextInt(4) )
334
      {
335
      case 0: scramble[num][2] = -2; break;
336
      case 1: scramble[num][2] = -1; break;
337
      case 2: scramble[num][2] =  1; break;
338
      case 3: scramble[num][2] =  2; break;
339
      }
340
    }
341

    
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343

    
344
  public boolean isSolved()
345
    {
346
    int index = CUBITS[0].mQuatIndex;
347

    
348
    for(int i=1; i<NUM_CUBITS; i++)
349
      {
350
      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
351
      }
352

    
353
    return true;
354
    }
355

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

    
379
  public String retObjectString()
380
    {
381
    StringBuilder objectString = new StringBuilder();
382
    int layers = getNumLayers();
383
    int len = layers*layers;
384
    int cubitIndex, row, col, color,face;
385

    
386
    final int RIGHT= 0;
387
    final int LEFT = 1;
388
    final int UP   = 2;
389
    final int DOWN = 3;
390
    final int FRONT= 4;
391
    final int BACK = 5;
392

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

    
396
    face = UP;
397

    
398
    for(int i=0; i<len; i++)
399
      {
400
      row = i/layers;
401
      col = i%layers;
402

    
403
      cubitIndex = col<layers-1 ? (layers-1)*(layers+4*col) + row : 6*layers*layers - 13*layers + 8 + row;
404
      color = getCubitFaceColorIndex(cubitIndex,face);
405
      objectString.append(FACE_NAMES[color]);
406
      }
407

    
408
    face = RIGHT;
409

    
410
    for(int i=0; i<len; i++)
411
      {
412
      cubitIndex = 6*layers*layers - 12*layers +7 - i;
413
      color = getCubitFaceColorIndex(cubitIndex,face);
414
      objectString.append(FACE_NAMES[color]);
415
      }
416

    
417
    face = FRONT;
418

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

    
424
      if( col==layers-1 ) cubitIndex = 6*layers*layers - 11*layers + 6 -i;
425
      else if(   col==0 ) cubitIndex = layers*layers - 1 - i;
426
      else
427
        {
428
        if( row==0 ) cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-2) + layers;
429
        else         cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-1-row);
430
        }
431

    
432
      color = getCubitFaceColorIndex(cubitIndex,face);
433
      objectString.append(FACE_NAMES[color]);
434
      }
435

    
436
    face = DOWN;
437

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

    
443
      cubitIndex = col==0 ? layers-1-row : layers*layers + layers-1 + 4*(col-1)*(layers-1) - row;
444
      color = getCubitFaceColorIndex(cubitIndex,face);
445
      objectString.append(FACE_NAMES[color]);
446
      }
447

    
448
    face = LEFT;
449

    
450
    for(int i=0; i<len; i++)
451
      {
452
      row = i/layers;
453
      col = i%layers;
454

    
455
      cubitIndex = (layers-1-row)*layers + col;
456
      color = getCubitFaceColorIndex(cubitIndex,face);
457
      objectString.append(FACE_NAMES[color]);
458
      }
459

    
460
    face = BACK;
461

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

    
467
      if( col==layers-1 ) cubitIndex = layers*(layers-1-row);
468
      else if(   col==0 ) cubitIndex = 5*layers*layers - 12*layers + 8 + (layers-1-row)*layers;
469
      else
470
        {
471
        if( row==layers-1 ) cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1);
472
        else                cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1) + layers + 2*(layers-2-row);
473
        }
474

    
475
      color = getCubitFaceColorIndex(cubitIndex,face);
476
      objectString.append(FACE_NAMES[color]);
477
      }
478

    
479
    return objectString.toString();
480
    }
481

    
482
///////////////////////////////////////////////////////////////////////////////////////////////////
483

    
484
  public int getObjectName(int numLayers)
485
    {
486
    switch(numLayers)
487
      {
488
      case 2: return R.string.cube2;
489
      case 3: return R.string.cube3;
490
      case 4: return R.string.cube4;
491
      case 5: return R.string.cube5;
492
      }
493
    return R.string.cube3;
494
    }
495

    
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497

    
498
  public int getInventor(int numLayers)
499
    {
500
    switch(numLayers)
501
      {
502
      case 2: return R.string.cube2_inventor;
503
      case 3: return R.string.cube3_inventor;
504
      case 4: return R.string.cube4_inventor;
505
      case 5: return R.string.cube5_inventor;
506
      }
507
    return R.string.cube3_inventor;
508
    }
509

    
510
///////////////////////////////////////////////////////////////////////////////////////////////////
511

    
512
  public int getComplexity(int numLayers)
513
    {
514
    switch(numLayers)
515
      {
516
      case 2: return 4;
517
      case 3: return 6;
518
      case 4: return 8;
519
      case 5: return 10;
520
      }
521
    return 6;
522
    }
523
}
(19-19/33)