Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / scrambling / ScrambleStateBandaged3x3.java @ d12d901a

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2022 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

    
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25
// Producer of the ScrambleStateGraph for any bandaged 3x3x3.
26

    
27
public class ScrambleStateBandaged3x3
28
{
29
  private static final long INVALID_MOVE = -1;
30
  private static final int NUM_MOVES = 27;
31

    
32
  private static final int AXIS_X = 0;
33
  private static final int AXIS_Y = 1;
34
  private static final int AXIS_Z = 2;
35

    
36
  private static final int LAYER_L = 0;
37
  private static final int LAYER_M = 1;
38
  private static final int LAYER_R = 2;
39

    
40
  private long mID;
41
  private int mDistance;
42
  private final long[] mMoves;
43

    
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45

    
46
  public ScrambleStateBandaged3x3(long id)
47
    {
48
    mDistance = -1;
49
    mID = id;
50
    mMoves = createMoves(mID);
51
    }
52

    
53
///////////////////////////////////////////////////////////////////////////////////////////////////
54

    
55
  public static void computeGraph(long id)
56
    {
57
    ScrambleStateBandaged3x3 bsg = new ScrambleStateBandaged3x3(id);
58
    ArrayList<ScrambleStateBandaged3x3> graph = new ArrayList<>();
59
    graph.add(bsg);
60

    
61
    insertChildren(graph,id);
62
    pruneGraph(graph,id,false);
63
    computeDistance(graph,id,0);
64
    removeDisconnectedParts(graph);
65
    remapGraph(graph);
66

    
67
    int num = graph.size();
68
    android.util.Log.e("D", "\n"+num+" states\n");
69

    
70
    for(int i=0; i<num; i++)
71
      {
72
      bsg = graph.get(i);
73
      android.util.Log.e("D", formatMoves(bsg));
74
      }
75
    }
76

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

    
79
  private static void insertChildren(ArrayList<ScrambleStateBandaged3x3> list, long id)
80
    {
81
    ScrambleStateBandaged3x3 bsg = findState(list,id);
82

    
83
    if( bsg==null )
84
      {
85
      android.util.Log.e("D", "error: "+id+" doesn't exist");
86
      return;
87
      }
88

    
89
    for(int i=0; i<NUM_MOVES; i++)
90
      {
91
      long move = bsg.getMove(i);
92

    
93
      if( move!=INVALID_MOVE )
94
        {
95
        ScrambleStateBandaged3x3 tmp = findState(list,move);
96

    
97
        if( tmp==null )
98
          {
99
          tmp = new ScrambleStateBandaged3x3(move);
100
          list.add(tmp);
101
          insertChildren(list,move);
102
          }
103
        else if( tmp.mID != move )
104
          {
105
          bsg.setMove(i,tmp.mID);
106
          }
107
        }
108
      }
109
    }
110

    
111
///////////////////////////////////////////////////////////////////////////////////////////////////
112

    
113
  private static void pruneGraph(ArrayList<ScrambleStateBandaged3x3> list, long id, boolean startingPrunedAlready)
114
    {
115
    int num = list.size();
116
    boolean pruned = false;
117
    ScrambleStateBandaged3x3 bsg;
118

    
119
    for(int i=0; i<num; i++)
120
      {
121
      bsg = list.get(i);
122

    
123
      if( bsg.numAxis()<2 )
124
        {
125
        long prunedID = bsg.getID();
126
        if( id!=prunedID || !startingPrunedAlready )
127
          {
128
          startingPrunedAlready = true;
129
          remapID(list,prunedID,INVALID_MOVE);
130
          }
131

    
132
        // do not remove the starting point, even if it does have only 1 axis
133
        if( id!=prunedID )
134
          {
135
          list.remove(i);
136
          pruned = true;
137
          break;
138
          }
139
        }
140
      }
141

    
142
    if( pruned ) pruneGraph(list,id,startingPrunedAlready);
143
    }
144

    
145
///////////////////////////////////////////////////////////////////////////////////////////////////
146

    
147
  private static void remapGraph(ArrayList<ScrambleStateBandaged3x3> list)
148
    {
149
    long id;
150
    int num = list.size();
151
    ScrambleStateBandaged3x3 bsg;
152

    
153
    for(int i=0; i<num; i++ )
154
      {
155
      bsg = list.get(i);
156
      id = bsg.getID();
157
      bsg.setID(i);
158
      remapID(list,id,i);
159
      }
160
    }
161

    
162
///////////////////////////////////////////////////////////////////////////////////////////////////
163

    
164
  private static void remapID(ArrayList<ScrambleStateBandaged3x3> list, long id, long newId)
165
    {
166
    ScrambleStateBandaged3x3 bsg;
167
    int size = list.size();
168

    
169
    for(int i=0; i<size; i++)
170
      {
171
      bsg = list.get(i);
172

    
173
      for(int j=0; j<NUM_MOVES; j++)
174
        {
175
        if( bsg.getMove(j)==id ) bsg.setMove(j,newId);
176
        }
177
      }
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
  private static void computeDistance(ArrayList<ScrambleStateBandaged3x3> list, long id, int distance)
183
    {
184
    ScrambleStateBandaged3x3 state = findState(list,id);
185

    
186
    if( state==null )
187
      {
188
      android.util.Log.e("D", "error: "+id+" doesn't exist");
189
      return;
190
      }
191

    
192
    if( state.mDistance<0 )
193
      {
194
      state.mDistance = distance;
195

    
196
      for(int i=0; i<NUM_MOVES; i++)
197
        {
198
        long move = state.getMove(i);
199

    
200
        if( move!=INVALID_MOVE )
201
          {
202
          computeDistance(list,move,distance+1);
203
          }
204
        }
205
      }
206
    }
207

    
208
///////////////////////////////////////////////////////////////////////////////////////////////////
209

    
210
  private static void removeDisconnectedParts(ArrayList<ScrambleStateBandaged3x3> list)
211
    {
212
    int num = list.size();
213
    ScrambleStateBandaged3x3 bsg;
214

    
215
    for(int i=0; i<num; i++)
216
      {
217
      bsg = list.get(i);
218

    
219
      if( bsg.mDistance<0 )
220
        {
221
        list.remove(i);
222
        i--;
223
        num--;
224
        }
225
      }
226
    }
227

    
228
///////////////////////////////////////////////////////////////////////////////////////////////////
229

    
230
  private static ScrambleStateBandaged3x3 findState(ArrayList<ScrambleStateBandaged3x3> list, long id)
231
    {
232
    ScrambleStateBandaged3x3 bsg;
233
    int num = list.size();
234

    
235
    for(int i=0; i<num; i++)
236
      {
237
      bsg= list.get(i);
238
      if( bsg.mID==id ) return bsg;
239
      }
240

    
241
    return null;
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

    
246
  private static String formatMoves(ScrambleStateBandaged3x3 bsg)
247
    {
248
    String x = getTable(bsg, 0);
249
    String y = getTable(bsg, 9);
250
    String z = getTable(bsg,18);
251

    
252
    return "    new ScrambleState( new int[][] { "+x+", "+y+", "+z+" } ),";
253
    }
254

    
255
///////////////////////////////////////////////////////////////////////////////////////////////////
256

    
257
  private static String getTable(ScrambleStateBandaged3x3 sc, int index)
258
    {
259
    long m0 = sc.getMove(index  );
260
    long m1 = sc.getMove(index+1);
261
    long m2 = sc.getMove(index+2);
262
    long m3 = sc.getMove(index+3);
263
    long m4 = sc.getMove(index+4);
264
    long m5 = sc.getMove(index+5);
265
    long m6 = sc.getMove(index+6);
266
    long m7 = sc.getMove(index+7);
267
    long m8 = sc.getMove(index+8);
268

    
269
    String ret = "";
270

    
271
    if( m0!=INVALID_MOVE ) ret += formatRet(ret,0,-1,m0);
272
    if( m1!=INVALID_MOVE ) ret += formatRet(ret,0, 2,m1);
273
    if( m2!=INVALID_MOVE ) ret += formatRet(ret,0, 1,m2);
274
    if( m3!=INVALID_MOVE ) ret += formatRet(ret,1,-1,m3);
275
    if( m4!=INVALID_MOVE ) ret += formatRet(ret,1, 2,m4);
276
    if( m5!=INVALID_MOVE ) ret += formatRet(ret,1, 1,m5);
277
    if( m6!=INVALID_MOVE ) ret += formatRet(ret,2,-1,m6);
278
    if( m7!=INVALID_MOVE ) ret += formatRet(ret,2, 2,m7);
279
    if( m8!=INVALID_MOVE ) ret += formatRet(ret,2, 1,m8);
280

    
281
    return formatL("{" + ret + "}");
282
    }
283

    
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285

    
286
  private static String formatRet(String str, int row, int angle, long id)
287
    {
288
    String ret = str.length()!=0 ? ",":"";
289

    
290
    ret += row;
291
    ret += angle<0 ? "," : ", ";
292
    ret += angle;
293

    
294
         if( id< 10 ) ret += (",  "+id);
295
    else if( id<100 ) ret += (", " +id);
296
    else              ret += (","  +id);
297

    
298
    return ret;
299
    }
300

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302

    
303
  private static final int LENGTH = 28;
304

    
305
  private static String formatL(String input)
306
    {
307
    int len = input.length();
308
    String ret = input;
309
    for(int i=0 ;i<LENGTH-len; i++) ret += " ";
310
    return ret;
311
    }
312

    
313
///////////////////////////////////////////////////////////////////////////////////////////////////
314

    
315
  private long getID()
316
    {
317
    return mID;
318
    }
319

    
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321

    
322
  private void setID(long id)
323
    {
324
    mID = id;
325
    }
326

    
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328

    
329
  private long getMove(int index)
330
    {
331
    return (index>=0 && index<NUM_MOVES) ? mMoves[index] : INVALID_MOVE;
332
    }
333

    
334
///////////////////////////////////////////////////////////////////////////////////////////////////
335

    
336
  private int numAxis()
337
    {
338
    int num = 0;
339

    
340
    if( mMoves[ 0]!=INVALID_MOVE || mMoves[ 1]!=INVALID_MOVE || mMoves[ 2]!=INVALID_MOVE ||
341
        mMoves[ 3]!=INVALID_MOVE || mMoves[ 4]!=INVALID_MOVE || mMoves[ 5]!=INVALID_MOVE ||
342
        mMoves[ 6]!=INVALID_MOVE || mMoves[ 7]!=INVALID_MOVE || mMoves[ 8]!=INVALID_MOVE   ) num++;
343

    
344
    if( mMoves[ 9]!=INVALID_MOVE || mMoves[10]!=INVALID_MOVE || mMoves[11]!=INVALID_MOVE ||
345
        mMoves[12]!=INVALID_MOVE || mMoves[13]!=INVALID_MOVE || mMoves[14]!=INVALID_MOVE ||
346
        mMoves[15]!=INVALID_MOVE || mMoves[16]!=INVALID_MOVE || mMoves[17]!=INVALID_MOVE   ) num++;
347

    
348
    if( mMoves[18]!=INVALID_MOVE || mMoves[19]!=INVALID_MOVE || mMoves[20]!=INVALID_MOVE ||
349
        mMoves[21]!=INVALID_MOVE || mMoves[22]!=INVALID_MOVE || mMoves[23]!=INVALID_MOVE ||
350
        mMoves[24]!=INVALID_MOVE || mMoves[25]!=INVALID_MOVE || mMoves[26]!=INVALID_MOVE   ) num++;
351

    
352
    return num;
353
    }
354

    
355
///////////////////////////////////////////////////////////////////////////////////////////////////
356

    
357
  private void setMove(int index, long newMove)
358
    {
359
    if( index>=0 && index<NUM_MOVES ) mMoves[index] = newMove;
360
    }
361

    
362
///////////////////////////////////////////////////////////////////////////////////////////////////
363

    
364
  private static long[] createMoves(long id)
365
    {
366
    long[] ret = new long[NUM_MOVES];
367
    int index = 0;
368

    
369
    for(int axis=0; axis<3; axis++)
370
      for(int layer=0; layer<3; layer++)
371
        {
372
        long x1 = turn(id,axis,layer);
373

    
374
        if( x1!=INVALID_MOVE )
375
          {
376
          long x2 = turn(x1,axis,layer);
377
          long x3 = turn(x2,axis,layer);
378

    
379
          ret[index  ] = x1;
380
          ret[index+1] = x2;
381
          ret[index+2] = x3;
382
          }
383
        else
384
          {
385
          ret[index  ] = INVALID_MOVE;
386
          ret[index+1] = INVALID_MOVE;
387
          ret[index+2] = INVALID_MOVE;
388
          }
389

    
390
        index+=3;
391
        }
392

    
393
    return ret;
394
    }
395

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397
// Definition of the id: it's an 'Andreas signature' of a bandaged cube.
398
// https://twistypuzzles.com/forum/viewtopic.php?t=24452&sid=f3b4ac0c611c4713e4d7840f1aabbb0b&start=350
399

    
400
  private static long turn(long id, int axis, int layer)
401
    {
402
    switch(axis)
403
      {
404
      case AXIS_X: switch(layer)
405
                     {
406
                     case LAYER_L: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
407
                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
408
                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1  ) return INVALID_MOVE;
409

    
410
                                   long x1 = cycle(id,9,14,46,41);
411
                                   long x2 = cycle(x1,4,35,51,20);
412
                                   return    cycle(x2,17,25,38,30);
413

    
414
                     case LAYER_M: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
415
                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
416
                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1 ||
417
                                       getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
418
                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
419
                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
420

    
421
                                   long x4 = cycle(id,8,13,45,40);
422
                                   long x5 = cycle(x4,3,34,50,19);
423
                                   return    cycle(x5,16,24,37,29);
424

    
425
                     case LAYER_R: if( getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
426
                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
427
                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
428

    
429
                                   long x7 = cycle(id,7,12,44,39);
430
                                   long x8 = cycle(x7,2,33,49,18);
431
                                   return    cycle(x8,15,23,36,28);
432
                     }
433

    
434
      case AXIS_Y: switch(layer)
435
                     {
436
                     case LAYER_L: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
437
                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
438
                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1  ) return INVALID_MOVE;
439

    
440
                                   long y1 = cycle(id,1,9,10,2);
441
                                   long y2 = cycle(y1,0,4,11,7);
442
                                   return    cycle(y2,3,6,8,5);
443

    
444
                     case LAYER_M: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
445
                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
446
                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1 ||
447
                                       getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
448
                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
449
                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
450

    
451
                                   long y4 = cycle(id,21,25,32,28);
452
                                   long y5 = cycle(y4,22,30,31,23);
453
                                   return    cycle(y5,24,27,29,26);
454

    
455
                     case LAYER_R: if( getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
456
                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
457
                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
458

    
459
                                   long y7 = cycle(id,42,46,53,49);
460
                                   long y8 = cycle(y7,43,51,52,44);
461
                                   return    cycle(y8,45,48,50,47);
462
                     }
463

    
464
      case AXIS_Z: switch(layer)
465
                     {
466
                     case LAYER_L: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
467
                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
468
                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1  ) return INVALID_MOVE;
469

    
470
                                   long z1 = cycle(id,10,20,53,39);
471
                                   long z2 = cycle(z1,11,41,52,18);
472
                                   return    cycle(z2,19,32,40,31);
473

    
474
                     case LAYER_M: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
475
                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
476
                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1 ||
477
                                       getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
478
                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
479
                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
480

    
481
                                   long z4 = cycle(id,5,17,48,36);
482
                                   long z5 = cycle(z4,6,38,47,15);
483
                                   return    cycle(z5,16,27,37,26);
484

    
485
                     case LAYER_R: if( getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
486
                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
487
                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
488

    
489
                                   long z7 = cycle(id,0,14,43,33);
490
                                   long z8 = cycle(z7,1,35,42,12);
491
                                   return    cycle(z8,13,22,34,21);
492
                     }
493
      }
494

    
495
    return 0;
496
    }
497

    
498
///////////////////////////////////////////////////////////////////////////////////////////////////
499
// bit b1 in place of b2 etc.
500

    
501
  private static long cycle(long id, int b1, int b2, int b3, int b4)
502
    {
503
    long bit1 = getBit(id,b1);
504
    long bit2 = getBit(id,b2);
505
    long bit3 = getBit(id,b3);
506
    long bit4 = getBit(id,b4);
507

    
508
    long i1 = setBit(id,b2,bit1);
509
    long i2 = setBit(i1,b3,bit2);
510
    long i3 = setBit(i2,b4,bit3);
511
    return    setBit(i3,b1,bit4);
512
    }
513

    
514
///////////////////////////////////////////////////////////////////////////////////////////////////
515

    
516
  private static long getBit(long id, int bit)
517
    {
518
    return (id>>bit)&0x1;
519
    }
520

    
521
///////////////////////////////////////////////////////////////////////////////////////////////////
522

    
523
  private static long setBit(long id, int bit, long value)
524
    {
525
    long old = getBit(id,bit);
526

    
527
    if( old!=value )
528
      {
529
      long diff = (1L<<bit);
530
      id += (value==0 ? -diff : diff);
531
      }
532

    
533
    return id;
534
    }
535

    
536
///////////////////////////////////////////////////////////////////////////////////////////////////
537
/*
538
  private void printMoves()
539
    {
540
    String moves = "";
541

    
542
    for(int i=0; i<NUM_MOVES; i++)
543
      {
544
      moves += (" " + mMoves[i]);
545
      }
546

    
547
    android.util.Log.e("D", moves);
548
    }
549

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

    
552
  private static String printBits(long id)
553
    {
554
    String ret = "[";
555
    boolean first = true;
556

    
557
    for(int i=0; i<64; i++)
558
      {
559
      if( ( (id>>i)&0x1)==1 )
560
        {
561
        String num = (i<10 ? " "+i : ""+i);
562

    
563
        if( first ) { ret += num; first=false; }
564
        else          ret += (","+num);
565
        }
566
      }
567

    
568
    return ret + "]";
569
    }
570
*/
571
}
(3-3/4)