Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyMegaminx.java @ d38f1397

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 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.MatrixEffectQuaternion;
27
import org.distorted.library.main.DistortedEffects;
28
import org.distorted.library.main.DistortedTexture;
29
import org.distorted.library.mesh.MeshBase;
30
import org.distorted.library.mesh.MeshSquare;
31
import org.distorted.library.type.Static3D;
32
import org.distorted.library.type.Static4D;
33
import org.distorted.main.R;
34
import org.distorted.main.RubikSurfaceView;
35

    
36
///////////////////////////////////////////////////////////////////////////////////////////////////
37

    
38
public class TwistyMegaminx extends TwistyMinx
39
{
40
  static final float MEGA_D = 0.05f;
41

    
42
  private static final int NUM_CORNERS = 20;
43
  private static final int NUM_CENTERS = 12;
44
  private static final int NUM_EDGES   = 30;
45

    
46
  // the five vertices that form a given face. Order: the same as colors
47
  // of the faces in TwistyMinx.
48
  private static final int[][] mCenterMap =
49
         {
50
           { 0, 12,  4, 14,  2},
51
           { 0,  2, 18,  6, 16},
52
           { 6, 18, 11, 19,  7},
53
           { 3, 15,  9, 11, 19},
54
           { 4,  5, 15,  9, 14},
55
           { 1, 13,  5, 15,  3},
56
           { 1,  3, 19,  7, 17},
57
           {10, 16,  6,  7, 17},
58
           { 0, 12,  8, 10, 16},
59
           { 8, 13,  5,  4, 12},
60
           { 1, 13,  8, 10, 17},
61
           { 2, 14,  9, 11, 18},
62
         };
63

    
64
  // the quadruple ( vertex1, vertex2, face1, face2 ) defining an edge.
65
  // In fact the 2 vertices already define it, the faces only provide easy
66
  // way to get to know the colors. Order: arbitrary. Face1 arbitrarily on
67
  // the 'left' or right of vector vertex1 --> vertex2, according to Quat.
68
  private static final int[][] mEdgeMap =
69
         {
70
           {  0, 12,  0,  8},
71
           { 12,  4,  0,  9},
72
           {  4, 14,  0,  4},
73
           { 14,  2,  0, 11},
74
           {  2,  0,  0,  1},
75
           { 14,  9,  4, 11},
76
           {  9, 11,  3, 11},
77
           { 11, 18,  2, 11},
78
           { 18,  2,  1, 11},
79
           { 18,  6,  2,  1},
80
           {  6, 16,  7,  1},
81
           { 16,  0,  8,  1},
82
           { 16, 10,  8,  7},
83
           { 10,  8,  8, 10},
84
           {  8, 12,  8,  9},
85
           {  8, 13,  9, 10},
86
           { 13,  5,  9,  5},
87
           {  5,  4,  9,  4},
88
           {  5, 15,  4,  5},
89
           { 15,  9,  4,  3},
90
           { 11, 19,  2,  3},
91
           { 19,  7,  2,  6},
92
           {  7,  6,  2,  7},
93
           {  7, 17,  7,  6},
94
           { 17, 10,  7, 10},
95
           { 17,  1,  6, 10},
96
           {  1,  3,  6,  4},
97
           {  3, 19,  6,  3},
98
           {  1, 13, 10,  5},
99
           {  3, 15,  5,  3},
100
         };
101

    
102
  private static final float[][] mCenterCoords = new float[NUM_CENTERS][3];
103

    
104
  static
105
    {
106
    for(int center=0; center<NUM_CENTERS; center++)
107
      {
108
      int[] map = mCenterMap[center];
109

    
110
      float x = CORNERS[map[0]].get0() +
111
                CORNERS[map[1]].get0() +
112
                CORNERS[map[2]].get0() +
113
                CORNERS[map[3]].get0() +
114
                CORNERS[map[4]].get0() ;
115

    
116
      float y = CORNERS[map[0]].get1() +
117
                CORNERS[map[1]].get1() +
118
                CORNERS[map[2]].get1() +
119
                CORNERS[map[3]].get1() +
120
                CORNERS[map[4]].get1() ;
121

    
122
      float z = CORNERS[map[0]].get2() +
123
                CORNERS[map[1]].get2() +
124
                CORNERS[map[2]].get2() +
125
                CORNERS[map[3]].get2() +
126
                CORNERS[map[4]].get2() ;
127

    
128
      mCenterCoords[center][0] = x/5;
129
      mCenterCoords[center][1] = y/5;
130
      mCenterCoords[center][2] = z/5;
131
      }
132
    }
133

    
134
  private static MeshBase[] mCenterMeshes, mCornerMeshes;
135
  private static MeshBase[][] mEdgeMeshes;
136

    
137
  private static final Static4D[] mBasicCornerV, mCurrCornerV;
138

    
139
  static
140
    {
141
    mBasicCornerV = new Static4D[3];
142
    mCurrCornerV  = new Static4D[3];
143

    
144
    mBasicCornerV[0] = new Static4D( (SQ5+1)*0.125f, (SQ5-1)*0.125f, -0.250f, 0.0f );
145
    mBasicCornerV[1] = new Static4D(-(SQ5+1)*0.125f, (SQ5-1)*0.125f, -0.250f, 0.0f );
146
    mBasicCornerV[2] = new Static4D(              0,        -0.500f,    0.0f, 0.0f );
147
    };
148

    
149
///////////////////////////////////////////////////////////////////////////////////////////////////
150

    
151
  TwistyMegaminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
152
                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
153
    {
154
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.MEGA, res, scrWidth);
155
    }
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158

    
159
  private int numCubitsPerCorner(int numLayers)
