Project

General

Profile

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

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

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

    
22
import java.util.ArrayList;
23
import java.util.Random;
24

    
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26

    
27
public class ObjectScrambler
28
  {
29
  private final ScrambleState[] mStates;
30
  private final int mType;
31
  private final int mNumAxis;
32
  private final int[] mNumLayers;
33

    
34
  // type=0, i.e. main
35
  private int mCurrState;
36
  private int mIndexExcluded;
37
  private int[][] mScrambleTable;
38
  private int[] mNumOccurences;
39

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

    
47
  private int[][] mPermittedAngles;
48
  private int[] mCornerQuat;
49
  private int mPermittedUp, mPermittedDo;
50
  private int[][] mBadCornerQuats;
51
  private int mLastRot;
52
  private int[][] mQuatMult;
53

    
54
  // type=2 , i.e. locally created bandaged 3x3s
55
  private static long mSignature;
56
  private ArrayList<ScrambleStateBandaged3x3> mBandagedStates;
57

    
58
///////////////////////////////////////////////////////////////////////////////////////////////////
59

    
60
  public ObjectScrambler(int type, int numAxis, int[] numLayers, ScrambleState[] states)
61
    {
62
    mType = type;
63
    mNumAxis = numAxis;
64
    mNumLayers = numLayers;
65
    mStates = states;
66

    
67
    if( mType==1 )
68
      {
69
      mPermittedAngles = new int[2][BASIC_ANGLE];
70
      mCornerQuat = new int[8];
71
      mLastRot = LAST_SL;
72
      }
73
    }
74

    
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76

    
77
  private void initializeScrambling()
78
    {
79
    if( mScrambleTable ==null )
80
      {
81
      mScrambleTable = new int[mNumAxis][];
82
      }
83
    if( mNumOccurences ==null )
84
      {
85
      int max=0;
86

    
87
      for (ScrambleState mState : mStates)
88
        {
89
        int tmp = mState.getTotal(-1);
90
        if (max < tmp) max = tmp;
91
        }
92

    
93
      mNumOccurences = new int[max];
94
      }
95

    
96
    for(int i=0; i<mNumAxis; i++)
97
      {
98
      int len = mNumLayers[i];
99
      mScrambleTable[i] = new int[len];
100
      for(int j=0; j<len; j++) mScrambleTable[i][j] = 0;
101
      }
102
    }
103

    
104
///////////////////////////////////////////////////////////////////////////////////////////////////
105
// PUBLIC API
106

    
107
  private void randomizeNewScramble0(int[][] scramble, Random rnd, int curr, int total)
108
    {
109
    if( curr==0 )
110
      {
111
      mCurrState     = 0;
112
      mIndexExcluded =-1;
113
      initializeScrambling();
114
      }
115

    
116
    int[] info= mStates[mCurrState].getRandom(rnd, mIndexExcluded, mScrambleTable, mNumOccurences);
117

    
118
    scramble[curr][0] = info[0];
119
    scramble[curr][1] = info[1];
120
    scramble[curr][2] = info[2];
121

    
122
    mCurrState     = info[3];
123
    mIndexExcluded = info[0];
124
    }
125

    
126
///////////////////////////////////////////////////////////////////////////////////////////////////
127
// TYPE 1
128
///////////////////////////////////////////////////////////////////////////////////////////////////
129
// QUATS[i]*QUATS[j] = QUATS[QUAT_MULT[i][j]]
130
///////////////////////////////////////////////////////////////////////////////////////////////////
131

    
132
  void initializeQuatMult()
133
    {
134
    mQuatMult = new int[][]
135
      {
136
        {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,},
137
        {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12,},
138
        {  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13,},
139
        {  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14,},
140
        {  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15,},
141
        {  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16,},
142
        {  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17,},
143
        {  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18,},
144
        {  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19,},
145
        {  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20,},
146
        { 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,},
147
        { 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,},
148
        { 12, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,},
149
        { 13, 12, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,},
150
        { 14, 13, 12, 23, 22, 21, 20, 19, 18, 17, 16, 15,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,},
151
        { 15, 14, 13, 12, 23, 22, 21, 20, 19, 18, 17, 16,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,},
152
        { 16, 15, 14, 13, 12, 23, 22, 21, 20, 19, 18, 17,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,},
153
        { 17, 16, 15, 14, 13, 12, 23, 22, 21, 20, 19, 18,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,},
154
        { 18, 17, 16, 15, 14, 13, 12, 23, 22, 21, 20, 19,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,},
155
        { 19, 18, 17, 16, 15, 14, 13, 12, 23, 22, 21, 20,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,},
156
        { 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 22, 21,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,},
157
        { 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 22,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,},
158
        { 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11,},
159
        { 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,}
160
      };
161
    }
162

    
163
///////////////////////////////////////////////////////////////////////////////////////////////////
164
// TYPE 1
165

    
166
  private boolean cornerIsUp(int index)
167
    {
168
    return ((index<4) ^ (mCornerQuat[index]>=12));
169
    }
170

    
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172
// TYPE 1
173

    
174
  private boolean cornerIsLeft(int index)
175
    {
176
    int q = mCornerQuat[index];
177

    
178
    switch(index)
179
      {
180
      case 0:
181
      case 4: return ((q>=3 && q<= 7) || (q>=18 && q<=22));
182
      case 1:
183
      case 5: return ((q>=6 && q<=10) || (q>=15 && q<=19));
184
      case 2:
185
      case 6: return ((q==0 || q==1 || (q>=9 && q<=11)) || (q>=12 && q<=16));
186
      case 3:
187
      case 7: return ((q>=0 && q<=4) || (q==12 || q==13 || (q>=21 && q<=23)));
188
      }
189

    
190
    return false;
191
    }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194
// TYPE 1
195

    
196
  private boolean quatIsBad(int quatIndex, int corner)
197
    {
198
    if( mBadCornerQuats ==null )
199
      {
200
      // quat indices that make corner cubits bandage the puzzle.
201
      mBadCornerQuats = new int[][] { { 2, 8,17,23}, { 5,11,14,20} };
202
      }
203

    
204
    int index = (corner%2);
205

    
206
    return ( quatIndex== mBadCornerQuats[index][0] ||
207
             quatIndex== mBadCornerQuats[index][1] ||
208
             quatIndex== mBadCornerQuats[index][2] ||
209
             quatIndex== mBadCornerQuats[index][3]  );
210
    }
211

    
212
///////////////////////////////////////////////////////////////////////////////////////////////////
213
// TYPE 1
214

    
215
  private boolean isPermittedDo(int angle)
216
    {
217
    if( mQuatMult==null ) initializeQuatMult();
218

    
219
    for(int corner=0; corner<8; corner++)
220
      {
221
      if( !cornerIsUp(corner) )
222
        {
223
        int currQuat = mCornerQuat[corner];
224
        int finalQuat= mQuatMult[angle][currQuat];
225
        if( quatIsBad(finalQuat,corner) ) return false;
226
        }
227
      }
228

    
229
    return true;
230
    }
231

    
232
///////////////////////////////////////////////////////////////////////////////////////////////////
233
// TYPE 1
234

    
235
  private boolean isPermittedUp(int angle)
236
    {
237
    if( mQuatMult==null ) initializeQuatMult();
238

    
239
    for(int corner=0; corner<8; corner++)
240
      {
241
      if( cornerIsUp(corner) )
242
        {
243
        int currQuat = mCornerQuat[corner];
244
        int finalQuat= mQuatMult[angle][currQuat];
245
        if( quatIsBad(finalQuat,corner) ) return false;
246
        }
247
      }
248

    
249
    return true;
250
    }
251

    
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253
// TYPE 1
254

    
255
  private void computePermittedAngles()
256
    {
257
    mPermittedDo = 0;
258

    
259
    for(int angle=0; angle<BASIC_ANGLE; angle++)
260
      {
261
      if( isPermittedDo(angle ) ) mPermittedAngles[0][mPermittedDo++] = angle;
262
      }
263

    
264
    mPermittedUp = 0;
265

    
266
    for(int angle=0; angle<BASIC_ANGLE; angle++)
267
      {
268
      if( isPermittedUp(angle ) ) mPermittedAngles[1][mPermittedUp++] = angle;
269
      }
270
    }
271

    
272
///////////////////////////////////////////////////////////////////////////////////////////////////
273
// TYPE 1
274

    
275
  private int getNextAngle(Random rnd, int layer)
276
    {
277
    int num = layer==0 ? mPermittedDo:mPermittedUp;
278
    int index = rnd.nextInt(num);
279
    int angle = mPermittedAngles[layer][index];
280
    return angle<BASIC_ANGLE/2 ? -angle : BASIC_ANGLE-angle;
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284
// TYPE 1
285

    
286
  private int getNextAngleNotZero(Random rnd, int layer)
287
    {
288
    int num = layer==0 ? mPermittedDo:mPermittedUp;
289
    int index = rnd.nextInt(num-1);
290
    int angle = mPermittedAngles[layer][index+1];
291
    return angle<BASIC_ANGLE/2 ? -angle : BASIC_ANGLE-angle;
292
    }
293

    
294
///////////////////////////////////////////////////////////////////////////////////////////////////
295
// TYPE 1
296

    
297
  private int makeQuat(int axis,int index)
298
    {
299
    if( axis==1 ) return 13;
300
    if( index<0 ) index+=12;
301
    return index;
302
    }
303

    
304
///////////////////////////////////////////////////////////////////////////////////////////////////
305
// TYPE 1
306

    
307
  private boolean cornerBelongs(int index, int axis, int layer)
308
    {
309
    if( axis==0 )
310
      {
311
      boolean up = cornerIsUp(index);
312
      return ((up && layer==2) || (!up && layer==0));
313
      }
314
    else
315
      {
316
      boolean le = cornerIsLeft(index);
317
      return ((le && layer==0) || (!le && layer==1));
318
      }
319
    }
320

    
321
///////////////////////////////////////////////////////////////////////////////////////////////////
322
// TYPE 1
323

    
324
  private void updateCornerQuats(int[] rotInfo)
325
    {
326
    if( mQuatMult==null ) initializeQuatMult();
327

    
328
    int axis = rotInfo[0];
329
    int layer= rotInfo[1];
330
    int index=-rotInfo[2];
331

    
332
    int quat = makeQuat(axis,index);
333

    
334
    for(int corner=0; corner<8; corner++)
335
      {
336
      if( cornerBelongs(corner,axis,layer) )
337
        {
338
        int curr = mCornerQuat[corner];
339
        mCornerQuat[corner] = mQuatMult[quat][curr];
340
        }
341
      }
342
    }
343

    
344
///////////////////////////////////////////////////////////////////////////////////////////////////
345
// TYPE 1
346

    
347
  private void randomizeNewScramble1(int[][] scramble, Random rnd, int curr, int total)
348
    {
349
    int layer, nextAngle;
350

    
351
    if( curr==0 )
352
      {
353
      for(int corner=0; corner<8; corner++) mCornerQuat[corner] = 0;
354
      mLastRot = rnd.nextInt(4);
355
      computePermittedAngles();
356
      }
357

    
358
    switch(mLastRot)
359
      {
360
      case LAST_SL: layer = rnd.nextInt(2);
361
                    nextAngle = getNextAngle(rnd,layer);
362

    
363
                    if( nextAngle==0 )
364
                      {
365
                      layer = 1-layer;
366
                      nextAngle = getNextAngleNotZero(rnd,layer);
367
                      }
368

    
369
                    scramble[curr][0] = 0;
370
                    scramble[curr][1] = 2*layer;
371
                    scramble[curr][2] = nextAngle;
372
                    mLastRot = layer==0 ? LAST_LO : LAST_UP;
373
                    updateCornerQuats(scramble[curr]);
374
                    break;
375
      case LAST_LO:
376
      case LAST_UP: layer = mLastRot==LAST_LO ? 1:0;
377
                    nextAngle = getNextAngle(rnd,layer);
378

    
379
                    if( nextAngle!=0 )
380
                      {
381
                      scramble[curr][0] = 0;
382
                      scramble[curr][1] = 2*layer;
383
                      scramble[curr][2] = nextAngle;
384
                      updateCornerQuats(scramble[curr]);
385
                      mLastRot = LAST_UL;
386
                      }
387
                    else
388
                      {
389
                      scramble[curr][0] = 1;
390
                      scramble[curr][1] = rnd.nextInt(2);
391
                      scramble[curr][2] = 1;
392
                      mLastRot = LAST_SL;
393
                      updateCornerQuats(scramble[curr]);
394
                      computePermittedAngles();
395
                      }
396

    
397
                    break;
398
      case LAST_UL: scramble[curr][0] = 1;
399
                    scramble[curr][1] = rnd.nextInt(2);
400
                    scramble[curr][2] = 1;
401
                    mLastRot = LAST_SL;
402
                    updateCornerQuats(scramble[curr]);
403
                    computePermittedAngles();
404
                    break;
405
      }
406
    }
407

    
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409
// TYPE 2
410

    
411
  private void buildMoveForced(int[][] scramble, Random rnd, int curr)
412
    {
413
    ScrambleStateBandaged3x3 currState = mBandagedStates.get(curr);
414
    int indexExcluded = curr>0 ? scramble[curr-1][0] : ScrambleStateBandaged3x3.AXIS_NONE;
415
    int numMoves = currState.numMoves(indexExcluded);
416

    
417
    if( numMoves==0 )
418
      {
419
      indexExcluded = ScrambleStateBandaged3x3.AXIS_NONE;
420
      numMoves = currState.numMoves(indexExcluded);
421
      }
422

    
423
    if( numMoves==0 )
424
      {
425
      scramble[curr][0] = 0;
426
      scramble[curr][1] = 0;
427
      scramble[curr][2] = 0;
428
      }
429
    else
430
      {
431
      int randMove = rnd.nextInt(numMoves);
432
      int moveIndex = currState.getNthMove(randMove,indexExcluded);
433
      mSignature = currState.getMove(moveIndex);
434

    
435
      scramble[curr][0] = moveIndex/9;
436
      scramble[curr][1] = (moveIndex%9)/3;
437

    
438
      switch(moveIndex%3)
439
        {
440
        case 0: scramble[curr][2] = -1; break;
441
        case 1: scramble[curr][2] =  2; break;
442
        case 2: scramble[curr][2] =  1; break;
443
        }
444
      }
445

    
446
    ScrambleStateBandaged3x3 nextState = new ScrambleStateBandaged3x3(mSignature);
447
    mBandagedStates.add(nextState);
448
    }
449

    
450
///////////////////////////////////////////////////////////////////////////////////////////////////
451
// TYPE 2
452

    
453
  private boolean buildMove(int[][] scramble, Random rnd, int curr)
454
    {
455
    ScrambleStateBandaged3x3 currState = mBandagedStates.get(curr);
456
    int indexExcluded = curr>0 ? scramble[curr-1][0] : ScrambleStateBandaged3x3.AXIS_NONE;
457
    int numMoves = currState.numMoves(indexExcluded);
458

    
459
    while( numMoves==0 )
460
      {
461
      if( curr>0 )
462
        {
463
        mBandagedStates.remove(curr);
464
        ScrambleStateBandaged3x3 prevState = mBandagedStates.get(curr-1);
465
        long signature = currState.getID();
466
        prevState.removeMoves(signature);
467
        boolean result = buildMove(scramble,rnd,curr-1);
468
        if( !result ) return false;
469
        currState = mBandagedStates.get(curr);
470
        indexExcluded = scramble[curr-1][0];
471
        numMoves = currState.numMoves(indexExcluded);
472
        }
473
      else
474
        {
475
        return false;
476
        }
477
      }
478

    
479
    int randMove = rnd.nextInt(numMoves);
480
    int moveIndex = currState.getNthMove(randMove,indexExcluded);
481
    mSignature = currState.getMove(moveIndex);
482

    
483
    scramble[curr][0] = moveIndex/9;
484
    scramble[curr][1] = (moveIndex%9)/3;
485

    
486
    switch(moveIndex%3)
487
      {
488
      case 0: scramble[curr][2] = -1; break;
489
      case 1: scramble[curr][2] =  2; break;
490
      case 2: scramble[curr][2] =  1; break;
491
      }
492

    
493
    ScrambleStateBandaged3x3 nextState = new ScrambleStateBandaged3x3(mSignature);
494
    mBandagedStates.add(nextState);
495

    
496
    return true;
497
    }
498

    
499
///////////////////////////////////////////////////////////////////////////////////////////////////
500
// TYPE 2
501

    
502
  private void initializeType2Scrambling(int[][] scramble, Random rnd, int total)
503
    {
504
    if( mBandagedStates==null ) mBandagedStates = new ArrayList<>();
505
    else                        mBandagedStates.clear();
506

    
507
    ScrambleStateBandaged3x3 state = new ScrambleStateBandaged3x3(mSignature);
508
    mBandagedStates.add(state);
509
    boolean success = true;
510

    
511
    for(int curr=0; curr<total; curr++)
512
      {
513
      boolean result = buildMove(scramble,rnd,curr);
514
      if( !result )
515
        {
516
        success = false;
517
        break;
518
        }
519
      }
520

    
521
    if( !success )
522
      {
523
      mBandagedStates.clear();
524
      state = new ScrambleStateBandaged3x3(mSignature);
525
      mBandagedStates.add(state);
526

    
527
      for(int curr=0; curr<total; curr++)
528
        {
529
        buildMoveForced(scramble,rnd,curr);
530
        }
531
      }
532
    }
533

    
534
///////////////////////////////////////////////////////////////////////////////////////////////////
535
// TYPE 2
536

    
537
  public static void setSignature(long signature)
538
    {
539
    mSignature = signature;
540
    }
541

    
542
///////////////////////////////////////////////////////////////////////////////////////////////////
543
// TYPE 2   (locally-created bandaged 3x3s)
544

    
545
  private void randomizeNewScramble2(int[][] scramble, Random rnd, int curr, int total)
546
    {
547
    if( curr==0 ) initializeType2Scrambling(scramble,rnd,total);
548
    }
549

    
550
///////////////////////////////////////////////////////////////////////////////////////////////////
551
// PUBLIC API
552

    
553
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
554
    {
555
    if( mType==0 ) randomizeNewScramble0(scramble, rnd, curr, total);
556
    if( mType==1 ) randomizeNewScramble1(scramble, rnd, curr, total);
557
    if( mType==2 ) randomizeNewScramble2(scramble, rnd, curr, total);
558
    }
559
  }
(1-1/4)