Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / RubikCube.java @ eab9d8f8

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 com.google.firebase.crashlytics.FirebaseCrashlytics;
27

    
28
import org.distorted.library.effect.VertexEffectDeform;
29
import org.distorted.library.effect.VertexEffectMove;
30
import org.distorted.library.effect.VertexEffectRotate;
31
import org.distorted.library.main.DistortedEffects;
32
import org.distorted.library.main.DistortedTexture;
33
import org.distorted.library.mesh.MeshBase;
34
import org.distorted.library.mesh.MeshJoined;
35
import org.distorted.library.mesh.MeshPolygon;
36
import org.distorted.library.mesh.MeshSquare;
37
import org.distorted.library.type.Static1D;
38
import org.distorted.library.type.Static3D;
39
import org.distorted.library.type.Static4D;
40
import org.distorted.main.RubikSurfaceView;
41

    
42
import java.util.Random;
43

    
44
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
45

    
46
///////////////////////////////////////////////////////////////////////////////////////////////////
47

    
48
class RubikCube extends RubikObject
49
{
50
  static final float SQ2 = (float)Math.sqrt(2);
51

    
52
  // the three rotation axis of a RubikCube. Must be normalized.
53
  static final Static3D[] ROT_AXIS = new Static3D[]
54
         {
55
           new Static3D(1,0,0),
56
           new Static3D(0,1,0),
57
           new Static3D(0,0,1)
58
         };
59

    
60
  // the six axis that determine the faces
61
  static final Static3D[] FACE_AXIS = new Static3D[]
62
         {
63
           new Static3D(1,0,0), new Static3D(-1,0,0),
64
           new Static3D(0,1,0), new Static3D(0,-1,0),
65
           new Static3D(0,0,1), new Static3D(0,0,-1)
66
         };
67

    
68
  private static final int[] FACE_COLORS = new int[]
69
         {
70
           0xffffff00, 0xffffffff,   // FACE_AXIS[0] (right-YELLOW) FACE_AXIS[1] (left  -WHITE)
71
           0xff0000ff, 0xff00ff00,   // FACE_AXIS[2] (top  -BLUE  ) FACE_AXIS[3] (bottom-GREEN)
72
           0xffff0000, 0xffb5651d    // FACE_AXIS[4] (front-RED   ) FACE_AXIS[5] (back  -BROWN)
73
         };
74

    
75
  // All legal rotation quats of a RubikCube of any size.
76
  // Here's how to compute this:
77
  // 1) compute how many rotations there are (RubikCube of any size = 24)
78
  // 2) take the AXIS, angles of rotation (90 in RubikCube's case) compute the basic quaternions
79
  // (i.e. rotations of 1 basic angle along each of the axis) and from there start semi-randomly
80
  // multiplying them and eventually you'll find all (24) legal rotations.
81
  // Example program in C, res/raw/compute_quats.c , is included.
82
  private static final Static4D[] QUATS = new Static4D[]
83
         {
84
         new Static4D(  0.0f,   0.0f,   0.0f,   1.0f),
85
         new Static4D(  1.0f,   0.0f,   0.0f,   0.0f),
86
         new Static4D(  0.0f,   1.0f,   0.0f,   0.0f),
87
         new Static4D(  0.0f,   0.0f,   1.0f,   0.0f),
88

    
89
         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
90
         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
91
         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
92
         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
93
         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
94
         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
95
         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
96
         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
97
         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
98
         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
99
         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
100
         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
101

    
102
         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
103
         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
104
         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
105
         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
106
         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
107
         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
108
         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
109
         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
110
         };
111

    
112
  private static MeshBase[] mMeshes;
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

    
116
  RubikCube(int size, Static4D quat, DistortedTexture texture,
117
            MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
118
    {
119
    super(size, 60, quat, texture, mesh, effects, moves, RubikObjectList.CUBE, res, scrWidth);
120
    }
121

    
122
///////////////////////////////////////////////////////////////////////////////////////////////////
123
// paint the square with upper-right corner at (left,top) and side length 'side' with texture
124
// for face 'face'.
125

    
126
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
127
    {
128
    final float R = side*0.10f;
129
    final float M = side*0.05f;
130

    
131
    paint.setColor(FACE_COLORS[face]);
132
    canvas.drawRoundRect( left+M, top+M, left+side-M, top+side-M, R, R, paint);
133
    }
134

    
135
///////////////////////////////////////////////////////////////////////////////////////////////////
136

    
137
  Static3D[] getCubitPositions(int size)
138
    {
139
    int numCubits = size>1 ? 6*size*size - 12*size + 8 : 1;
140
    Static3D[] tmp = new Static3D[numCubits];
141

    
142
    float diff = 0.5f*(size-1);
143
    int currentPosition = 0;
144

    
145
    for(int x = 0; x<size; x++)
146
      for(int y = 0; y<size; y++)
147
        for(int z = 0; z<size; z++)
148
          if( x==0 || x==size-1 || y==0 || y==size-1 || z==0 || z==size-1 )
149
            {
150
            tmp[currentPosition++] = new Static3D(x-diff,y-diff,z-diff);
151
            }
152

    
153
    return tmp;
154
    }
155

    
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157

    
158
  Static4D[] getQuats()
159
    {
160
    return QUATS;
161
    }
162

    
163
///////////////////////////////////////////////////////////////////////////////////////////////////
164

    
165
  int getNumFaces()
166
    {
167
    return FACE_COLORS.length;
168
    }
169

    
170
///////////////////////////////////////////////////////////////////////////////////////////////////
171

    
172
  int getNumStickerTypes()
173
    {
174
    return 1;
175
    }
176

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

    
179
  int getNumCubitFaces()
180
    {
181
    return FACE_COLORS.length;
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185

    
186
  float getScreenRatio()
187
    {
188
    return 0.5f;
189
    }
190

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

    
193
  int getFaceColor(int cubit, int cubitface, int size)
194
    {
195
    boolean belongs = isOnFace(cubit, cubitface/2, cubitface%2==0 ? size-1:0 );
196
    return belongs ? cubitface : NUM_FACES;
197
    }
198

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

    
201
  MeshBase createCubitMesh(int cubit)
202
    {
203
    int size   = getSize();
204
    int ordinal= RubikObjectList.CUBE.ordinal();
205
    int index  = RubikObjectList.getSizeIndex(ordinal,size);
206
    float[] bands;
207
    float D = 0.027f;
208
    float E = 0.5f-D;
209
    float[] vertices = { -E,-E, +E,-E, +E,+E, -E,+E };
210
    int extraI, extraV;
211

    
212
    switch(size)
213
      {
214
      case 2 : bands = new float[] { 1.0f    ,-D,
215
                                     1.0f-D/2,-D*0.55f,
216
                                     1.0f-D  ,-D*0.25f,
217
                                     1.0f-2*D, 0.0f,
218
                                     0.50f, 0.040f,
219
                                     0.0f, 0.048f };
220
               extraI = 2;
221
               extraV = 2;
222
               break;
223
      case 3 : bands = new float[] { 1.0f    ,-D,
224
                                     1.0f-D*1.2f,-D*0.55f,
225
                                     1.0f-2*D, 0.0f,
226
                                     0.50f, 0.040f,
227
                                     0.0f, 0.048f };
228
               extraI = 2;
229
               extraV = 2;
230
               break;
231
      case 4 : bands = new float[] { 1.0f    ,-D,
232
                                     1.0f-D*1.2f,-D*0.55f,
233
                                     1.0f-2*D, 0.0f,
234
                                     0.50f, 0.040f,
235
                                     0.0f, 0.048f };
236
               extraI = 1;
237
               extraV = 2;
238
               break;
239
      default: bands = new float[] { 1.0f    ,-D,
240
                                     1.0f-2*D, 0.0f,
241
                                     0.50f, 0.025f,
242
                                     0.0f, 0.030f };
243
               extraI = 1;
244
               extraV = 1;
245
               break;
246
      }
247

    
248
    return createCubitMesh(index,vertices,bands,extraI,extraV);
249
    }
250

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

    
253
  MeshBase createCubitMesh(int index, float[] vertices, float[] bands, int extraI, int extraV)
254
    {
255
    if( mMeshes==null )
256
      {
257
      mMeshes = new MeshBase[RubikObjectList.CUBE.getNumVariants()];
258
      }
259

    
260
    if( mMeshes[index]==null )
261
      {
262
      final int MESHES=6;
263
      int association = 1;
264
      MeshBase[] meshes = new MeshPolygon[MESHES];
265
      meshes[0] = new MeshPolygon(vertices,bands,extraI,extraV);
266
      meshes[0].setEffectAssociation(0,association,0);
267

    
268
      for(int i=1; i<MESHES; i++)
269
        {
270
        association <<=1;
271
        meshes[i] = meshes[0].copy(true);
272
        meshes[i].setEffectAssociation(0,association,0);
273
        }
274

    
275
      mMeshes[index] = new MeshJoined(meshes);
276

    
277
      Static3D axisY   = new Static3D(0,1,0);
278
      Static3D axisX   = new Static3D(1,0,0);
279
      Static3D center  = new Static3D(0,0,0);
280
      Static1D angle90 = new Static1D(90);
281
      Static1D angle180= new Static1D(180);
282
      Static1D angle270= new Static1D(270);
283

    
284
      float d1 = 1.0f;
285
      float d2 =-0.05f;
286
      float d3 = 0.12f;
287

    
288
      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
289
      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
290
      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
291
      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
292
      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
293
      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
294
      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
295
      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
296

    
297
      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
298
      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
299
      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
300
      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
301
      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
302
      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
303
      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
304
      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
305

    
306
      Static4D dReg  = new Static4D(0,0,0,d3);
307
      Static1D dRad  = new Static1D(1);
308

    
309
      VertexEffectMove   effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
310
      effect0.setMeshAssociation(63,-1);  // all 6 sides
311
      VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
312
      effect1.setMeshAssociation(32,-1);  // back
313
      VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
314
      effect2.setMeshAssociation( 8,-1);  // bottom
315
      VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
316
      effect3.setMeshAssociation( 4,-1);  // top
317
      VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
318
      effect4.setMeshAssociation( 2,-1);  // left
319
      VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
320
      effect5.setMeshAssociation( 1,-1);  // right
321

    
322
      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
323
      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
324
      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
325
      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
326
      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
327
      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
328
      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
329
      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
330

    
331
      mMeshes[index].apply(effect0);
332
      mMeshes[index].apply(effect1);
333
      mMeshes[index].apply(effect2);
334
      mMeshes[index].apply(effect3);
335
      mMeshes[index].apply(effect4);
336
      mMeshes[index].apply(effect5);
337
      mMeshes[index].apply(effect6);
338
      mMeshes[index].apply(effect7);
339
      mMeshes[index].apply(effect8);
340
      mMeshes[index].apply(effect9);
341
      mMeshes[index].apply(effect10);
342
      mMeshes[index].apply(effect11);
343
      mMeshes[index].apply(effect12);
344
      mMeshes[index].apply(effect13);
345

    
346
      mMeshes[index].mergeEffComponents();
347
      }
348

    
349
    return mMeshes[index].copy(true);
350
    }
351

    
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353

    
354
  float returnMultiplier()
355
    {
356
    return getSize();
357
    }
358

    
359
///////////////////////////////////////////////////////////////////////////////////////////////////
360

    
361
  float[] getRowChances()
362
    {
363
    int size = getSize();
364
    float[] chances = new float[size];
365

    
366
    for(int i=0; i<size; i++)
367
      {
368
      chances[i] = (i+1.0f) / size;
369
      }
370

    
371
    return chances;
372
    }
373

    
374
///////////////////////////////////////////////////////////////////////////////////////////////////
375
// PUBLIC API
376

    
377
  public Static3D[] getRotationAxis()
378
    {
379
    return ROT_AXIS;
380
    }
381

    
382
///////////////////////////////////////////////////////////////////////////////////////////////////
383

    
384
  public int getBasicAngle()
385
    {
386
    return 4;
387
    }
388

    
389
///////////////////////////////////////////////////////////////////////////////////////////////////
390

    
391
  public int computeRowFromOffset(float offset)
392
    {
393
    return (int)(getSize()*offset);
394
    }
395

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397

    
398
  public float returnRotationFactor(float offset)
399
    {
400
    return 1.0f;
401
    }
402

    
403
///////////////////////////////////////////////////////////////////////////////////////////////////
404

    
405
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
406
    {
407
    int numAxis = ROTATION_AXIS.length;
408

    
409
    if( oldRotAxis == START_AXIS )
410
      {
411
      return rnd.nextInt(numAxis);
412
      }
413
    else
414
      {
415
      int newVector = rnd.nextInt(numAxis-1);
416
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
417
      }
418
    }
419

    
420
///////////////////////////////////////////////////////////////////////////////////////////////////
421

    
422
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
423
    {
424
    float rowFloat = rnd.nextFloat();
425

    
426
    for(int row=0; row<mRowChances.length; row++)
427
      {
428
      if( rowFloat<=mRowChances[row] ) return row;
429
      }
430

    
431
    return 0;
432
    }
433

    
434
///////////////////////////////////////////////////////////////////////////////////////////////////
435

    
436
  public boolean isSolved()
437
    {
438
    int index = CUBITS[0].mQuatIndex;
439

    
440
    for(int i=1; i<NUM_CUBITS; i++)
441
      {
442
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
443
      }
444

    
445
    return true;
446
    }
447

    
448
///////////////////////////////////////////////////////////////////////////////////////////////////
449
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
450
// then if it were rotated by quaternion 'quat'.
451
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
452
// middle squares get interchanged. No visible difference!
453
//
454
// So: this is true iff the cubit
455
// a) is a corner or edge and the quaternions are the same
456
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
457

    
458
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
459
    {
460
    if ( cubit.mQuatIndex == quatIndex ) return true;
461

    
462
    int belongsToHowManyFaces = 0;
463
    int size = getSize()-1;
464
    float row;
465
    final float MAX_ERROR = 0.01f;
466

    
467
    for(int i=0; i<NUM_AXIS; i++)
468
      {
469
      row = cubit.mRotationRow[i];
470
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
471
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
472
      }
473

    
474
    switch(belongsToHowManyFaces)
475
      {
476
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
477
      case 1 :                // cubit that lies inside one of the faces
478
               Static3D orig = cubit.getOrigPosition();
479
               Static4D quat1 = QUATS[quatIndex];
480
               Static4D quat2 = QUATS[cubit.mQuatIndex];
481

    
482
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
483
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
484
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
485

    
486
               float row1, row2, row3, row4;
487
               float ax,ay,az;
488
               Static3D axis;
489
               float x1 = rotated1.get0();
490
               float y1 = rotated1.get1();
491
               float z1 = rotated1.get2();
492
               float x2 = rotated2.get0();
493
               float y2 = rotated2.get1();
494
               float z2 = rotated2.get2();
495

    
496
               for(int i=0; i<NUM_AXIS; i++)
497
                 {
498
                 axis = ROTATION_AXIS[i];
499
                 ax = axis.get0();
500
                 ay = axis.get1();
501
                 az = axis.get2();
502

    
503
                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
504
                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
505
                 row3 = row1 - size;
506
                 row4 = row2 - size;
507

    
508
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
509
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
510
                   {
511
                   return true;
512
                   }
513
                 }
514
               return false;
515

    
516
      default: return false;  // edge or corner
517
      }
518
    }
519

    
520
///////////////////////////////////////////////////////////////////////////////////////////////////
521
// order: Up --> Right --> Front --> Down --> Left --> Back
522
// (because the first implemented Solver - the two-phase Cube3 one - expects such order)
523
//
524
// Solved 3x3x3 Cube maps to "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB"
525
//
526
// s : size of the cube; let index = a*s + b    (i.e. a,b = row,column)
527
//
528
// Up    :   index --> b<s-1 ? (s-1)*(s+4b)+a : 6*s*s -13*s +8 +a
529
// Right :   index --> 6*s*s - 12*s + 7 - index
530
// Front :   index --> if b==0  : s*s - 1 - index
531
//                     if b==s-1: 6*s*s -11*s +6 - index
532
//                     else
533
//                         a==0: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-2) + s
534
//                         else: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-1-a)
535
// Down  :   index --> b==0 ? (s-1-a) : s*s + s-1 + 4*(b-1)*(s-1) - a
536
// Left  :   index --> (s-1-a)*s + b
537
// Back  :   index --> if b==s-1: s*(s-1-a)
538
//                     if b==0  : 5*s*s -12*s + 8 + (s-1-a)*s
539
//                     else
540
//                        if a==s-1: s*s + 4*(s-2-b)*(s-1)
541
//                        else     : s*s + 4*(s-2-b)*(s-1) + s + (s-2-a)*2
542

    
543
  public String retObjectString()
544
    {
545
    StringBuilder objectString = new StringBuilder();
546
    int size = getSize();
547
    int len = size*size;
548
    int cubitIndex=-1, row=-1, col=-1;
549
    int color=-1, face=-1;
550

    
551
    final int RIGHT= 0;
552
    final int LEFT = 1;
553
    final int UP   = 2;
554
    final int DOWN = 3;
555
    final int FRONT= 4;
556
    final int BACK = 5;
557

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

    
561
    face = UP;
562

    
563
    for(int i=0; i<len; i++)
564
      {
565
      row = i/size;
566
      col = i%size;
567

    
568
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
569
      color = getCubitFaceColorIndex(cubitIndex,face);
570
      objectString.append(FACE_NAMES[color]);
571
      }
572

    
573
    face = RIGHT;
574

    
575
    for(int i=0; i<len; i++)
576
      {
577
      cubitIndex = 6*size*size - 12*size +7 - i;
578
      color = getCubitFaceColorIndex(cubitIndex,face);
579
      objectString.append(FACE_NAMES[color]);
580
      }
581

    
582
     face = FRONT;
583

    
584
    for(int i=0; i<len; i++)
585
      {
586
      row = i/size;
587
      col = i%size;
588

    
589
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
590
      else if( col==0 ) cubitIndex = size*size - 1 - i;
591
      else
592
        {
593
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
594
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
595
        }
596

    
597
      color = getCubitFaceColorIndex(cubitIndex,face);
598
      objectString.append(FACE_NAMES[color]);
599
      }
600

    
601
    face = DOWN;
602

    
603
    for(int i=0; i<len; i++)
604
      {
605
      row = i/size;
606
      col = i%size;
607

    
608
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
609
      color = getCubitFaceColorIndex(cubitIndex,face);
610
      objectString.append(FACE_NAMES[color]);
611
      }
612

    
613
    face = LEFT;
614

    
615
    for(int i=0; i<len; i++)
616
      {
617
      row = i/size;
618
      col = i%size;
619

    
620
      cubitIndex = (size-1-row)*size + col;
621
      color = getCubitFaceColorIndex(cubitIndex,face);
622
      objectString.append(FACE_NAMES[color]);
623
      }
624

    
625
    face = BACK;
626

    
627
    for(int i=0; i<len; i++)
628
      {
629
      row = i/size;
630
      col = i%size;
631

    
632
      if( col==size-1 ) cubitIndex = size*(size-1-row);
633
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
634
      else
635
        {
636
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
637
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
638
        }
639

    
640
      color = getCubitFaceColorIndex(cubitIndex,face);
641
      objectString.append(FACE_NAMES[color]);
642
      }
643

    
644
    return objectString.toString();
645
    }
646
}
(2-2/12)