Project

General

Profile

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

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

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 static ObjectSignature mSignature;
58
  private ArrayList<ScrambleStateBandagedCuboid> mBandagedStates;
59

    
60
///////////////////////////////////////////////////////////////////////////////////////////////////
61

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

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

    
77
///////////////////////////////////////////////////////////////////////////////////////////////////
78

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

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

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

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

    
106
///////////////////////////////////////////////////////////////////////////////////////////////////
107
// PUBLIC API
108

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

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

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

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

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

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

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166
// TYPE 1
167

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

    
173
///////////////////////////////////////////////////////////////////////////////////////////////////
174
// TYPE 1
175

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

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

    
192
    return false;
193
    }
194

    
195
///////////////////////////////////////////////////////////////////////////////////////////////////
196
// TYPE 1
197

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

    
206
    int index = (corner%2);
207

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

    
214
///////////////////////////////////////////////////////////////////////////////////////////////////
215
// TYPE 1
216

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

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

    
231
    return true;
232
    }
233

    
234
///////////////////////////////////////////////////////////////////////////////////////////////////
235
// TYPE 1
236

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

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

    
251
    return true;
252
    }
253

    
254
///////////////////////////////////////////////////////////////////////////////////////////////////
255
// TYPE 1
256

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

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

    
266
    mPermittedUp = 0;
267

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

    
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275
// TYPE 1
276

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

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286
// TYPE 1
287

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

    
296
///////////////////////////////////////////////////////////////////////////////////////////////////
297
// TYPE 1
298

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

    
306
///////////////////////////////////////////////////////////////////////////////////////////////////
307
// TYPE 1
308

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

    
323
///////////////////////////////////////////////////////////////////////////////////////////////////
324
// TYPE 1
325

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

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

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

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

    
346
///////////////////////////////////////////////////////////////////////////////////////////////////
347
// TYPE 1
348

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

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

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

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

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

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

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

    
410
///////////////////////////////////////////////////////////////////////////////////////////////////
411
// TYPE 2
412

    
413
  private void buildMoveForced(int[][] scramble, Random rnd, int curr)
414
    {
415
    ScrambleStateBandagedCuboid currState = mBandagedStates.get(curr);
416
    int indexExcluded = curr>0 ? scramble[curr-1][0] : ScrambleStateBandagedCuboid.AXIS_NONE;
417
    int numMoves = currState.numMoves(indexExcluded);
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
      }
431
    else
432
      {
433
      int randMove = rnd.nextInt(numMoves);
434
      int moveIndex = currState.getNthMove(randMove,indexExcluded);
435
      mSignature = currState.getMove(moveIndex);
436
      currState.fillOutScramble(scramble[curr],moveIndex);
437
      }
438

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

    
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444
// TYPE 2
445

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

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

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

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

    
480
    return true;
481
    }
482

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

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

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

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

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

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

    
517
for(int i=0; i<total; i++)
518
  {
519
  android.util.Log.e("D", "scramble "+i+" axis: "+scramble[i][0]+" layer="+scramble[i][1]+" angle="+scramble[i][2]);
520
  }
521
    }
522

    
523
///////////////////////////////////////////////////////////////////////////////////////////////////
524
// TYPE 2
525

    
526
  public static void setSignature(ObjectSignature signature)
527
    {
528
    mSignature = signature;
529
    }
530

    
531
///////////////////////////////////////////////////////////////////////////////////////////////////
532
// TYPE 2   (locally-created bandaged cuboids)
533

    
534
  private void randomizeNewScramble2(int[][] scramble, Random rnd, int curr, int total)
535
    {
536
    if( curr==0 ) initializeType2Scrambling(scramble,rnd,total);
537
    }
538

    
539
///////////////////////////////////////////////////////////////////////////////////////////////////
540
// PUBLIC API
541

    
542
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
543
    {
544
    if( mType==0 ) randomizeNewScramble0(scramble, rnd, curr, total);
545
    if( mType==1 ) randomizeNewScramble1(scramble, rnd, curr, total);
546
    if( mType==2 ) randomizeNewScramble2(scramble, rnd, curr, total);
547
    }
548
  }
(1-1/5)