Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / RubikCube.java @ 3717a94e

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.library.effect.VertexEffectDeform;
27
import org.distorted.library.effect.VertexEffectMove;
28
import org.distorted.library.effect.VertexEffectRotate;
29
import org.distorted.library.main.DistortedEffects;
30
import org.distorted.library.main.DistortedTexture;
31
import org.distorted.library.mesh.MeshBase;
32
import org.distorted.library.mesh.MeshJoined;
33
import org.distorted.library.mesh.MeshPolygon;
34
import org.distorted.library.mesh.MeshSquare;
35
import org.distorted.library.type.Static1D;
36
import org.distorted.library.type.Static3D;
37
import org.distorted.library.type.Static4D;
38
import org.distorted.main.RubikSurfaceView;
39

    
40
import java.util.Random;
41

    
42
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
43

    
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45

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

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

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

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

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

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

    
100
         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
101
         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
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
         };
109

    
110
  private static MeshBase[] mMeshes;
111

    
112
///////////////////////////////////////////////////////////////////////////////////////////////////
113

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

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

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

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

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134

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

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

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

    
151
    return tmp;
152
    }
153

    
154
///////////////////////////////////////////////////////////////////////////////////////////////////
155

    
156
  Static4D[] getQuats()
157
    {
158
    return QUATS;
159
    }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

    
163
  int getNumFaces()
164
    {
165
    return FACE_COLORS.length;
166
    }
167

    
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169

    
170
  float getBasicStep()
171
    {
172
    return 1.0f;
173
    }
174

    
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176

    
177
  int getNumStickerTypes()