160
    {
161
    return 3*((numLayers-1)/2)*((numLayers-3)/2) + 1;
162
    }
163

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165

    
166
  private int numCubitsPerEdge(int numLayers)
167
    {
168
    return numLayers-2;
169
    }
170

    
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172

    
173
  int getNumStickerTypes(int numLayers)
174
    {
175
    return numLayers-1; //numLayers;
176
    }
177

    
178
///////////////////////////////////////////////////////////////////////////////////////////////////
179

    
180
  float[] getCuts(int numLayers)
181
    {
182
    float[] cuts = new float[numLayers-1];
183
    float D = (numLayers/3.0f)*MovementMinx.DIST3D;
184
    float E = 2*C1;           // 2*cos(36 deg)
185
    float X = 2*D*E/(1+2*E);  // height of the 'upper' part of a dodecahedron, i.e. put it on a table,
186
                              // its height is then 2*DIST3D, it has one 'lower' part of height X, one
187
                              // 'middle' part of height Y and one upper part of height X again.
188
                              // It's edge length = numLayers/3.0f.
189
    int num = (numLayers-1)/2;
190
    float G = X*(0.5f-MEGA_D)/num; // height of one Layer
191

    
192
    for(int i=0; i<num; i++)
193
      {
194
      cuts[        i] = -MovementMinx.DIST3D + (i+0.5f)*G;
195
      cuts[2*num-1-i] = -cuts[i];
196
      }
197

    
198
    return cuts;
199
    }
200

    
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202

    
203
  private void computeCenter(Static3D[] array, int center)
204
    {
205
    float[] coords = mCenterCoords[center];
206
    array[center].set( coords[0], coords[1], coords[2]);
207
    }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210
// Fill out mCurrCorner{X,Y,Z} by applying appropriate Quat to mBasicCorner{X,Y,Z}
211
// Appropriate one: QUATS[QUAT_INDICES[corner]].
212

    
213
  private void computeBasicCornerVectors(int corner)
214
    {
215
    Static4D quat = QUATS[QUAT_CORNER_INDICES[corner]];
216

    
217
    mCurrCornerV[0] = RubikSurfaceView.rotateVectorByQuat(mBasicCornerV[0],quat);
218
    mCurrCornerV[1] = RubikSurfaceView.rotateVectorByQuat(mBasicCornerV[1],quat);
219
    mCurrCornerV[2] = RubikSurfaceView.rotateVectorByQuat(mBasicCornerV[2],quat);
220
    }
221

    
222
///////////////////////////////////////////////////////////////////////////////////////////////////
223

    
224
  private void computeCorner(Static3D pos, int numCubitsPerCorner, int numLayers, int corner, int part)
