Project

General

Profile

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

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

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
import org.distorted.objectlib.helpers.ObjectSignature;
26

    
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28

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

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

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

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

    
56
  // type=2 , i.e. locally created bandaged cuboids
57
  private ArrayList<ScrambleStateBandagedCuboid> mBandagedStates;
58

    
59
///////////////////////////////////////////////////////////////////////////////////////////////////
60

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

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

    
76
///////////////////////////////////////////////////////////////////////////////////////////////////
77

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

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

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

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

    
105
///////////////////////////////////////////////////////////////////////////////////////////////////
106
// PUBLIC API
107

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

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

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

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

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

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

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165
// TYPE 1
166

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

    
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173
// TYPE 1
174

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

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

    
191
    return false;
192
    }
193

    
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195
// TYPE 1
196

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

    
205
    int index = (corner%2);
206

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

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214
// TYPE 1
215

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

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

    
230
    return true;
231
    }
232

    
233
///////////////////////////////////////////////////////////////////////////////////////////////////
234
// TYPE 1
235

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

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

    
250
    return true;
251
    }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254
// TYPE 1
255

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

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

    
265
    mPermittedUp = 0;
266

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

    
273
///////////////////////////////////////////////////////////////////////////////////////////////////
274
// TYPE 1
275

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

    
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285
// TYPE 1
286

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

    
295
///////////////////////////////////////////////////////////////////////////////////////////////////
296
// TYPE 1
297

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

    
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306
// TYPE 1
307

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

    
322
///////////////////////////////////////////////////////////////////////////////////////////////////
323
// TYPE 1
324

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

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

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

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

    
345
///////////////////////////////////////////////////////////////////////////////////////////////////
346
// TYPE 1
347

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

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

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

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

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

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

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

    
409
///////////////////////////////////////////////////////////////////////////////////////////////////
410
// TYPE 2
411

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

    
419
    if( numMoves==0 )
420
      {
421
      indexExcluded = ScrambleStateBandagedCuboid.AXIS_NONE;
422
      numMoves = currState.numMoves(indexExcluded);
423
      }
424

    
425
    if( numMoves==0 )
426
      {
427
      scramble[curr][0] = 0;
428
      scramble[curr][1] = 0;
429
      scramble[curr][2] = 0;
430
      signature = currState.getSignature();
431
      }
432
    else
433
      {
434
      int randMove = rnd.nextInt(numMoves);
435
      int moveIndex = currState.getNthMove(randMove,indexExcluded);
436
      signature = currState.getMove(moveIndex);
437
      currState.fillOutScramble(scramble[curr],moveIndex);
438
      }
439

    
440
    ScrambleStateBandagedCuboid nextState = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], signature);
441
    mBandagedStates.add(nextState);
442
    }
443

    
444
///////////////////////////////////////////////////////////////////////////////////////////////////
445
// TYPE 2
446

    
447
  private boolean buildMove(int[][] scramble, Random rnd, int curr)
448
    {
449
    ScrambleStateBandagedCuboid currState = mBandagedStates.get(curr);
450
    int indexExcluded = curr>0 ? scramble[curr-1][0] : ScrambleStateBandagedCuboid.AXIS_NONE;
451
    int numMoves = currState.numMoves(indexExcluded);
452

    
453
    while( numMoves==0 )
454
      {
455
      if( curr>0 )
456
        {
457
        mBandagedStates.remove(curr);
458
        ScrambleStateBandagedCuboid prevState = mBandagedStates.get(curr-1);
459
        ObjectSignature signature = currState.getSignature();
460
        prevState.removeMoves(signature);
461
        boolean result = buildMove(scramble,rnd,curr-1);
462
        if( !result ) return false;
463
        currState = mBandagedStates.get(curr);
464
        indexExcluded = scramble[curr-1][0];
465
        numMoves = currState.numMoves(indexExcluded);
466
        }
467
      else
468
        {
469
        return false;
470
        }
471
      }
472

    
473
    int randMove = rnd.nextInt(numMoves);
474
    int moveIndex = currState.getNthMove(randMove,indexExcluded);
475
    ObjectSignature signature = currState.getMove(moveIndex);
476
    currState.fillOutScramble(scramble[curr],moveIndex);
477

    
478
    ScrambleStateBandagedCuboid nextState = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], signature);
479
    mBandagedStates.add(nextState);
480

    
481
    return true;
482
    }
483

    
484
///////////////////////////////////////////////////////////////////////////////////////////////////
485
// TYPE 2
486

    
487
  private void initializeType2Scrambling(int[][] scramble, Random rnd, int total, ObjectSignature signature)
488
    {
489
    if( mBandagedStates==null ) mBandagedStates = new ArrayList<>();
490
    else                        mBandagedStates.clear();
491

    
492
    ScrambleStateBandagedCuboid state = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], signature);
493
    mBandagedStates.add(state);
494
    boolean success = true;
495

    
496
    for(int curr=0; curr<total; curr++)
497
      {
498
      boolean result = buildMove(scramble,rnd,curr);
499
      if( !result )
500
        {
501
        success = false;
502
        break;
503
        }
504
      }
505

    
506
    if( !success )
507
      {
508
      mBandagedStates.clear();
509
      state = new ScrambleStateBandagedCuboid(mNumLayers[0], mNumLayers[1], mNumLayers[2], signature);
510
      mBandagedStates.add(state);
511

    
512
      for(int curr=0; curr<total; curr++)
513
        {
514
        buildMoveForced(scramble,rnd,curr);
515
        }
516
      }
517
    }
518

    
519
///////////////////////////////////////////////////////////////////////////////////////////////////
520
// TYPE 2   (locally-created bandaged cuboids)
521

    
522
  private void randomizeNewScramble2(int[][] scramble, Random rnd, int curr, int total, ObjectSignature signature)
523
    {
524
    if( curr==0 ) initializeType2Scrambling(scramble,rnd,total,signature);
525
    }
526

    
527
///////////////////////////////////////////////////////////////////////////////////////////////////
528
// PUBLIC API
529

    
530
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total, ObjectSignature signature)
531
    {
532
    switch(mType)
533
      {
534
      case 0: randomizeNewScramble0(scramble, rnd, curr); break;
535
      case 1: randomizeNewScramble1(scramble, rnd, curr); break;
536
      case 2: randomizeNewScramble2(scramble, rnd, curr, total, signature); break;
537
      }
538
    }
539
  }
(1-1/5)