Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / scrambling / ObjectScrambler.java @ 6777e712

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2021 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.scrambling;
11

    
12
import java.util.ArrayList;
13
import java.util.Random;
14

    
15
import org.distorted.objectlib.signature.ObjectSignature;
16
import org.distorted.objectlib.tablebases.TablebasesAbstract;
17

    
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
public class ObjectScrambler
21
  {
22
  public static final int SCRAMBLING_ALGORITHMS   = 0;
23
  public static final int SCRAMBLING_SQUARE1      = 1;
24
  public static final int SCRAMBLING_BANDAGED     = 2;
25
  public static final int SCRAMBLING_TABLEBASE    = 3;
26
  public static final int SCRAMBLING_SHAPESHIFTER = 4;
27

    
28
  private final ScrambleState[] mStates;
29
  private final int mType;
30
  private final int mNumAxis;
31
  private final int[] mNumLayers;
32
  private final int[][] mAlgorithms;
33

    
34
  // type=0, i.e. main
35
  private int mCurrState;
36
  private int mAxisExcluded;
37
  private int[][] mScrambleTable;
38
  private int[] mNumOccurences;
39
  private int[] mResult;
40
  private int mCurrAlgorithm;
41
  private int mCurrStep;
42

    
43
  // type=1, i.e. the exception: Square-1
44
  private static final int BASIC_ANGLE = 12;
45
  private static final int LAST_SL = 0; // automatic rotations: last rot was a 'slash' i.e. along ROT_AXIS[1]
46
  private static final int LAST_UP = 1; // last rot was along ROT_AXIS[0], upper layer and forelast was a slash
47
  private static final int LAST_LO = 2; // last rot was along ROT_AXIS[0], lower layer and forelast was a slash
48
  private static final int LAST_UL = 3; // two last rots were along ROT_AXIS[0] (so the next must be a slash)
49

    
50
  private int[][] mPermittedAngles;
51
  private int[] mCornerQuat;
52
  private int mPermittedUp, mPermittedDo;
53
  private int[][] mBadCornerQuats;
54
  private int mLastRot;
55
  private int[][] mQuatMult;
56

    
57
  // type=2 , i.e. locally created bandaged cuboids and pyramines
58
  private ArrayList<ScrambleStateLocallyBandaged> mStatesHistory;
59
  private BlacklistedSignatures mBlacklisted;
60

    
61
  // type==3 (tablebases)
62
  private final TablebasesAbstract mTablebase;
63

    
64
  // type==4 (shape-shifting cuboids)
65
  private boolean[] mPhase1Permitted, mPhase2Permitted;
66

    
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

    
69
  public ObjectScrambler(int type, int numAxis, int[] numLayers, int[][] algorithms, int[][] edges, TablebasesAbstract tablebase)
70
    {
71
    mType       = type;
72
    mNumAxis    = numAxis;
73
    mNumLayers  = numLayers;
74
    mAlgorithms = algorithms;
75

    
76
    if( mType==0 || mType==4 )
77
      {
78
      mResult = new int[2];
79
      }
80

    
81
    if( edges!=null )
82
      {
83
      int numEdges = edges.length;
84
      mStates = new ScrambleState[numEdges];
85
      for(int i=0; i<numEdges; i++) mStates[i] = new ScrambleState(edges[i],algorithms);
86
      }
87
    else mStates = null;
88

    
89
    if( mType==1 )
90
      {
91
      mPermittedAngles = new int[2][BASIC_ANGLE];
92
      mCornerQuat = new int[8];
93
      mLastRot = LAST_SL;
94
      }
95

    
96
    mTablebase = ((tablebase!=null && tablebase.useForScrambling()) ? tablebase : null);
97
    }
98

    
99
///////////////////////////////////////////////////////////////////////////////////////////////////
100

    
101
  private void initializeScrambling()
102
    {
103
    if( mScrambleTable==null )
104
      {
105
      mScrambleTable = new int[mNumAxis][];
106
      }
107
    if( mNumOccurences==null )
108
      {
109
      int max=0;
110

    
111
      for (ScrambleState mState : mStates)
112
        {
113
        int tmp = mState.getTotal();
114
        if (max < tmp) max = tmp;
115
        }
116

    
117
      mNumOccurences = new int[max];
118
      }
119

    
120
    for(int i=0; i<mNumAxis; i++)
121
      {
122
      int len = mNumLayers[i];
123
      mScrambleTable[i] = new int[len];
124
      for(int j=0; j<len; j++) mScrambleTable[i][j] = 0;
125
      }
126
    }
127

    
128
///////////////////////////////////////////////////////////////////////////////////////////////////
129
// PUBLIC API
130

    
131
  private void randomizeNewScramble0(int[][] scramble, Random rnd, int curr)
132
    {
133
    if( curr==0 )
134
      {
135
      mCurrStep     = 100000;
136
      mCurrState    = 0;
137
      mAxisExcluded =-1;
138
      initializeScrambling();
139
      }
140

    
141
    if( mCurrStep+6 <= mAlgorithms[mCurrAlgorithm].length )
142
      {
143
      mCurrStep += 3;
144
      }
145
    else
146
      {
147
      mStates[mCurrState].getRandom(rnd, mAlgorithms, mAxisExcluded, mScrambleTable, mNumOccurences, mResult);
148
      mCurrAlgorithm = mResult[0];
149
      mCurrState     = mResult[1];
150
      mCurrStep      = 0;
151
      }
152

    
153
    int[] algorithm = mAlgorithms[mCurrAlgorithm];
154
    scramble[curr][0] = algorithm[mCurrStep  ];
155
    scramble[curr][1] = algorithm[mCurrStep+1];
156
    scramble[curr][2] = algorithm[mCurrStep+2];
157

    
158
    mAxisExcluded = algorithm[0];
159
    }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162
// TYPE 1
163
///////////////////////////////////////////////////////////////////////////////////////////////////
164
// QUATS[i]*QUATS[j] = QUATS[QUAT_MULT[i][j]]
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

    
167
  void initializeQuatMult()
168
    {
169
    mQuatMult = new int[][]
170
      {
171
        {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,},
172
        {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12,},
173
        {  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13,},
174
        {  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14,},
175
        {  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15,},
176
        {  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16,},
177
        {  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17,},
178
        {  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18,},
179
        {  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19,},
180
        {  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20,},
181
        { 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,},
182
        { 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,},
183
        { 12, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,},
184
        { 13, 12, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,},
185
        { 14, 13, 12, 23, 22, 21, 20, 19, 18, 17, 16, 15,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,},
186
        { 15, 14, 13, 12, 23, 22, 21, 20, 19, 18, 17, 16,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,},
187
        { 16, 15, 14, 13, 12, 23, 22, 21, 20, 19, 18, 17,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,},
188
        { 17, 16, 15, 14, 13, 12, 23, 22, 21, 20, 19, 18,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,},
189
        { 18, 17, 16, 15, 14, 13, 12, 23, 22, 21, 20, 19,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,},
190
        { 19, 18, 17, 16, 15, 14, 13, 12, 23, 22, 21, 20,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,},
191
        { 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 22, 21,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,},
192
        { 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 22,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,},
193
        { 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11,},
194
        { 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,}
195
      };
196
    }
197

    
198
///////////////////////////////////////////////////////////////////////////////////////////////////
199
// TYPE 1
200

    
201
  private boolean cornerIsUp(int index)
202
    {
203
    return ((index<4) ^ (mCornerQuat[index]>=12));
204
    }
205

    
206
///////////////////////////////////////////////////////////////////////////////////////////////////
207
// TYPE 1
208

    
209
  private boolean cornerIsLeft(int index)
210
    {
211
    int q = mCornerQuat[index];
212

    
213
    switch(index)
214
      {
215
      case 0:
216
      case 4: return ((q>=3 && q<= 7) || (q>=18 && q<=22));
217
      case 1:
218
      case 5: return ((q>=6 && q<=10) || (q>=15 && q<=19));
219
      case 2:
220
      case 6: return ((q==0 || q==1 || (q>=9 && q<=11)) || (q>=12 && q<=16));
221
      case 3:
222
      case 7: return ((q>=0 && q<=4) || (q==12 || q==13 || (q>=21 && q<=23)));
223
      }
224

    
225
    return false;
226
    }
227

    
228
///////////////////////////////////////////////////////////////////////////////////////////////////
229
// TYPE 1
230

    
231
  private boolean quatIsBad(int quatIndex, int corner)
232
    {
233
    if( mBadCornerQuats ==null )
234
      {
235
      // quat indices that make corner cubits bandage the puzzle.
236
      mBadCornerQuats = new int[][] { { 2, 8,17,23}, { 5,11,14,20} };
237
      }
238

    
239
    int index = (corner%2);
240

    
241
    return ( quatIndex== mBadCornerQuats[index][0] ||
242
             quatIndex== mBadCornerQuats[index][1] ||
243
             quatIndex== mBadCornerQuats[index][2] ||
244
             quatIndex== mBadCornerQuats[index][3]  );
245
    }
246

    
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248
// TYPE 1
249

    
250
  private boolean isPermittedDo(int angle)
251
    {
252
    if( mQuatMult==null ) initializeQuatMult();
253

    
254
    for(int corner=0; corner<8; corner++)
255
      {
256
      if( !cornerIsUp(corner) )
257
        {
258
        int currQuat = mCornerQuat[corner];
259
        int finalQuat= mQuatMult[angle][currQuat];
260
        if( quatIsBad(finalQuat,corner) ) return false;
261
        }
262
      }
263

    
264
    return true;
265
    }
266

    
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268
// TYPE 1
269

    
270
  private boolean isPermittedUp(int angle)
271
    {
272
    if( mQuatMult==null ) initializeQuatMult();
273

    
274
    for(int corner=0; corner<8; corner++)
275
      {
276
      if( cornerIsUp(corner) )
277
        {
278
        int currQuat = mCornerQuat[corner];
279
        int finalQuat= mQuatMult[angle][currQuat];
280
        if( quatIsBad(finalQuat,corner) ) return false;
281
        }
282
      }
283

    
284
    return true;
285
    }
286

    
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288
// TYPE 1
289

    
290
  private void computePermittedAngles()
291
    {
292
    mPermittedDo = 0;
293

    
294
    for(int angle=0; angle<BASIC_ANGLE; angle++)
295
      {
296
      if( isPermittedDo(angle ) ) mPermittedAngles[0][mPermittedDo++] = angle;
297
      }
298

    
299
    mPermittedUp = 0;
300

    
301
    for(int angle=0; angle<BASIC_ANGLE; angle++)
302
      {
303
      if( isPermittedUp(angle ) ) mPermittedAngles[1][mPermittedUp++] = angle;
304
      }
305
    }
306

    
307
///////////////////////////////////////////////////////////////////////////////////////////////////
308
// TYPE 1
309

    
310
  private int getNextAngle(Random rnd, int layer)
311
    {
312
    int num = layer==0 ? mPermittedDo:mPermittedUp;
313
    int index = rnd.nextInt(num);
314
    int angle = mPermittedAngles[layer][index];
315
    return angle<BASIC_ANGLE/2 ? -angle : BASIC_ANGLE-angle;
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319
// TYPE 1
320

    
321
  private int getNextAngleNotZero(Random rnd, int layer)
322
    {
323
    int num = layer==0 ? mPermittedDo:mPermittedUp;
324
    int index = rnd.nextInt(num-1);
325
    int angle = mPermittedAngles[layer][index+1];
326
    return angle<BASIC_ANGLE/2 ? -angle : BASIC_ANGLE-angle;
327
    }
328

    
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330
// TYPE 1
331

    
332
  private int makeQuat(int axis,int index)
333
    {
334
    if( axis==1 ) return 13;
335
    if( index<0 ) index+=12;
336
    return index;
337
    }
338

    
339
///////////////////////////////////////////////////////////////////////////////////////////////////
340
// TYPE 1
341

    
342
  private boolean cornerBelongs(int index, int axis, int layer)
343
    {
344
    if( axis==0 )
345
      {
346
      boolean up = cornerIsUp(index);
347
      return ((up && layer==2) || (!up && layer==0));
348
      }
349
    else
350
      {
351
      boolean le = cornerIsLeft(index);
352
      return ((le && layer==0) || (!le && layer==1));
353
      }
354
    }
355

    
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357
// TYPE 1
358

    
359
  private void updateCornerQuats(int[] rotInfo)
360
    {
361
    if( mQuatMult==null ) initializeQuatMult();
362

    
363
    int axis = rotInfo[0];
364
    int layer= rotInfo[1];
365
    int index=-rotInfo[2];
366

    
367
    int quat = makeQuat(axis,index);
368

    
369
    for(int corner=0; corner<8; corner++)
370
      {
371
      if( cornerBelongs(corner,axis,layer) )
372
        {
373
        int curr = mCornerQuat[corner];
374
        mCornerQuat[corner] = mQuatMult[quat][curr];
375
        }
376
      }
377
    }
378

    
379
///////////////////////////////////////////////////////////////////////////////////////////////////
380
// TYPE 1
381

    
382
  private void randomizeNewScramble1(int[][] scramble, Random rnd, int curr)
383
    {
384
    int layer, nextAngle;
385

    
386
    if( curr==0 )
387
      {
388
      for(int corner=0; corner<8; corner++) mCornerQuat[corner] = 0;
389
      mLastRot = rnd.nextInt(4);
390
      computePermittedAngles();
391
      }
392

    
393
    switch(mLastRot)
394
      {
395
      case LAST_SL: layer = rnd.nextInt(2);
396
                    nextAngle = getNextAngle(rnd,layer);
397

    
398
                    if( nextAngle==0 )
399
                      {
400
                      layer = 1-layer;
401
                      nextAngle = getNextAngleNotZero(rnd,layer);
402
                      }
403

    
404
                    scramble[curr][0] = 0;
405
                    scramble[curr][1] = 2*layer;
406
                    scramble[curr][2] = nextAngle;
407
                    mLastRot = layer==0 ? LAST_LO : LAST_UP;
408
                    updateCornerQuats(scramble[curr]);
409
                    scramble[curr][1] = (1<<scramble[curr][1]);
410
                    break;
411
      case LAST_LO:
412
      case LAST_UP: layer = mLastRot==LAST_LO ? 1:0;
413
                    nextAngle = getNextAngle(rnd,layer);
414

    
415
                    if( nextAngle!=0 )
416
                      {
417
                      scramble[curr][0] = 0;
418
                      scramble[curr][1] = 2*layer;
419
                      scramble[curr][2] = nextAngle;
420
                      updateCornerQuats(scramble[curr]);
421
                      scramble[curr][1] = (1<<scramble[curr][1]);
422
                      mLastRot = LAST_UL;
423
                      }
424
                    else
425
                      {
426
                      scramble[curr][0] = 1;
427
                      scramble[curr][1] = rnd.nextInt(2);
428
                      scramble[curr][2] = 1;
429
                      mLastRot = LAST_SL;
430
                      updateCornerQuats(scramble[curr]);
431
                      scramble[curr][1] = (1<<scramble[curr][1]);
432
                      computePermittedAngles();
433
                      }
434

    
435
                    break;
436
      case LAST_UL: scramble[curr][0] = 1;
437
                    scramble[curr][1] = rnd.nextInt(2);
438
                    scramble[curr][2] = 1;
439
                    mLastRot = LAST_SL;
440
                    updateCornerQuats(scramble[curr]);
441
                    computePermittedAngles();
442
                    scramble[curr][1] = (1<<scramble[curr][1]);
443
                    break;
444
      }
445
    }
446

    
447
///////////////////////////////////////////////////////////////////////////////////////////////////
448
// TYPE 2
449

    
450
  private void buildMoveForced(int[][] scramble, Random rnd, int curr)
451
    {
452
    ScrambleStateLocallyBandaged currState = mStatesHistory.get(curr);
453
    int indexExcluded = curr>0 ? scramble[curr-1][0] : ScrambleStateLocallyBandaged.AXIS_NONE;
454
    int numMoves = currState.numMoves(indexExcluded);
455
    ObjectSignature signature;
456

    
457
    if( numMoves==0 )
458
      {
459
      indexExcluded = ScrambleStateLocallyBandaged.AXIS_NONE;
460
      numMoves = currState.numMoves(indexExcluded);
461
      }
462

    
463
    if( numMoves==0 )
464
      {
465
      scramble[curr][0] = 0;
466
      scramble[curr][1] = 0;
467
      scramble[curr][2] = 0;
468
      signature = currState.getSignature();
469
      }
470
    else
471
      {
472
      int randMove = rnd.nextInt(numMoves);
473
      int moveIndex = currState.getNthMove(randMove,indexExcluded);
474
      signature = currState.getMove(moveIndex);
475
      currState.fillOutScramble(scramble[curr],moveIndex);
476
      }
477

    
478
    ScrambleStateLocallyBandaged nextState = new ScrambleStateLocallyBandaged(mNumLayers, signature, mBlacklisted);
479
    mStatesHistory.add(nextState);
480
    }
481

    
482

    
483
///////////////////////////////////////////////////////////////////////////////////////////////////
484
// TYPE 2
485

    
486
  private boolean buildMove2Cons(int[][] scramble, Random rnd, int curr)
487
    {
488
    ScrambleStateLocallyBandaged currState = mStatesHistory.get(curr);
489
    boolean lock= (curr>=2 && scramble[curr-2][0]==scramble[curr-1][0] );
490
    int axisExcluded = lock ? scramble[curr-1][0] : ScrambleStateLocallyBandaged.AXIS_NONE;
491
    int layerExcluded= !lock && curr>=1 ? scramble[curr-1][1] : -1;
492
    int numMoves = currState.numMoves(axisExcluded,layerExcluded);
493

    
494
    while( numMoves==0 )
495
      {
496
      if( curr>0 )
497
        {
498
        mStatesHistory.remove(curr);
499
        ScrambleStateLocallyBandaged prevState = mStatesHistory.get(curr-1);
500
        ObjectSignature signature = currState.getSignature();
501
        prevState.removeMoves(signature);
502
        mBlacklisted.add(signature);
503
        boolean result = buildMove2Cons(scramble,rnd,curr-1);
504
        if( !result ) return false;
505
        currState = mStatesHistory.get(curr);
506
        lock= (curr>=2 && scramble[curr-2][0]==scramble[curr-1][0] );
507
        axisExcluded = lock ? scramble[curr-1][0] : ScrambleStateLocallyBandaged.AXIS_NONE;
508
        layerExcluded= lock ? -1 : scramble[curr-1][1];
509
        numMoves = currState.numMoves(axisExcluded,layerExcluded);
510
        }
511
      else return false;
512
      }
513

    
514
    int randMove = rnd.nextInt(numMoves);
515
    int moveIndex = currState.getNthMove(randMove,axisExcluded,layerExcluded);
516
    ObjectSignature signature = currState.getMove(moveIndex);
517
    currState.fillOutScramble(scramble[curr],moveIndex);
518

    
519
    ScrambleStateLocallyBandaged nextState = new ScrambleStateLocallyBandaged(mNumLayers, signature, mBlacklisted);
520
    mStatesHistory.add(nextState);
521

    
522
    return true;
523
    }
524

    
525
///////////////////////////////////////////////////////////////////////////////////////////////////
526
// TYPE 2
527

    
528
  private boolean buildMove1Cons(int[][] scramble, Random rnd, int curr)
529
    {
530
    ScrambleStateLocallyBandaged currState = mStatesHistory.get(curr);
531
    int axisExcluded = curr>=1 ? scramble[curr-1][0] : ScrambleStateLocallyBandaged.AXIS_NONE;
532
    int numMoves = currState.numMoves(axisExcluded);
533

    
534
    while( numMoves==0 )
535
      {
536
      if( curr>0 )
537
        {
538
        mStatesHistory.remove(curr);
539
        ScrambleStateLocallyBandaged prevState = mStatesHistory.get(curr-1);
540
        ObjectSignature signature = currState.getSignature();
541
        prevState.removeMoves(signature);
542
        mBlacklisted.add(signature);
543
        boolean result = buildMove1Cons(scramble,rnd,curr-1);
544
        if( !result ) return false;
545
        currState = mStatesHistory.get(curr);
546
        axisExcluded = scramble[curr-1][0];
547
        numMoves = currState.numMoves(axisExcluded);
548
        }
549
      else
550
        {
551
        return false;
552
        }
553
      }
554

    
555
    int randMove = rnd.nextInt(numMoves);
556
    int moveIndex = currState.getNthMove(randMove,axisExcluded);
557
    ObjectSignature signature = currState.getMove(moveIndex);
558
    currState.fillOutScramble(scramble[curr],moveIndex);
559

    
560
    ScrambleStateLocallyBandaged nextState = new ScrambleStateLocallyBandaged(mNumLayers, signature, mBlacklisted);
561
    mStatesHistory.add(nextState);
562

    
563
    return true;
564
    }
565

    
566
///////////////////////////////////////////////////////////////////////////////////////////////////
567
// TYPE 2
568

    
569
  private void initializeType2Scrambling(int[][] scramble, Random rnd, int total, ObjectSignature signature)
570
    {
571
    if( mStatesHistory==null ) mStatesHistory = new ArrayList<>();
572
    else                       mStatesHistory.clear();
573

    
574
    if( mBlacklisted==null ) mBlacklisted = BlacklistedSignatures.getInstance();
575
    else                     mBlacklisted.clear();
576

    
577
    ScrambleStateLocallyBandaged state = new ScrambleStateLocallyBandaged(mNumLayers, signature, mBlacklisted);
578
    mStatesHistory.add(state);
579
    boolean success = true;
580

    
581
    for(int curr=0; curr<total; curr++)
582
      {
583
      boolean result = buildMove1Cons(scramble,rnd,curr);
584
      if( !result )
585
        {
586
        success = false;
587
        break;
588
        }
589
      }
590

    
591
    if( !success )
592
      {
593
      success = true;
594
      mStatesHistory.clear();
595
      mBlacklisted.clear();
596
      state = new ScrambleStateLocallyBandaged(mNumLayers, signature, mBlacklisted);
597
      mStatesHistory.add(state);
598

    
599
      for(int curr=0; curr<total; curr++)
600
        {
601
        boolean result = buildMove2Cons(scramble,rnd,curr);
602
        if( !result )
603
          {
604
          success = false;
605
          break;
606
          }
607
        }
608
      }
609

    
610
    if( !success )
611
      {
612
      mStatesHistory.clear();
613
      mBlacklisted.clear();
614
      state = new ScrambleStateLocallyBandaged(mNumLayers, signature, mBlacklisted);
615
      mStatesHistory.add(state);
616

    
617
      for(int curr=0; curr<total; curr++)
618
        {
619
        buildMoveForced(scramble,rnd,curr);
620
        }
621
      }
622
    }
623

    
624
///////////////////////////////////////////////////////////////////////////////////////////////////
625
// TYPE 2   (locally-created bandaged cuboids, pyraminxes & dodecahedrons)
626

    
627
  private void randomizeNewScramble2(int[][] scramble, Random rnd, int curr, int total, ObjectSignature signature)
628
    {
629
    if( curr==0 )
630
      {
631
      initializeType2Scrambling(scramble,rnd,total,signature);
632
      int len = scramble.length;
633
      for(int i=0; i<len; i++) scramble[i][1] = (1<<scramble[i][1]);
634
      }
635
    }
636

    
637
///////////////////////////////////////////////////////////////////////////////////////////////////
638
// TYPE 4
639

    
640
  private void initializePhases()
641
    {
642
    int len = mAlgorithms.length;
643
    int x = mNumLayers[0];
644
    int y = mNumLayers[1];
645
    int z = mNumLayers[2];
646

    
647
    int xCoreL=-1, xCoreR=-1, yCoreL=-1, yCoreR=-1, zCoreL=-1, zCoreR=-1;
648

    
649
    if( ((x+y+z)%2)==0 )
650
      {
651
      int m = x<y ? Math.min(x,z) : Math.min(y,z);
652

    
653
      xCoreL = (x-m)/2;
654
      xCoreR = (x+m)/2 -1;
655
      yCoreL = (y-m)/2;
656
      yCoreR = (y+m)/2 -1;
657
      zCoreL = (z-m)/2;
658
      zCoreR = (z+m)/2 -1;
659
      }
660
    else
661
      {
662
      if( ((x-y)%2) != 0 && ((x-z)%2) != 0 )
663
        {
664
        int m = Math.min(y,z);
665

    
666
        xCoreL = 0;
667
        xCoreR = x-1;
668
        yCoreL = (y-m)/2;
669
        yCoreR = (y+m)/2 -1;
670
        zCoreL = (z-m)/2;
671
        zCoreR = (z+m)/2 -1;
672
        }
673
      else if( ((y-x)%2) != 0 && ((y-z)%2) != 0 )
674
        {
675
        int m = Math.min(x,z);
676

    
677
        xCoreL = (x-m)/2;
678
        xCoreR = (x+m)/2 -1;
679
        yCoreL = 0;
680
        yCoreR = y-1;
681
        zCoreL = (z-m)/2;
682
        zCoreR = (z+m)/2 -1;
683
        }
684
      else if( ((z-x)%2) != 0 && ((z-y)%2) != 0 )
685
        {
686
        int m = Math.min(x,y);
687

    
688
        xCoreL = (x-m)/2;
689
        xCoreR = (x+m)/2 -1;
690
        yCoreL = (y-m)/2;
691
        yCoreR = (y+m)/2 -1;
692
        zCoreL = 0;
693
        zCoreR = z-1;
694
        }
695
      }
696

    
697
    xCoreL = (1<<xCoreL);
698
    xCoreR = (1<<xCoreR);
699
    yCoreL = (1<<yCoreL);
700
    yCoreR = (1<<yCoreR);
701
    zCoreL = (1<<zCoreL);
702
    zCoreR = (1<<zCoreR);
703

    
704
    if( mPhase1Permitted==null ) mPhase1Permitted = new boolean[len];
705
    if( mPhase2Permitted==null ) mPhase2Permitted = new boolean[len];
706

    
707
    for(int i=0; i<len; i++)
708
      {
709
      int[] alg = mAlgorithms[i];
710
      int axis = alg[0];
711
      int layer= alg[1];
712
      int angle= alg[2];
713

    
714
      mPhase1Permitted[i] = (angle!=-1 && angle!=1) ||
715
                            ((axis!=0 || y==z || ((y-z)%2!=0)) &&
716
                             (axis!=1 || x==z || ((x-z)%2!=0)) &&
717
                             (axis!=2 || x==y || ((x-y)%2!=0))  );
718

    
719
      mPhase2Permitted[i] = ( (axis==0 && layer>=xCoreL && layer<=xCoreR) ||
720
                              (axis==1 && layer>=yCoreL && layer<=yCoreR) ||
721
                              (axis==2 && layer>=zCoreL && layer<=zCoreR)  );
722
      }
723
    }
724

    
725
///////////////////////////////////////////////////////////////////////////////////////////////////
726
// TYPE 4
727

    
728
  private void randomizeNewScramble4(int[][] scramble, Random rnd, int curr, int total)
729
    {
730
    if( curr==0 )
731
      {
732
      mCurrStep     = 100000;
733
      mCurrState    = 0;
734
      mAxisExcluded =-1;
735
      initializeScrambling();
736
      initializePhases();
737
      }
738

    
739
    boolean[] phase = 2.2f*curr<total ? mPhase1Permitted : mPhase2Permitted;
740

    
741
    if( mCurrStep+6 <= mAlgorithms[mCurrAlgorithm].length )
742
      {
743
      mCurrStep += 3;
744
      }
745
    else
746
      {
747
      do
748
        {
749
        mStates[mCurrState].getRandom(rnd, mAlgorithms, mAxisExcluded, mScrambleTable, mNumOccurences, mResult);
750
        }
751
      while( !phase[mResult[0]] );
752

    
753
      mCurrAlgorithm = mResult[0];
754
      mCurrState     = mResult[1];
755
      mCurrStep      = 0;
756
      }
757

    
758
    int[] algorithm = mAlgorithms[mCurrAlgorithm];
759
    scramble[curr][0] = algorithm[mCurrStep  ];
760
    scramble[curr][1] = algorithm[mCurrStep+1];
761
    scramble[curr][2] = algorithm[mCurrStep+2];
762

    
763
    mAxisExcluded = algorithm[0];
764
    }
765

    
766
///////////////////////////////////////////////////////////////////////////////////////////////////
767
// PUBLIC API
768

    
769
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total, ObjectSignature signature)
770
    {
771
    if( mTablebase!=null )
772
      {
773
      if( curr==0 ) mTablebase.scramble(rnd,total,scramble);
774
      }
775
    else
776
      {
777
      switch(mType)
778
        {
779
        case SCRAMBLING_ALGORITHMS  : randomizeNewScramble0(scramble, rnd, curr); break;
780
        case SCRAMBLING_SQUARE1     : randomizeNewScramble1(scramble, rnd, curr); break;
781
        case SCRAMBLING_BANDAGED    : randomizeNewScramble2(scramble, rnd, curr, total, signature); break;
782
        case SCRAMBLING_SHAPESHIFTER: randomizeNewScramble4(scramble, rnd, curr, total); break;
783
        }
784
      }
785
    }
786
  }
(2-2/7)