178
    {
179
    return 1;
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
  int getNumCubitFaces()
185
    {
186
    return FACE_COLORS.length;
187
    }
188

    
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

    
191
  float getScreenRatio()
192
    {
193
    return 0.5f;
194
    }
195

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

    
198
  int getFaceColor(int cubit, int cubitface, int size)
199
    {
200
    boolean belongs = isOnFace(cubit, cubitface/2, cubitface%2==0 ? size-1:0 );
201
    return belongs ? cubitface : NUM_FACES;
202
    }
203

    
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205

    
206
  MeshBase createCubitMesh(int cubit)
207
    {
208
    int size   = getSize();
209
    int ordinal= RubikObjectList.CUBE.ordinal();
210
    int index  = RubikObjectList.getSizeIndex(ordinal,size);
211
    float[] bands;
212
    float D = 0.027f;
213
    float E = 0.5f-D;
214
    float[] vertices = { -E,-E, +E,-E, +E,+E, -E,+E };
215
    int extraI, extraV;
216

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

    
253
    return createCubitMesh(index,vertices,bands,extraI,extraV);
254
    }
255

    
256
///////////////////////////////////////////////////////////////////////////////////////////////////
257

    
258
  MeshBase createCubitMesh(int index, float[] vertices, float[] bands, int extraI, int extraV)
259
    {
260
    if( mMeshes==null )
261
      {
262
      mMeshes = new MeshBase[RubikObjectList.CUBE.getNumVariants()];
263
      }
264

    
265
    if( mMeshes[index]==null )
266
      {
267
      final int MESHES=6;
268
      int association = 1;
269
      MeshBase[] meshes = new MeshPolygon[MESHES];
270
      meshes[0] = new MeshPolygon(vertices,bands,extraI,extraV);
271
      meshes[0].setEffectAssociation(0,association,0);
272

    
273
      for(int i=1; i<MESHES; i++)
274
        {
275
        association <<=1;
276
        meshes[i] = meshes[0].copy(true);
277
        meshes[i].setEffectAssociation(0,association,0);
278
        }
279

    
280
      mMeshes[index] = new MeshJoined(meshes);
281

    
282
      Static3D axisY   = new Static3D(0,1,0);
283
      Static3D axisX   = new Static3D(1,0,0);
284
      Static3D center  = new Static3D(0,0,0);
285
      Static1D angle90 = new Static1D(90);
286
      Static1D angle180= new Static1D(180);
287
      Static1D angle270= new Static1D(270);
288

    
289
      float d1 = 1.0f;
290
      float d2 =-0.05f;
291
      float d3 = 0.12f;
292

    
293
      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
294
      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
295
      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
296
      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
297
      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
298
      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
299
      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
300
      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
301

    
302
      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
303
      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
304
      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
305
      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
306
      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
307
      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
308
      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
309
      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
310

    
311
      Static4D dReg  = new Static4D(0,0,0,d3);
312
      Static1D dRad  = new Static1D(1);
313

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

    
327
      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
328
      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
329
      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
330
      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
331
      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
332
      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
333
      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
334
      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
335

    
336
      mMeshes[index].apply(effect0);
337
      mMeshes[index].apply(effect1);
338
      mMeshes[index].apply(effect2);
339
      mMeshes[index].apply(effect3);
340
      mMeshes[index].apply(effect4);
341
      mMeshes[index].apply(effect5);
342
      mMeshes[index].apply(effect6);
343
      mMeshes[index].apply(effect7);
344
      mMeshes[index].apply(effect8);
345
      mMeshes[index].apply(effect9);
346
      mMeshes[index].apply(effect10);
347
      mMeshes[index].apply(effect11);
348
      mMeshes[index].apply(effect12);
349
      mMeshes[index].apply(effect13);
350

    
351
      mMeshes[index].mergeEffComponents();
352
      }
353

    
354
    return mMeshes[index].copy(true);
355
    }
356

    
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358

    
359
  float returnMultiplier()
360
    {
361
    return getSize();
362
    }
363

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

    
366
  float[] getRowChances()
367
    {
368
    int size = getSize();
369
    float[] chances = new float[size];
370

    
371
    for(int i=0; i<size; i++)
372
      {
373
      chances[i] = (i+1.0f) / size;
374
      }
375

    
376
    return chances;
377
    }
378

    
379
///////////////////////////////////////////////////////////////////////////////////////////////////
380
// PUBLIC API
381

    
382
  public Static3D[] getRotationAxis()
383
    {
384
    return ROT_AXIS;
385
    }
386

    
387
///////////////////////////////////////////////////////////////////////////////////////////////////
388

    
389
  public int getBasicAngle()
390
    {
391
    return 4;
392
    }
393

    
394
///////////////////////////////////////////////////////////////////////////////////////////////////
395

    
396
  public int computeRowFromOffset(float offset)
397
    {
398
    return (int)(getSize()*offset);
399
    }
400

    
401
///////////////////////////////////////////////////////////////////////////////////////////////////
402

    
403
  public float returnRotationFactor(float offset)
404
    {
405
    return 1.0f;
406
    }
407

    
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409

    
410
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
411
    {
412
    int numAxis = ROTATION_AXIS.length;
413

    
414
    if( oldRotAxis == START_AXIS )
415
      {
416
      return rnd.nextInt(numAxis);
417
      }
418
    else
419
      {
420
      int newVector = rnd.nextInt(numAxis-1);
421
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
422
      }
423
    }
424

    
425
///////////////////////////////////////////////////////////////////////////////////////////////////
426

    
427
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
428
    {
429
    float rowFloat = rnd.nextFloat();
430

    
431
    for(int row=0; row<mRowChances.length; row++)
432
      {
433
      if( rowFloat<=mRowChances[row] ) return row;
434
      }
435

    
436
    return 0;
437
    }
438

    
439
///////////////////////////////////////////////////////////////////////////////////////////////////
440

    
441
  public boolean isSolved()
442
    {
443
    int index = CUBITS[0].mQuatIndex;
444

    
445
    for(int i=1; i<NUM_CUBITS; i++)
446
      {
447
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
448
      }
449

    
450
    return true;
451
    }
452

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

    
463
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
464
    {
465
    if ( cubit.mQuatIndex == quatIndex ) return true;
466

    
467
    int belongsToHowManyFaces = 0;
468
    int size = getSize()-1;
469
    float row;
470
    final float MAX_ERROR = 0.01f;
471

    
472
    for(int i=0; i<NUM_AXIS; i++)
473
      {
474
      row = cubit.mRotationRow[i];
475
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
476
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
477
      }
478

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

    
487
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
488
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
489
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
490

    
491
               float row1, row2, row3, row4;
492
               float ax,ay,az;
493
               Static3D axis;
494
               float x1 = rotated1.get0();
495
               float y1 = rotated1.get1();
496
               float z1 = rotated1.get2();
497
               float x2 = rotated2.get0();
498
               float y2 = rotated2.get1();
499
               float z2 = rotated2.get2();
500

    
501
               for(int i=0; i<NUM_AXIS; i++)
502
                 {
503
                 axis = ROTATION_AXIS[i];
504
                 ax = axis.get0();
505
                 ay = axis.get1();
506
                 az = axis.get2();
507

    
508
                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
509
                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
510
                 row3 = row1 - size;
511
                 row4 = row2 - size;
512

    
513
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
514
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
515
                   {
516
                   return true;
517
                   }
518
                 }
519
               return false;
520

    
521
      default: return false;  // edge or corner
522
      }
523
    }
