Project

General

Profile

Download (20.5 KB) Statistics
| Branch: | Revision:

distorted-objectlib / src / main / java / org / distorted / objectlib / objects / TwistyRedi.java @ 6f9408f8

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.objectlib.objects;
11

    
12
import static org.distorted.objectlib.touchcontrol.TouchControl.TC_HEXAHEDRON;
13
import static org.distorted.objectlib.touchcontrol.TouchControl.TYPE_SPLIT_CORNER;
14

    
15
import org.distorted.library.type.Static3D;
16
import org.distorted.library.type.Static4D;
17

    
18
import org.distorted.objectlib.helpers.FactoryCubit;
19
import org.distorted.objectlib.helpers.ObjectFaceShape;
20
import org.distorted.objectlib.helpers.ObjectSignature;
21
import org.distorted.objectlib.helpers.ObjectVertexEffects;
22
import org.distorted.objectlib.main.InitAssets;
23
import org.distorted.objectlib.main.InitData;
24
import org.distorted.objectlib.main.ObjectSignatures;
25
import org.distorted.objectlib.scrambling.ScrambleEdgeGenerator;
26
import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
27
import org.distorted.objectlib.main.ObjectType;
28
import org.distorted.objectlib.helpers.ObjectShape;
29
import org.distorted.objectlib.shape.ShapeHexahedron;
30

    
31
///////////////////////////////////////////////////////////////////////////////////////////////////
32

    
33
public class TwistyRedi extends ShapeHexahedron
34
{
35
  static final Static3D[] ROT_AXIS = new Static3D[]
36
         {
37
           new Static3D( SQ3/3, SQ3/3, SQ3/3),
38
           new Static3D( SQ3/3, SQ3/3,-SQ3/3),
39
           new Static3D( SQ3/3,-SQ3/3, SQ3/3),
40
           new Static3D( SQ3/3,-SQ3/3,-SQ3/3)
41
         };
42

    
43
  private int[][] mEdges;
44
  private int[][] mBasicAngle;
45
  private float[][] mCuts;
46
  private float[][] mPosition;
47
  private int[] mQuatIndex;
48
  private boolean[][] mRotatable;
49

    
50
///////////////////////////////////////////////////////////////////////////////////////////////////
51

    
52
  public TwistyRedi(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InitData data, InitAssets asset)
53
    {
54
    super(meshState, iconMode, data.getNumLayers()[0], quat, move, scale, data, asset);
55
    }
56

    
57
///////////////////////////////////////////////////////////////////////////////////////////////////
58

    
59
  @Override
60
  public float[][] returnRotationFactor()
61
    {
62
    float C = 2.0f;
63
    int numL = getNumLayers()[0];
64
    float[] f = new float[numL];
65
    for(int i=0; i<numL; i++) f[i] = C;
66
    return new float[][] { f,f,f,f };
67
    }
68

    
69
///////////////////////////////////////////////////////////////////////////////////////////////////
70

    
71
  public int[][] getScrambleEdges()
72
    {
73
    if( mEdges==null )
74
      {
75
      int[] numLayers = getNumLayers();
76

    
77
      if( numLayers[0]==3 )
78
        {
79
        mEdges = new int[][]
80
          {
81
            { 0,8,1,8, 4,1,5,1, 6,7,7,7, 10,2,11,2, 12,6,13,6, 16,3,17,3, 18,5,19,5, 22,4,23,4 },  // 0
82
            {                            10,2,11,2,            16,3,17,3, 18,5,19,5            },  // 1
83
            {          4,1,5,1,                     12,6,13,6,                       22,4,23,4 },  // 2
84
            {          4,1,5,1, 6,7,7,7,                                             22,4,23,4 },  // 3
85
            { 0,8,1,8,                   10,2,11,2,            16,3,17,3                       },  // 4
86
            {          4,1,5,1, 6,7,7,7,            12,6,13,6                                  },  // 5
87
            { 0,8,1,8,                   10,2,11,2,                       18,5,19,5            },  // 6
88
            { 0,8,1,8,                                         16,3,17,3, 18,5,19,5            },  // 7
89
            {                   6,7,7,7,            12,6,13,6,                       22,4,23,4 },  // 8
90
          };
91
        }
92
      else
93
        {
94
        if( mRotatable==null ) mRotatable = createRotatable();
95
        mEdges = ScrambleEdgeGenerator.getScrambleEdgesSingle(mBasicAngle, mRotatable);
96
        }
97
      }
98

    
99
    return mEdges;
100
    }
101

    
102
///////////////////////////////////////////////////////////////////////////////////////////////////
103

    
104
  @Override
105
  public int[][] getScrambleAlgorithms()
106
    {
107
    int[] numLayers = getNumLayers();
108

    
109
    if( numLayers[0]==3 )
110
      {
111
      return super.getScrambleAlgorithms();
112
      }
113
    else
114
      {
115
      if(mRotatable==null) mRotatable = createRotatable();
116
      return ScrambleEdgeGenerator.getScramblingAlgorithms(mBasicAngle, mRotatable);
117
      }
118
    }
119

    
120
///////////////////////////////////////////////////////////////////////////////////////////////////
121

    
122
  public float[][] getCuts(int[] numLayers)
123
    {
124
    if( mCuts==null )
125
      {
126
      switch( numLayers[0] )
127
        {
128
        case 3: float C3 = SQ3/2 + 0.05f;
129
                float[] c3 = new float[] {-C3,C3};
130
                mCuts = new float[][] { c3,c3,c3,c3 };
131
                break;
132
        case 5: float A5 = 5*SQ3/3;
133
                float B5 = 3*SQ3/3;
134
                float[] c5 = new float[] {-A5,-B5, B5, A5};
135
                mCuts = new float[][] { c5,c5,c5,c5 };
136
                break;
137
        }
138

    
139
      }
140

    
141
    return mCuts;
142
    }
143

    
144
///////////////////////////////////////////////////////////////////////////////////////////////////
145

    
146
  private boolean[][] createRotatable()
147
    {
148
    int numL = getNumLayers()[0];
149

    
150
    switch( numL )
151
      {
152
      case 3: boolean[] t3 = new boolean[] {true,false,true};
153
              return new boolean[][] { t3,t3,t3,t3 };
154
      case 5: boolean[] t5 = new boolean[] {true,true,false,true,true};
155
              return new boolean[][] { t5,t5,t5,t5 };
156
      }
157
    return null;
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

    
162
  public boolean[][] getLayerRotatable(int[] numLayers)
163
    {
164
    if( mRotatable==null ) mRotatable = createRotatable();
165
    return mRotatable;
166
    }
167

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

    
170
  public int getTouchControlType()
171
    {
172
    return TC_HEXAHEDRON;
173
    }
174

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

    
177
  public int getTouchControlSplit()
178
    {
179
    return TYPE_SPLIT_CORNER;
180
    }
181

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

    
184
  public int[][][] getEnabled()
185
    {
186
    return new int[][][]
187
      {
188
          {{0,1},{3,1},{2,3},{0,2}},
189
          {{2,3},{3,1},{0,1},{0,2}},
190
          {{1,2},{0,1},{0,3},{2,3}},
191
          {{1,2},{2,3},{0,3},{0,1}},
192
          {{0,3},{0,2},{1,2},{1,3}},
193
          {{1,2},{0,2},{0,3},{1,3}},
194
      };
195
    }
196

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

    
199
  public float[] getDist3D(int[] numLayers)
200
    {
201
    return TouchControlHexahedron.D3D;
202
    }
203

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

    
206
  public Static3D[] getFaceAxis()
207
    {
208
    return TouchControlHexahedron.FACE_AXIS;
209
    }
210

    
211
///////////////////////////////////////////////////////////////////////////////////////////////////
212

    
213
  public float[][] getCubitPositions(int[] numLayers)
214
    {
215
    if( mPosition==null )
216
      {
217
      if( numLayers[0]==3 )
218
        {
219
        final float C = 1.0f;
220
        final float E = 1.5f;
221

    
222
        mPosition = new float[][]
223
          {
224
             { C, C, C },
225
             { C, C,-C },
226
             { C,-C, C },
227
             { C,-C,-C },
228
             {-C, C, C },
229
             {-C, C,-C },
230
             {-C,-C, C },
231
             {-C,-C,-C },
232

    
233
             { 0, E, E },
234
             { E, 0, E },
235
             { 0,-E, E },
236
             {-E, 0, E },
237
             { E, E, 0 },
238
             { E,-E, 0 },
239
             {-E,-E, 0 },
240
             {-E, E, 0 },
241
             { 0, E,-E },
242
             { E, 0,-E },
243
             { 0,-E,-E },
244
             {-E, 0,-E }
245
          };
246
        }
247
      else if( numLayers[0]==5 )
248
        {
249
        final float C = 15.0f/8;
250
        final float E = 2.5f;
251
        final float F = 5.0f/8;
252

    
253
        mPosition = new float[][]
254
          {
255
             { C, C, C },
256
             { C, C,-C },
257
             { C,-C, C },
258
             { C,-C,-C },
259
             {-C, C, C },
260
             {-C, C,-C },
261
             {-C,-C, C },
262
             {-C,-C,-C },
263

    
264
             {-F, E, E },
265
             { F, E, E },
266
             { E,-F, E },
267
             { E, F, E },
268
             {-F,-E, E },
269
             { F,-E, E },
270
             {-E,-F, E },
271
             {-E, F, E },
272
             { E, E,-F },
273
             { E, E, F },
274
             { E,-E,-F },
275
             { E,-E, F },
276
             {-E,-E,-F },
277
             {-E,-E, F },
278
             {-E, E,-F },
279
             {-E, E, F },
280
             {-F, E,-E },
281
             { F, E,-E },
282
             { E,-F,-E },
283
             { E, F,-E },
284
             {-F,-E,-E },
285
             { F,-E,-E },
286
             {-E,-F,-E },
287
             {-E, F,-E },
288

    
289
             { 0, F, E },
290
             { 0,-F, E },
291
             { F, 0, E },
292
             {-F, 0, E },
293
             { 0, F,-E },
294
             { 0,-F,-E },
295
             { F, 0,-E },
296
             {-F, 0,-E },
297
             { E, 0, F },
298
             { E, 0,-F },
299
             { E, F, 0 },
300
             { E,-F, 0 },
301
             {-E, 0, F },
302
             {-E, 0,-F },
303
             {-E, F, 0 },
304
             {-E,-F, 0 },
305

    
306
             { 0,-E, F },
307
             { 0,-E,-F },
308
             { F,-E, 0 },
309
             {-F,-E, 0 },
310
             { 0, E, F },
311
             { 0, E,-F },
312
             { F, E, 0 },
313
             {-F, E, 0 },
314
          };
315
        }
316
      }
317

    
318
    return mPosition;
319
    }
320

    
321
///////////////////////////////////////////////////////////////////////////////////////////////////
322

    
323
  public Static4D getCubitQuats(int cubit, int[] numLayers)
324
    {
325
    if( mQuatIndex==null ) mQuatIndex = new int[] { 0,2,10,8,1,4,6,7,11,5,9,3,   0,11,2,3,4,1 };
326

    
327
    switch(cubit)
328
      {
329
      case  0: return mObjectQuats[0];                   //  unit quat
330
      case  1: return new Static4D( SQ2/2,0,0,SQ2/2);    //  90 along X
331
      case  2: return new Static4D(-SQ2/2,0,0,SQ2/2);    // -90 along X
332
      case  3: return mObjectQuats[9];                   // 180 along X
333
      case  4: return new Static4D(0, SQ2/2,0,SQ2/2);    //  90 along Y
334
      case  5: return mObjectQuats[11];                  // 180 along Y
335
      case  6: return mObjectQuats[10];                  // 180 along Z
336
      case  7: return new Static4D(SQ2/2,0,-SQ2/2,0);    // 180 along (SQ2/2,0,-SQ2/2)
337
      default: int index = numLayers[0]==3 ? (cubit-8) : (cubit<32 ? (cubit-8)/2 : (cubit-32)/4 + 12);
338
               return mObjectQuats[mQuatIndex[index]];
339
      }
340
    }
341

    
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343

    
344
  private float[][] getVertices(int variant)
345
    {
346
    float A = getNumLayers()[0]==3 ? 0.5f : 5.0f/8;
347
    float B = 2*A;
348
    float C = 3*A;
349

    
350
    if( variant==0 )
351
      {
352
      return new float[][]
353
          {
354
             { 0, 0, 0 },
355
             {-A, A, A },
356
             {-A,-A, A },
357
             { A, A, A },
358
             { A,-A, A },
359
             { A, A,-A },
360
             { A,-A,-A },
361
             {-A, A,-A },
362
          };
363
      }
364
    else if( variant==1 )
365
      {
366
      return new float[][]
367
          {
368
             {-A, 0, 0},
369
             { A, 0, 0},
370
             {-A,-B, 0},
371
             { A,-B, 0},
372
             { 0,-C, 0},
373
             {-A, 0,-B},
374
             { A, 0,-B},
375
             { 0, 0,-C},
376
          };
377
      }
378
    else
379
      {
380
      return new float[][]
381
          {
382
             {-A, 0, 0},
383
             { 0,-A, 0},
384
             { A, 0, 0},
385
             { 0, A, 0},
386
             { 0, 0,-A}
387
          };
388
      }
389
    }
390

    
391
///////////////////////////////////////////////////////////////////////////////////////////////////
392

    
393
  public ObjectShape getObjectShape(int variant)
394
    {
395
    if( variant==0 )
396
      {
397
      int[][] indices =
398
          {
399
             { 2,4,3,1 },
400
             { 1,3,5,7 },
401
             { 4,6,5,3 },
402

    
403
             { 0,4,2 },
404
             { 0,7,5 },
405
             { 0,6,4 },
406
             { 0,1,7 },
407
             { 0,2,1 },
408
             { 0,5,6 }
409
          };
410

    
411
      return new ObjectShape(getVertices(variant), indices);
412
      }
413
    else if( variant==1 )
414
      {
415
      int[][] indices =
416
          {
417
             { 0,2,4,3,1 },
418
             { 0,1,6,7,5 },
419
             { 1,3,6 },
420
             { 5,2,0 },
421
             { 4,7,6,3 },
422
             { 2,5,7,4 }
423
          };
424

    
425
      return new ObjectShape(getVertices(variant), indices);
426
      }
427
    else
428
      {
429
      int[][] indices = { {0,1,2,3},{4,1,0},{4,2,1},{4,3,2},{4,0,3} };
430
      return new ObjectShape(getVertices(variant), indices);
431
      }
432
    }
433

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

    
436
  public ObjectFaceShape getObjectFaceShape(int variant)
437
    {
438
    int numL = getNumLayers()[0];
439

    
440
    if( variant==0 )
441
      {
442
      int N = numL==3 ? 5:4;
443
      int E = numL==3 ? 1:0;
444
      float h1 = isInIconMode() ? 0.001f : 0.06f;
445
      float h2 = isInIconMode() ? 0.001f : 0.01f;
446
      float[][] bands = { {h1,35,0.5f,0.7f,N,E,E}, {h2,35,0.2f,0.4f,N,E,E} };
447
      int[] indices   = { 0,0,0,1,1,1,1,1,1 };
448
      return new ObjectFaceShape(bands,indices,null);
449
      }
450
    else if( variant==1 )
451
      {
452
      int N = numL==3 ? 6:5;
453
      int E = numL==3 ? 1:0;
454
      float h1 = isInIconMode() ? 0.001f : 0.038f;
455
      float h2 = isInIconMode() ? 0.001f : 0.020f;
456
      float[][] bands = { {h1,35,0.250f,0.7f,N,E,E}, {h2,35,0.125f,0.2f,3,E,E}, {h2,35,0.125f,0.2f,3,E,E} };
457
      int[] indices   = { 0,0,1,1,2,2 };
458
      return new ObjectFaceShape(bands,indices,null);
459
      }
460
    else
461
      {
462
      int N1 = numL==3 ? 6:2;
463
      int N2 = numL==3 ? 3:2;
464
      int E  = numL==3 ? 1:0;
465
      float height = isInIconMode() ? 0.001f : 0.04f;
466
      float[][] bands = { {height,35,SQ2/8,0.9f,N1,E,E}, {0.001f,35,1,0.0f,N2,0,0} };
467
      int[] indices   = { 0,1,1,1,1 };
468
      return new ObjectFaceShape(bands,indices,null);
469
      }
470
    }
471

    
472
///////////////////////////////////////////////////////////////////////////////////////////////////
473

    
474
  public ObjectVertexEffects getVertexEffects(int variant)
475
    {
476
    if( variant==0 )
477
      {
478
      float[][] corners = { {0.06f,0.12f} };
479
      int[] indices     = { -1,0,-1,0,0,0,-1,-1 };
480
      float[][] centers = { { 0.0f, 0.0f, 0.0f} };
481
      return FactoryCubit.generateVertexEffect(getVertices(variant),corners,indices,centers,indices);
482
      }
483
    else if( variant==1 )
484
      {
485
      float[][] corners = { {0.06f,0.20f} };
486
      int[] indices     = { 0,0,-1,-1,-1,-1,-1,-1 };
487
      float[][] centers = { { 0.0f,-0.75f,-0.75f} };
488
      return FactoryCubit.generateVertexEffect(getVertices(variant),corners,indices,centers,indices);
489
      }
490
    else
491
      {
492
      return null;
493
      }
494
    }
495

    
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497

    
498
  public int getNumCubitVariants(int[] numLayers)
499
    {
500
    return numLayers[0]==3 ? 2 : 3;
501
    }
502

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

    
505
  public int getCubitVariant(int cubit, int[] numLayers)
506
    {
507
    switch( numLayers[0] )
508
      {
509
      case 3: return cubit<8 ? 0:1;
510
      case 5: return cubit<8 ? 0 : cubit<32 ? 1:2;
511
      }
512
    return 0;
513
    }
514

    
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516

    
517
  public float getStickerRadius()
518
    {
519
    return 0.09f;
520
    }
521

    
522
///////////////////////////////////////////////////////////////////////////////////////////////////
523

    
524
  public float getStickerStroke()
525
    {
526
    switch(getNumLayers()[0])
527
      {
528
      case 3: return isInIconMode() ? 0.20f : 0.09f;
529
      case 5: return isInIconMode() ? 0.30f : 0.10f;
530
      }
531

    
532
    return 0;
533
    }
534

    
535
///////////////////////////////////////////////////////////////////////////////////////////////////
536

    
537
  public float[][][] getStickerAngles()
538
    {
539
    return null;
540
    }
541

    
542
///////////////////////////////////////////////////////////////////////////////////////////////////
543
// PUBLIC API
544

    
545
  public Static3D[] getRotationAxis()
546
    {
547
    return ROT_AXIS;
548
    }
549

    
550
///////////////////////////////////////////////////////////////////////////////////////////////////
551

    
552
  public int[][] getBasicAngles()
553
    {
554
    if( mBasicAngle==null )
555
      {
556
      int num = getNumLayers()[0];
557
      int[] tmp = new int[num];
558
      for(int i=0; i<num; i++) tmp[i] = 3;
559
      mBasicAngle = new int[][] { tmp,tmp,tmp,tmp };
560
      }
561

    
562
    return mBasicAngle;
563
    }
564

    
565
///////////////////////////////////////////////////////////////////////////////////////////////////
566

    
567
  public String getShortName()
568
    {
569
    switch(getNumLayers()[0])
570
      {
571
      case 3: return ObjectType.REDI_3.name();
572
      case 5: return ObjectType.FADI_5.name();
573
      }
574

    
575
    return null;
576
    }
577

    
578
///////////////////////////////////////////////////////////////////////////////////////////////////
579

    
580
  public ObjectSignature getSignature()
581
    {
582
    switch(getNumLayers()[0])
583
      {
584
      case 3: return new ObjectSignature(ObjectSignatures.REDI_3);
585
      case 5: return new ObjectSignature(ObjectSignatures.FADI_5);
586
      }
587

    
588
    return null;
589
    }
590

    
591
///////////////////////////////////////////////////////////////////////////////////////////////////
592

    
593
  public String getObjectName()
594
    {
595
    switch(getNumLayers()[0])
596
      {
597
      case 3: return "Redi Cube";
598
      case 5: return "Mosaic Cube";
599
      }
600
    return null;
601
    }
602

    
603
///////////////////////////////////////////////////////////////////////////////////////////////////
604

    
605
  public String getInventor()
606
    {
607
    return "Oskar van Deventer";
608
    }
609

    
610
///////////////////////////////////////////////////////////////////////////////////////////////////
611

    
612
  public int getYearOfInvention()
613
    {
614
    return 2009;
615
    }
616

    
617
///////////////////////////////////////////////////////////////////////////////////////////////////
618

    
619
  public int getComplexity()
620
    {
621
    switch(getNumLayers()[0])
622
      {
623
      case 3: return 1;
624
      case 5: return 2;
625
      }
626
    return 1;
627
    }
628

    
629
///////////////////////////////////////////////////////////////////////////////////////////////////
630

    
631
  public String[][] getTutorials()
632
    {
633
    switch(getNumLayers()[0])
634
      {
635
      case 3: return new String[][] {
636
                                {"gb", "Qn7TJED6O-4", "How to Solve the MoYu Redi Cube", "Z3"},
637
                                {"es", "g0M38Aotgac", "Resolver Redi Cube", "Cuby"},
638
                                {"ru", "dlNRbE-hyzU", "Как собрать Реди Куб", "Алексей Ярыгин"},
639
                                {"fr", "zw7UZcqqsgA", "Comment résoudre le Redi Cube", "ValentinoCube"},
640
                                {"de", "YU8riouyC2w", "Redi Cube Solve", "CubaroCubing"},
641
                                {"pl", "vxo3lXMsWQI", "Jak ułożyć Redi Cube?", "DJ rubiks"},
642
                                {"br", "muQ8U_G4LmM", "Como resolver o Redi Cube", "Rafael Cinoto"},
643
                                {"kr", "a5CzDMbRzbY", "레디큐브를 배우기", "vincentcube"},
644
                                {"vn", "2JZxtmrKUn4", "Tutorial N.6 - Redi", "Duy Thích Rubik"},
645
                                {"tw", "LBKPL01IHD8", "Redi Cube盲解教學", "1hrBLD"},
646
                               };
647
      case 5: return new String[][]{
648
                                {"gb", "Lp63Pn6q4vM", "Mosaic Cube", "Cubes made Easy"},
649
                                {"gb", "LGrxmaTiCZM", "How to solve the Mosaic Cube", "Cuber Stu"},
650
                                {"es", "o5_7dezogqM", "Resolver Mosaic Cube 1/4", "TheMaoiSha"},
651
                                {"es", "wT4wwCOfvQc", "Resolver Mosaic Cube 2/4", "TheMaoiSha"},
652
                                {"es", "yLdSbIK0ULU", "Resolver Mosaic Cube 3/4", "TheMaoiSha"},
653
                                {"es", "l_wl0AgP48k", "Resolver Mosaic Cube 4/4", "TheMaoiSha"},
654
                                {"ru", "wbbP45jHsU4", "Как собрать Мозаичный Куб", "SOLPUZ-Димон"},
655
                                {"pl", "Iu5vd_4h3Uc", "Mosaic cube Tutorial PL", "MrUK"},
656
                                {"br", "a-WiaZdlfjQ", "Mosaic Cube Walkthrough Solve", "Cubo vício"},
657
                                {"tw", "m0vG-cRB8fo", "Mosaic Cube Solution 1/2", "Arwin Hsu"},
658
                                {"tw", "qUDC4TF_1cw", "Mosaic Cube Solution 2/2", "Arwin Hsu"},
659
                               };
660
      }
661
    return null;
662
    }
663
}
(38-38/50)