Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyCube.java @ 57e86ed0

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 num)
303
    {
304
    if( num==0 )
305
      {
306
      scramble[num][0] = rnd.nextInt(NUM_AXIS);
307
      }
308
    else
309
      {
310
      int newVector = rnd.nextInt(NUM_AXIS -1);
311
      scramble[num][0] = (newVector>=scramble[num-1][0] ? newVector+1 : newVector);
312
      }
313

    
314
    float rowFloat = rnd.nextFloat();
315
    int numLayers = getNumLayers();
316

    
317
    for(int row=0; row<numLayers; row++)
318
      {
319
      if( rowFloat*numLayers <= row+1 )
320
        {
321
        scramble[num][1] = row;
322
        break;
323
        }
324
      }
325

    
326
    switch( rnd.nextInt(4) )
327
      {
328
      case 0: scramble[num][2] = -2; break;
329
      case 1: scramble[num][2] = -1; break;
330
      case 2: scramble[num][2] =  1; break;
331
      case 3: scramble[num][2] =  2; break;
332
      }
333
    }
334

    
335
///////////////////////////////////////////////////////////////////////////////////////////////////
336

    
337
  public boolean isSolved()
338
    {
339
    int index = CUBITS[0].mQuatIndex;
340

    
341
    for(int i=1; i<NUM_CUBITS; i++)
342
      {
343
      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
344
      }
345

    
346
    return true;
347
    }
348

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

    
372
  public String retObjectString()
373
    {
374
    StringBuilder objectString = new StringBuilder();
375
    int layers = getNumLayers();
376
    int len = layers*layers;
377
    int cubitIndex, row, col, color,face;
378

    
379
    final int RIGHT= 0;
380
    final int LEFT = 1;
381
    final int UP   = 2;
382
    final int DOWN = 3;
383
    final int FRONT= 4;
384
    final int BACK = 5;
385

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

    
389
    face = UP;
390

    
391
    for(int i=0; i<len; i++)
392
      {
393
      row = i/layers;
394
      col = i%layers;
395

    
396
      cubitIndex = col<layers-1 ? (layers-1)*(layers+4*col) + row : 6*layers*layers - 13*layers + 8 + row;
397
      color = getCubitFaceColorIndex(cubitIndex,face);
398
      objectString.append(FACE_NAMES[color]);
399
      }
400

    
401
    face = RIGHT;
402

    
403
    for(int i=0; i<len; i++)
404
      {
405
      cubitIndex = 6*layers*layers - 12*layers +7 - i;
406
      color = getCubitFaceColorIndex(cubitIndex,face);
407
      objectString.append(FACE_NAMES[color]);
408
      }
409

    
410
    face = FRONT;
411

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

    
417
      if( col==layers-1 ) cubitIndex = 6*layers*layers - 11*layers + 6 -i;
418
      else if(   col==0 ) cubitIndex = layers*layers - 1 - i;
419
      else
420
        {
421
        if( row==0 ) cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-2) + layers;
422
        else         cubitIndex = layers*layers + layers-1 + 4*(col-1)*(layers-1) + 2*(layers-1-row);
423
        }
424

    
425
      color = getCubitFaceColorIndex(cubitIndex,face);
426
      objectString.append(FACE_NAMES[color]);
427
      }
428

    
429
    face = DOWN;
430

    
431
    for(int i=0; i<len; i++)
432
      {
433
      row = i/layers;
434
      col = i%layers;
435

    
436
      cubitIndex = col==0 ? layers-1-row : layers*layers + layers-1 + 4*(col-1)*(layers-1) - row;
437
      color = getCubitFaceColorIndex(cubitIndex,face);
438
      objectString.append(FACE_NAMES[color]);
439
      }
440

    
441
    face = LEFT;
442

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

    
448
      cubitIndex = (layers-1-row)*layers + col;
449
      color = getCubitFaceColorIndex(cubitIndex,face);
450
      objectString.append(FACE_NAMES[color]);
451
      }
452

    
453
    face = BACK;
454

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

    
460
      if( col==layers-1 ) cubitIndex = layers*(layers-1-row);
461
      else if(   col==0 ) cubitIndex = 5*layers*layers - 12*layers + 8 + (layers-1-row)*layers;
462
      else
463
        {
464
        if( row==layers-1 ) cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1);
465
        else                cubitIndex = layers*layers + 4*(layers-2-col)*(layers-1) + layers + 2*(layers-2-row);
466
        }
467

    
468
      color = getCubitFaceColorIndex(cubitIndex,face);
469
      objectString.append(FACE_NAMES[color]);
470
      }
471

    
472
    return objectString.toString();
473
    }
474

    
475
///////////////////////////////////////////////////////////////////////////////////////////////////
476

    
477
  public int getObjectName(int numLayers)
478
    {
479
    switch(numLayers)
480
      {
481
      case 2: return R.string.cube2;
482
      case 3: return R.string.cube3;
483
      case 4: return R.string.cube4;
484
      case 5: return R.string.cube5;
485
      }
486
    return R.string.cube3;
487
    }
488

    
489
///////////////////////////////////////////////////////////////////////////////////////////////////
490

    
491
  public int getInventor(int numLayers)
492
    {
493
    switch(numLayers)
494
      {
495
      case 2: return R.string.cube2_inventor;
496
      case 3: return R.string.cube3_inventor;
497
      case 4: return R.string.cube4_inventor;
498
      case 5: return R.string.cube5_inventor;
499
      }
500
    return R.string.cube3_inventor;
501
    }
502

    
503
///////////////////////////////////////////////////////////////////////////////////////////////////
504

    
505
  public int getComplexity(int numLayers)
506
    {
507
    switch(numLayers)
508
      {
509
      case 2: return 4;
510
      case 3: return 6;
511
      case 4: return 8;
512
      case 5: return 10;
513
      }
514
    return 6;
515
    }
516
}
(21-21/39)