524

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

    
548
  public String retObjectString()
549
    {
550
    StringBuilder objectString = new StringBuilder();
551
    int size = getSize();
552
    int len = size*size;
553
    int cubitIndex=-1, row=-1, col=-1;
554
    int color=-1, face=-1;
555

    
556
    final int RIGHT= 0;
557
    final int LEFT = 1;
558
    final int UP   = 2;
559
    final int DOWN = 3;
560
    final int FRONT= 4;
561
    final int BACK = 5;
562

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

    
566
    face = UP;
567

    
568
    for(int i=0; i<len; i++)
569
      {
570
      row = i/size;
571
      col = i%size;
572

    
573
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
574
      color = getCubitFaceColorIndex(cubitIndex,face);
575
      objectString.append(FACE_NAMES[color]);
576
      }
577

    
578
    face = RIGHT;
579

    
580
    for(int i=0; i<len; i++)
581
      {
582
      cubitIndex = 6*size*size - 12*size +7 - i;
583
      color = getCubitFaceColorIndex(cubitIndex,face);
584
      objectString.append(FACE_NAMES[color]);
585
      }
586

    
587
     face = FRONT;
588

    
589
    for(int i=0; i<len; i++)
590
      {
591
      row = i/size;
592
      col = i%size;
593

    
594
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
595
      else if( col==0 ) cubitIndex = size*size - 1 - i;
596
      else
597
        {
598
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
599
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
600
        }
601

    
602
      color = getCubitFaceColorIndex(cubitIndex,face);
603
      objectString.append(FACE_NAMES[color]);
604
      }
605

    
606
    face = DOWN;
607

    
608
    for(int i=0; i<len; i++)
609
      {
610
      row = i/size;
611
      col = i%size;
612

    
613
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
614
      color = getCubitFaceColorIndex(cubitIndex,face);
615
      objectString.append(FACE_NAMES[color]);
616
      }
617

    
618
    face = LEFT;
619

    
620
    for(int i=0; i<len; i++)
621
      {
622
      row = i/size;
623
      col = i%size;
624

    
625
      cubitIndex = (size-1-row)*size + col;
626
      color = getCubitFaceColorIndex(cubitIndex,face);
627
      objectString.append(FACE_NAMES[color]);
628
      }
629

    
630
    face = BACK;
631

    
632
    for(int i=0; i<len; i++)
633
      {
634
      row = i/size;
635
      col = i%size;
636

    
637
      if( col==size-1 ) cubitIndex = size*(size-1-row);
638
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
639
      else
640
        {
641
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
642
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
643
        }
644

    
645
      color = getCubitFaceColorIndex(cubitIndex,face);
646
      objectString.append(FACE_NAMES[color]);
647
      }
648

    
649
    return objectString.toString();
650
    }
651
}
(2-2/14)