Project

General

Profile

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

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

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
  int getNumStickerTypes()
171
    {
172
    return 1;
173
    }
174

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

    
177
  int getNumCubitFaces()
178
    {
179
    return FACE_COLORS.length;
180
    }
181

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

    
184
  float getScreenRatio()
185
    {
186
    return 0.5f;
187
    }
188

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

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

    
197
///////////////////////////////////////////////////////////////////////////////////////////////////
198

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

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

    
246
    return createCubitMesh(index,vertices,bands,extraI,extraV);
247
    }
248

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

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

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

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

    
273
      mMeshes[index] = new MeshJoined(meshes);
274

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

    
282
      float d1 = 1.0f;
283
      float d2 =-0.05f;
284
      float d3 = 0.12f;
285

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

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

    
304
      Static4D dReg  = new Static4D(0,0,0,d3);
305
      Static1D dRad  = new Static1D(1);
306

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

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

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

    
344
      mMeshes[index].mergeEffComponents();
345
      }
346

    
347
    return mMeshes[index].copy(true);
348
    }
349

    
350
///////////////////////////////////////////////////////////////////////////////////////////////////
351

    
352
  float returnMultiplier()
353
    {
354
    return getSize();
355
    }
356

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

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

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

    
369
    return chances;
370
    }
371

    
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373
// PUBLIC API
374

    
375
  public Static3D[] getRotationAxis()
376
    {
377
    return ROT_AXIS;
378
    }
379

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381

    
382
  public int getBasicAngle()
383
    {
384
    return 4;
385
    }
386

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

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

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

    
396
  public float returnRotationFactor(float offset)
397
    {
398
    return 1.0f;
399
    }
400

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

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

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

    
418
///////////////////////////////////////////////////////////////////////////////////////////////////
419

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

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

    
429
    return 0;
430
    }
431

    
432
///////////////////////////////////////////////////////////////////////////////////////////////////
433

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

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

    
443
    return true;
444
    }
445

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

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

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

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

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

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

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

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

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

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

    
514
      default: return false;  // edge or corner
515
      }
516
    }
517

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

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

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

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

    
559
    face = UP;
560

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

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

    
571
    face = RIGHT;
572

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

    
580
     face = FRONT;
581

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

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

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

    
599
    face = DOWN;
600

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

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

    
611
    face = LEFT;
612

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

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

    
623
    face = BACK;
624

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

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

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

    
642
    return objectString.toString();
643
    }
644
}
(2-2/14)