225
    {
226
    float D = numLayers/3.0f;
227
    Static3D corn = CORNERS[corner];
228

    
229
    if( part==0 )
230
      {
231
      pos.set( corn.get0()*D, corn.get1()*D, corn.get2()*D );
232
      }
233
    else
234
      {
235
      float E = 2.0f*(numLayers/6.0f - MEGA_D)/(0.5f*(numLayers-1));
236
      int N = (numCubitsPerCorner-1)/3;
237
      int block = (part-1) % N;
238
      int index = (part-1) / N;
239
      Static4D pri = mCurrCornerV[index];
240
      Static4D sec = mCurrCornerV[(index+2)%3];
241

    
242
      int multP = (block % ((numLayers-3)/2)) + 1;
243
      int multS = (block / ((numLayers-3)/2));
244

    
245
      pos.set( corn.get0()*D + (pri.get0()*multP + sec.get0()*multS)*E,
246
               corn.get1()*D + (pri.get1()*multP + sec.get1()*multS)*E,
247
               corn.get2()*D + (pri.get2()*multP + sec.get2()*multS)*E );
248
      }
249
    }
250

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

    
253
  private int computeEdgeType(int cubit, int numCubitsPerCorner, int numCubitsPerEdge)
254
    {
255
    int part = (cubit - NUM_CORNERS*numCubitsPerCorner) % numCubitsPerEdge;
256
    return (part+1)/2;
257
    }
258

    
259
///////////////////////////////////////////////////////////////////////////////////////////////////
260

    
261
  private void computeEdge(Static3D pos, int numLayers, int edge, int part)
262
    {
263
    Static3D c1 = CORNERS[ mEdgeMap[edge][0] ];
264
    Static3D c2 = CORNERS[ mEdgeMap[edge][1] ];
265
    float x = (c1.get0() + c2.get0())/2;
266
    float y = (c1.get1() + c2.get1())/2;
267
    float z = (c1.get2() + c2.get2())/2;
268

    
269
    if( part==0 )
270
      {
271
      pos.set(x,y,z);
272
      }
273
    else
274
      {
275
      int mult = (part+1)/2;
276
      int dir  = (part+1)%2;
277
      float[] center = mCenterCoords[ mEdgeMap[edge][dir+2] ];
278

    
279
      float vX = center[0]-x;
280
      float vY = center[1]-y;
281
      float vZ = center[2]-z;
282

    
283
      float A = mult*(0.5f-MEGA_D)/((1+SQ5/5)*(numLayers-1));
284

    
285
      pos.set( x+A*vX, y+A*vY, z+A*vZ );
286
      }
287
    }
288

    
289
///////////////////////////////////////////////////////////////////////////////////////////////////
290

    
291
  Static3D[] getCubitPositions(int numLayers)
292
    {
293
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
294
    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
295
    int numCubits = NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge;// + NUM_CENTERS;
296
    int index=0;
297

    
298
    final Static3D[] CENTERS = new Static3D[numCubits];
299

    
300
    for(int corner=0; corner<NUM_CORNERS; corner++)
301
      {
302
      computeBasicCornerVectors(corner);
303

    
304
      for(int part=0; part<numCubitsPerCorner; part++, index++)
305
        {
306
        CENTERS[index] = new Static3D(0,0,0);
307
        computeCorner(CENTERS[index],numCubitsPerCorner,numLayers,corner,part);
308
        }
309
      }
310

    
311
    for(int edge=0; edge<NUM_EDGES; edge++)
312
      {
313
      for(int part=0; part<numCubitsPerEdge; part++, index++)
314
        {
315
        CENTERS[index] = new Static3D(0,0,0);
316
        computeEdge(CENTERS[index], numLayers, edge, part );
317
        }
318
      }
319
/*
320
    for(int center=0; center<NUM_CENTERS; center++, index++)
321
      {
322
      computeCenter(CENTERS,index);
323
      }
324
*/
325
    return CENTERS;
326
    }
327

    
328
///////////////////////////////////////////////////////////////////////////////////////////////////
329

    
330
  private int getQuat(int cubit, int numLayers)
331
    {
332
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
333

    
334
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
335
      {
336
      int corner = cubit/numCubitsPerCorner;
337
      return QUAT_CORNER_INDICES[corner];
338
      }
339

    
340
    int numCubitsPerEdge = numCubitsPerEdge(numLayers);
341

    
342
    if( cubit < NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
343
      {
344
      int edge = (cubit-NUM_CORNERS*numCubitsPerCorner)/numCubitsPerEdge;
345
      return QUAT_EDGE_INDICES[edge];
346
      }
347

    
348
/*
349
    else
350
      {
351
      // TODO: centers
352
      }
353
*/
354

    
355
    return 0;
356
    }
357

    
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359

    
360
  MeshBase createCubitMesh(int cubit, int numLayers)
361
    {
362
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
363
    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
364
    int index = (numLayers-3)/2;
365
    int variants = ObjectList.MEGA.getNumVariants();
366
    MeshBase mesh;
367

    
368
    if( mCornerMeshes==null ) mCornerMeshes = new MeshBase[variants];
369
    if( mEdgeMeshes  ==null ) mEdgeMeshes   = new MeshBase[variants][index+1];
370

    
371
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
372
      {
373
      if( mCornerMeshes[index]==null )
374
        {
375
        mCornerMeshes[index] = FactoryCubit.getInstance().createMegaminxCornerMesh(numLayers);
376
        }
377
      mesh = mCornerMeshes[index].copy(true);
378
      }
379
    else //if( cubit<NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
380
      {
381
      int type = computeEdgeType(cubit,numCubitsPerCorner,numCubitsPerEdge);
382

    
383
      if( mEdgeMeshes[index][type]==null )
384
        {
385
        mEdgeMeshes[index][type] = FactoryCubit.getInstance().createMegaminxEdgeMesh(index,type);
386
        }
387

    
388
      mesh = mEdgeMeshes[index][type].copy(true);
389
      }
390
/*
391
    else
392
      {
393
      // TODO: centers
394
      }
395
*/
396
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( QUATS[getQuat(cubit,numLayers)], new Static3D(0,0,0) );
397
    mesh.apply(quat,0xffffffff,0);
398

    
399
    return mesh;
400
    }
401

    
402
///////////////////////////////////////////////////////////////////////////////////////////////////
403

    
404
  int getCornerColor(int cubit, int cubitface, int numLayers, int numCubitsPerCorner)
405
    {
406
    if( cubitface<0 || cubitface>2 ) return NUM_TEXTURES*NUM_FACES;
407

    
408
    int part  = cubit % numCubitsPerCorner;
409
    int corner= cubit / numCubitsPerCorner;
410

    
411
    if( part==0 )
412
      {
413
      return mCornerFaceMap[corner][cubitface];
414
      }
415
    else
416
      {
417
      int N = (numCubitsPerCorner-1)/3;
418
      int block = (part-1) % N;
419
      int index = (part-1) / N;
420

    
421
      if( block< (numLayers-1)/2 )
422
        {
423
        switch(index)
424
          {
425
          case 0: return cubitface==1 ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
426
          case 1: return cubitface==0 ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
427
          case 2: return cubitface==2 ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
428
          }
429
        }
430
      else
431
        {
432
        switch(index)
433
          {
434
          case 0: return cubitface==0 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES*NUM_FACES;
435
          case 1: return cubitface==2 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES*NUM_FACES;
436
          case 2: return cubitface==1 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES*NUM_FACES;
437
          }
438
        }
439
      }
440

    
441
    return NUM_TEXTURES*NUM_FACES;
442
    }
443

    
444
///////////////////////////////////////////////////////////////////////////////////////////////////
445

    
446
  int getEdgeColor(int cubit, int cubitface, int numLayers, int numCubitsPerCorner, int numCubitsPerEdge)
447
    {
448
    if( cubitface<0 || cubitface>1 ) return NUM_TEXTURES*NUM_FACES;
449

    
450
    int part = (cubit - NUM_CORNERS*numCubitsPerCorner) % numCubitsPerEdge;
451
    int edge = (cubit - NUM_CORNERS*numCubitsPerCorner) / numCubitsPerEdge;
452

    
453
    if( part==0 )
454
      {
455
      return mEdgeMap[edge][cubitface+2];
456
      }
457

    
458
    return cubitface==((part+1)%2) ? mEdgeMap[edge][cubitface+2] : NUM_TEXTURES*NUM_FACES;
459
    }
460

    
461
///////////////////////////////////////////////////////////////////////////////////////////////////
462
// TODO
463

    
464
  int getCenterColor(int cubit, int cubitface, int numLayers)
465
    {
466
    return 0;
467
    }
468

    
469
///////////////////////////////////////////////////////////////////////////////////////////////////
470

    
471
  int getFaceColor(int cubit, int cubitface, int numLayers)
472
    {
473
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
474
    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
475

    
476
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
477
      {
478
      return getCornerColor(cubit,cubitface,numLayers,numCubitsPerCorner);
479
      }
480
    else if( cubit<NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
481
      {
482
      return getEdgeColor(cubit,cubitface,numLayers,numCubitsPerCorner,numCubitsPerEdge);
483
      }
484
    else
485
      {
486
      return getCenterColor(cubit,cubitface,numLayers);
487
      }
488
    }
489

    
490
///////////////////////////////////////////////////////////////////////////////////////////////////
491
// TODO
492

    
493
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
494
    {
495
    paint.setColor(FACE_COLORS[face%NUM_FACES]);
496
    paint.setStyle(Paint.Style.FILL);
497
    canvas.drawRect(left,top,left+TEXTURE_HEIGHT,top+TEXTURE_HEIGHT,paint);
498
    }
499

    
500
///////////////////////////////////////////////////////////////////////////////////////////////////
501
// PUBLIC API
502

    
503
  public boolean isSolved()
504
    {
505
    int index = CUBITS[0].mQuatIndex;
506

    
507
    for(int i=1; i<NUM_CUBITS; i++)
508
      {
509
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
510
      }
511

    
512
    return true;
513
    }
514

    
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
517
// then if it were rotated by quaternion 'quat'.
518
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
519
// middle squares get interchanged. No visible difference!
520
//
521
// So: this is true iff the cubit
522
// a) is a corner or edge and the quaternions are the same
523
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
524

    
525
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
526
    {
527
    if ( cubit.mQuatIndex == quatIndex ) return true;
528

    
529
    int belongsToHowManyFaces = 0;
530
    int lastLayer = getNumLayers()-1;
531
    float row;
532
    final float MAX_ERROR = 0.01f;
533

    
534
    for(int i=0; i<NUM_AXIS; i++)
535
      {
536
      row = cubit.mRotationRow[i];
537
      if( (row          <MAX_ERROR && row          >-MAX_ERROR) ||
538
          (row-lastLayer<MAX_ERROR && row-lastLayer>-MAX_ERROR)  ) belongsToHowManyFaces++;
539
      }
540

    
541
    switch(belongsToHowManyFaces)
542
      {
543
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
544
      case 1 :                // cubit that lies inside one of the faces
545
               Static3D orig = cubit.getOrigPosition();
546
               Static4D quat1 = QUATS[quatIndex];
547
               Static4D quat2 = QUATS[cubit.mQuatIndex];
548

    
549
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
550
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
551
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
552

    
553
               float row1, row2;
554
               float x1 = rotated1.get0();
555
               float y1 = rotated1.get1();
556
               float z1 = rotated1.get2();
557
               float x2 = rotated2.get0();
558
               float y2 = rotated2.get1();
559
               float z2 = rotated2.get2();
560

    
561
               for(int i=0; i<NUM_AXIS; i++)
562
                 {
563
                 row1 = computeRow(x1,y1,z1,i);
564
                 row2 = computeRow(x2,y2,z2,i);
565

    
566
                 if( (row1==0 && row2==0) || (row1==lastLayer && row2==lastLayer) ) return true;
567
                 }
568
               return false;
569

    
570
      default: return false;  // edge or corner
571
      }
572
    }
573

    
574
///////////////////////////////////////////////////////////////////////////////////////////////////
575

    
576
  public int getObjectName(int numLayers)
577
    {
578
    if( numLayers==3 ) return R.string.minx3;
579
    if( numLayers==5 ) return R.string.minx4;
580

    
581
    return 0;
582
    }
583

    
584
///////////////////////////////////////////////////////////////////////////////////////////////////
585

    
586
  public int getInventor(int numLayers)
587
    {
588
    if( numLayers==3 ) return R.string.minx3_inventor;
589
    if( numLayers==5 ) return R.string.minx4_inventor;
590

    
591
    return 0;
592
    }
593

    
594
///////////////////////////////////////////////////////////////////////////////////////////////////
595

    
596
  public int getComplexity(int numLayers)
597
    {
598
    if( numLayers==3 ) return 4;
599

    
600
    return 5;
601
    }
602
}
(24-24/30)