Project

General

Profile

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

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

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2022 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
5
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
6
///////////////////////////////////////////////////////////////////////////////////////////////////
7

    
8
package org.distorted.objectlib.scrambling;
9

    
10
import java.util.LinkedHashMap;
11
import java.util.Iterator;
12
import java.util.Map;
13

    
14
///////////////////////////////////////////////////////////////////////////////////////////////////
15
// Producer of the ScrambleStateGraph for any bandaged 3x3x3.
16

    
17
public class ScrambleStateBandaged3x3
18
{
19
  public static final int AXIS_X = 0;
20
  public static final int AXIS_Y = 1;
21
  public static final int AXIS_Z = 2;
22

    
23
  private static final long INVALID_MOVE = -1;
24
  private static final int NUM_MOVES = 27;
25

    
26
  private static final int LAYER_L = 0;
27
  private static final int LAYER_M = 1;
28
  private static final int LAYER_R = 2;
29

    
30
  private long mID;
31
  private int mDistance;
32
  private final long[] mMoves;
33

    
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35

    
36
  public ScrambleStateBandaged3x3(long id)
37
    {
38
    mDistance = -1;
39
    mID = id;
40
    mMoves = createMoves(mID);
41
    }
42

    
43
///////////////////////////////////////////////////////////////////////////////////////////////////
44

    
45
  public long getID()
46
    {
47
    return mID;
48
    }
49

    
50
///////////////////////////////////////////////////////////////////////////////////////////////////
51

    
52
  public long getMove(int index)
53
    {
54
    return (index>=0 && index<NUM_MOVES) ? mMoves[index] : INVALID_MOVE;
55
    }
56

    
57
///////////////////////////////////////////////////////////////////////////////////////////////////
58

    
59
  public int numAxis()
60
    {
61
    int num = 0;
62

    
63
    if( mMoves[ 0]!=INVALID_MOVE || mMoves[ 1]!=INVALID_MOVE || mMoves[ 2]!=INVALID_MOVE ||
64
        mMoves[ 3]!=INVALID_MOVE || mMoves[ 4]!=INVALID_MOVE || mMoves[ 5]!=INVALID_MOVE ||
65
        mMoves[ 6]!=INVALID_MOVE || mMoves[ 7]!=INVALID_MOVE || mMoves[ 8]!=INVALID_MOVE   ) num++;
66

    
67
    if( mMoves[ 9]!=INVALID_MOVE || mMoves[10]!=INVALID_MOVE || mMoves[11]!=INVALID_MOVE ||
68
        mMoves[12]!=INVALID_MOVE || mMoves[13]!=INVALID_MOVE || mMoves[14]!=INVALID_MOVE ||
69
        mMoves[15]!=INVALID_MOVE || mMoves[16]!=INVALID_MOVE || mMoves[17]!=INVALID_MOVE   ) num++;
70

    
71
    if( mMoves[18]!=INVALID_MOVE || mMoves[19]!=INVALID_MOVE || mMoves[20]!=INVALID_MOVE ||
72
        mMoves[21]!=INVALID_MOVE || mMoves[22]!=INVALID_MOVE || mMoves[23]!=INVALID_MOVE ||
73
        mMoves[24]!=INVALID_MOVE || mMoves[25]!=INVALID_MOVE || mMoves[26]!=INVALID_MOVE   ) num++;
74

    
75
    return num;
76
    }
77

    
78
///////////////////////////////////////////////////////////////////////////////////////////////////
79

    
80
  private int numXMoves()
81
    {
82
    int num=0;
83

    
84
    if( mMoves[ 0]!=INVALID_MOVE ) num++;
85
    if( mMoves[ 1]!=INVALID_MOVE ) num++;
86
    if( mMoves[ 2]!=INVALID_MOVE ) num++;
87
    if( mMoves[ 3]!=INVALID_MOVE ) num++;
88
    if( mMoves[ 4]!=INVALID_MOVE ) num++;
89
    if( mMoves[ 5]!=INVALID_MOVE ) num++;
90
    if( mMoves[ 6]!=INVALID_MOVE ) num++;
91
    if( mMoves[ 7]!=INVALID_MOVE ) num++;
92
    if( mMoves[ 8]!=INVALID_MOVE ) num++;
93

    
94
    return num;
95
    }
96

    
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98

    
99
  private int numYMoves()
100
    {
101
    int num=0;
102

    
103
    if( mMoves[ 9]!=INVALID_MOVE ) num++;
104
    if( mMoves[10]!=INVALID_MOVE ) num++;
105
    if( mMoves[11]!=INVALID_MOVE ) num++;
106
    if( mMoves[12]!=INVALID_MOVE ) num++;
107
    if( mMoves[13]!=INVALID_MOVE ) num++;
108
    if( mMoves[14]!=INVALID_MOVE ) num++;
109
    if( mMoves[15]!=INVALID_MOVE ) num++;
110
    if( mMoves[16]!=INVALID_MOVE ) num++;
111
    if( mMoves[17]!=INVALID_MOVE ) num++;
112

    
113
    return num;
114
    }
115

    
116
///////////////////////////////////////////////////////////////////////////////////////////////////
117

    
118
  private int numZMoves()
119
    {
120
    int num=0;
121

    
122
    if( mMoves[18]!=INVALID_MOVE ) num++;
123
    if( mMoves[19]!=INVALID_MOVE ) num++;
124
    if( mMoves[20]!=INVALID_MOVE ) num++;
125
    if( mMoves[21]!=INVALID_MOVE ) num++;
126
    if( mMoves[22]!=INVALID_MOVE ) num++;
127
    if( mMoves[23]!=INVALID_MOVE ) num++;
128
    if( mMoves[24]!=INVALID_MOVE ) num++;
129
    if( mMoves[25]!=INVALID_MOVE ) num++;
130
    if( mMoves[26]!=INVALID_MOVE ) num++;
131

    
132
    return num;
133
    }
134

    
135
///////////////////////////////////////////////////////////////////////////////////////////////////
136

    
137
  public int numMoves()
138
    {
139
    return numXMoves()+numYMoves()+numZMoves();
140
    }
141

    
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143

    
144
  public int numMoves(int excludedAxis)
145
    {
146
    switch(excludedAxis)
147
      {
148
      case AXIS_X: return numYMoves()+numZMoves();
149
      case AXIS_Y: return numXMoves()+numZMoves();
150
      case AXIS_Z: return numXMoves()+numYMoves();
151
      }
152

    
153
    int ret= numXMoves()+numYMoves()+numZMoves();
154

    
155
    //android.util.Log.e("D", "numMoves returning "+ret+" "+formatMoves() );
156

    
157
    return ret;
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

    
162
  public int getNthMove(int n, int excludedAxis)
163
    {
164
    int num = 0;
165

    
166
    for(int m=0; m<NUM_MOVES; m++)
167
      {
168
      if( (m/9)!=excludedAxis && mMoves[m]!=INVALID_MOVE)
169
        {
170
        if( num==n ) return m;
171
        num++;
172
        }
173
      }
174

    
175
    return -1;
176
    }
177

    
178
///////////////////////////////////////////////////////////////////////////////////////////////////
179

    
180
  public void removeMoves(long signature)
181
    {
182
    for(int m=0; m<NUM_MOVES; m++)
183
      if( signature==mMoves[m] ) mMoves[m]=INVALID_MOVE;
184
    }
185

    
186
///////////////////////////////////////////////////////////////////////////////////////////////////
187

    
188
  public String formatMoves()
189
    {
190
    String x = getTable( 0);
191
    String y = getTable( 9);
192
    String z = getTable(18);
193

    
194
    return mID+"\n"+x+"\n"+y+"\n"+z;
195
    }
196

    
197
///////////////////////////////////////////////////////////////////////////////////////////////////
198

    
199
  private String getTable(int index)
200
    {
201
    long m0 = getMove(index  );
202
    long m1 = getMove(index+1);
203
    long m2 = getMove(index+2);
204
    long m3 = getMove(index+3);
205
    long m4 = getMove(index+4);
206
    long m5 = getMove(index+5);
207
    long m6 = getMove(index+6);
208
    long m7 = getMove(index+7);
209
    long m8 = getMove(index+8);
210

    
211
    String ret = "";
212

    
213
    if( m0!=INVALID_MOVE ) ret += formatRet(ret,0,-1,m0);
214
    if( m1!=INVALID_MOVE ) ret += formatRet(ret,0, 2,m1);
215
    if( m2!=INVALID_MOVE ) ret += formatRet(ret,0, 1,m2);
216
    if( m3!=INVALID_MOVE ) ret += formatRet(ret,1,-1,m3);
217
    if( m4!=INVALID_MOVE ) ret += formatRet(ret,1, 2,m4);
218
    if( m5!=INVALID_MOVE ) ret += formatRet(ret,1, 1,m5);
219
    if( m6!=INVALID_MOVE ) ret += formatRet(ret,2,-1,m6);
220
    if( m7!=INVALID_MOVE ) ret += formatRet(ret,2, 2,m7);
221
    if( m8!=INVALID_MOVE ) ret += formatRet(ret,2, 1,m8);
222

    
223
    return formatL("{" + ret + "}");
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227

    
228
  private int[] getMoves(int index)
229
    {
230
    int numValid = 0;
231
    for(int i=index; i<index+9; i++) if( mMoves[i]!=INVALID_MOVE ) numValid++;
232

    
233
    int[] ret = new int[3*numValid];
234

    
235
    long m0 = getMove(index  );
236
    long m1 = getMove(index+1);
237
    long m2 = getMove(index+2);
238
    long m3 = getMove(index+3);
239
    long m4 = getMove(index+4);
240
    long m5 = getMove(index+5);
241
    long m6 = getMove(index+6);
242
    long m7 = getMove(index+7);
243
    long m8 = getMove(index+8);
244

    
245
    int pointer=0;
246

    
247
    if( m0!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]=-1; ret[pointer+2]= (int)m0; pointer+=3; }
248
    if( m1!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]= 2; ret[pointer+2]= (int)m1; pointer+=3; }
249
    if( m2!=INVALID_MOVE ) { ret[pointer]=0; ret[pointer+1]= 1; ret[pointer+2]= (int)m2; pointer+=3; }
250
    if( m3!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]=-1; ret[pointer+2]= (int)m3; pointer+=3; }
251
    if( m4!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]= 2; ret[pointer+2]= (int)m4; pointer+=3; }
252
    if( m5!=INVALID_MOVE ) { ret[pointer]=1; ret[pointer+1]= 1; ret[pointer+2]= (int)m5; pointer+=3; }
253
    if( m6!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]=-1; ret[pointer+2]= (int)m6; pointer+=3; }
254
    if( m7!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]= 2; ret[pointer+2]= (int)m7; pointer+=3; }
255
    if( m8!=INVALID_MOVE ) { ret[pointer]=2; ret[pointer+1]= 1; ret[pointer+2]= (int)m8; pointer+=3; }
256

    
257
    return ret;
258
    }
259

    
260
///////////////////////////////////////////////////////////////////////////////////////////////////
261

    
262
  private ScrambleState produceScrambleState()
263
    {
264
    int[] xMoves = getMoves(0);
265
    int[] yMoves = getMoves(9);
266
    int[] zMoves = getMoves(18);
267

    
268
    int[][] moves = { xMoves,yMoves,zMoves };
269

    
270
    return new ScrambleState( moves );
271
    }
272

    
273
///////////////////////////////////////////////////////////////////////////////////////////////////
274
// STATIC STUFF
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276

    
277
  public static ScrambleState[] computeGraph(long id)
278
    {
279
    ScrambleStateBandaged3x3 bsg = new ScrambleStateBandaged3x3(id);
280
    Map<Long, ScrambleStateBandaged3x3> graph = new LinkedHashMap<>();
281
    graph.put(id,bsg);
282

    
283
    insertChildren(graph,id);
284
    // if there's only one state, do not prune moves which point to itself
285
    if(graph.size()>1) pruneGraph(graph,id);
286
    computeDistance(graph,id,0);
287
    removeDisconnectedParts(graph);
288
    remapGraph(graph);
289

    
290
    int num = graph.size();
291
    ScrambleState[] ret = new ScrambleState[num];
292

    
293
    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : graph.entrySet())
294
      {
295
      ScrambleStateBandaged3x3 value = entry.getValue();
296
      int mid = (int)value.mID;
297
      ret[mid] = value.produceScrambleState();
298
      }
299

    
300
    return ret;
301
    }
302

    
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304

    
305
  private static void insertChildren(Map<Long, ScrambleStateBandaged3x3> map, long id)
306
    {
307
    ScrambleStateBandaged3x3 bsg = findState(map,id);
308

    
309
    if( bsg==null )
310
      {
311
      android.util.Log.e("D", "error: "+id+" doesn't exist");
312
      return;
313
      }
314

    
315
    for(int i=0; i<NUM_MOVES; i++)
316
      {
317
      long move = bsg.getMove(i);
318

    
319
      if( move!=INVALID_MOVE )
320
        {
321
        ScrambleStateBandaged3x3 tmp = findState(map,move);
322

    
323
        if( tmp==null )
324
          {
325
          tmp = new ScrambleStateBandaged3x3(move);
326
          map.put(move,tmp);
327
          insertChildren(map,move);
328
          }
329
        }
330
      }
331
    }
332

    
333
///////////////////////////////////////////////////////////////////////////////////////////////////
334

    
335
  private static void pruneGraph(Map<Long, ScrambleStateBandaged3x3> map, long startingID)
336
    {
337
    boolean pruned = false;
338
    boolean startingIsSingle = false;
339

    
340
    Iterator<Map.Entry<Long, ScrambleStateBandaged3x3>> it = map.entrySet().iterator();
341

    
342
    while (it.hasNext())
343
      {
344
      Map.Entry<Long, ScrambleStateBandaged3x3> entry = it.next();
345
      ScrambleStateBandaged3x3 value = entry.getValue();
346

    
347
      if( value.numAxis()<2 )
348
        {
349
        long prunedID = value.getID();
350

    
351
        if( prunedID!=startingID ) // do not remove the starting point, even if it does have only 1 axis
352
          {
353
          it.remove();
354
          pruned = true;
355
          }
356
        else
357
          {
358
          startingIsSingle = true;
359
          }
360
        }
361
      }
362

    
363
    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet() )
364
      {
365
      ScrambleStateBandaged3x3 value = entry.getValue();
366

    
367
      for(int m=0; m<NUM_MOVES; m++)
368
        {
369
        long move = value.mMoves[m];
370
        ScrambleStateBandaged3x3 tmp = findState(map,move);
371

    
372
        if( tmp==null || (startingIsSingle && move==startingID) )
373
          {
374
          value.mMoves[m]=INVALID_MOVE;
375
          }
376
        }
377
      }
378

    
379
    if( pruned ) pruneGraph(map,startingID);
380
    }
381

    
382
///////////////////////////////////////////////////////////////////////////////////////////////////
383

    
384
  private static void remapGraph(Map<Long, ScrambleStateBandaged3x3> map)
385
    {
386
    int id=0;
387

    
388
    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet())
389
      {
390
      ScrambleStateBandaged3x3 value = entry.getValue();
391
      value.mID = id++;
392
      }
393

    
394
    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet())
395
      {
396
      ScrambleStateBandaged3x3 value = entry.getValue();
397

    
398
      for(int m=0; m<NUM_MOVES; m++)
399
        {
400
        long move = value.mMoves[m];
401

    
402
        if( move!=INVALID_MOVE )
403
          {
404
          ScrambleStateBandaged3x3 tmp = map.get(move);
405
          if( tmp!=null ) value.mMoves[m] = tmp.mID;
406
          else            android.util.Log.e("D", "ERROR in remapGraph");
407
          }
408
        }
409
      }
410
    }
411

    
412
///////////////////////////////////////////////////////////////////////////////////////////////////
413

    
414
  private static void computeDistance(Map<Long, ScrambleStateBandaged3x3> map, long id, int distance)
415
    {
416
    ScrambleStateBandaged3x3 state = findState(map,id);
417

    
418
    if( state==null )
419
      {
420
      android.util.Log.e("D", "error: "+id+" doesn't exist");
421
      return;
422
      }
423

    
424
    if( state.mDistance<0 )
425
      {
426
      state.mDistance = distance;
427

    
428
      for(int i=0; i<NUM_MOVES; i++)
429
        {
430
        long move = state.getMove(i);
431

    
432
        if( move!=INVALID_MOVE )
433
          {
434
          computeDistance(map,move,distance+1);
435
          }
436
        }
437
      }
438
    }
439

    
440
///////////////////////////////////////////////////////////////////////////////////////////////////
441

    
442
  private static void removeDisconnectedParts(Map<Long, ScrambleStateBandaged3x3> map)
443
    {
444
    Iterator<Map.Entry<Long, ScrambleStateBandaged3x3>> it = map.entrySet().iterator();
445

    
446
    while (it.hasNext())
447
      {
448
      Map.Entry<Long, ScrambleStateBandaged3x3> entry = it.next();
449
      ScrambleStateBandaged3x3 value = entry.getValue();
450
      if( value.mDistance<0 ) it.remove();
451
      }
452
    }
453

    
454
///////////////////////////////////////////////////////////////////////////////////////////////////
455

    
456
  private static ScrambleStateBandaged3x3 findState(Map<Long, ScrambleStateBandaged3x3> map, long id)
457
    {
458
    return map.get(id);
459
    }
460

    
461
///////////////////////////////////////////////////////////////////////////////////////////////////
462

    
463
  private static String formatRet(String str, int row, int angle, long id)
464
    {
465
    String ret = str.length()!=0 ? ",":"";
466

    
467
    ret += row;
468
    ret += angle<0 ? "," : ", ";
469
    ret += angle;
470

    
471
         if( id< 10 ) ret += (",  "+id);
472
    else if( id<100 ) ret += (", " +id);
473
    else              ret += (","  +id);
474

    
475
    return ret;
476
    }
477

    
478
///////////////////////////////////////////////////////////////////////////////////////////////////
479

    
480
  private static final int LENGTH = 28;
481

    
482
  private static String formatL(String input)
483
    {
484
    int len = input.length();
485
    String ret = input;
486
    for(int i=0 ;i<LENGTH-len; i++) ret += " ";
487
    return ret;
488
    }
489

    
490
///////////////////////////////////////////////////////////////////////////////////////////////////
491

    
492
  private static long[] createMoves(long id)
493
    {
494
    long[] ret = new long[NUM_MOVES];
495
    int index = 0;
496

    
497
    for(int axis=0; axis<3; axis++)
498
      for(int layer=0; layer<3; layer++)
499
        {
500
        long x1 = turn(id,axis,layer);
501

    
502
        if( x1!=INVALID_MOVE )
503
          {
504
          long x2 = turn(x1,axis,layer);
505
          long x3 = turn(x2,axis,layer);
506

    
507
          ret[index  ] = x1;
508
          ret[index+1] = x2;
509
          ret[index+2] = x3;
510
          }
511
        else
512
          {
513
          ret[index  ] = INVALID_MOVE;
514
          ret[index+1] = INVALID_MOVE;
515
          ret[index+2] = INVALID_MOVE;
516
          }
517

    
518
        index+=3;
519
        }
520

    
521
    return ret;
522
    }
523

    
524
///////////////////////////////////////////////////////////////////////////////////////////////////
525
// Definition of the id: it's an 'Andreas signature' of a bandaged cube.
526
// https://twistypuzzles.com/forum/viewtopic.php?t=24452&sid=f3b4ac0c611c4713e4d7840f1aabbb0b&start=350
527

    
528
  private static long turn(long id, int axis, int layer)
529
    {
530
    switch(axis)
531
      {
532
      case AXIS_X: switch(layer)
533
                     {
534
                     case LAYER_L: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
535
                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
536
                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1  ) return INVALID_MOVE;
537

    
538
                                   long x1 = cycle(id,9,14,46,41);
539
                                   long x2 = cycle(x1,4,35,51,20);
540
                                   return    cycle(x2,17,25,38,30);
541

    
542
                     case LAYER_M: if( getBit(id, 1)==1 || getBit(id, 6)==1 || getBit(id,11)==1 ||
543
                                       getBit(id,22)==1 || getBit(id,27)==1 || getBit(id,32)==1 ||
544
                                       getBit(id,43)==1 || getBit(id,48)==1 || getBit(id,53)==1 ||
545
                                       getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
546
                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
547
                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
548

    
549
                                   long x4 = cycle(id,8,13,45,40);
550
                                   long x5 = cycle(x4,3,34,50,19);
551
                                   return    cycle(x5,16,24,37,29);
552

    
553
                     case LAYER_R: if( getBit(id, 0)==1 || getBit(id, 5)==1 || getBit(id,10)==1 ||
554
                                       getBit(id,21)==1 || getBit(id,26)==1 || getBit(id,31)==1 ||
555
                                       getBit(id,42)==1 || getBit(id,47)==1 || getBit(id,52)==1  ) return INVALID_MOVE;
556

    
557
                                   long x7 = cycle(id,7,12,44,39);
558
                                   long x8 = cycle(x7,2,33,49,18);
559
                                   return    cycle(x8,15,23,36,28);
560
                     }
561

    
562
      case AXIS_Y: switch(layer)
563
                     {
564
                     case LAYER_L: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
565
                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
566
                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1  ) return INVALID_MOVE;
567

    
568
                                   long y1 = cycle(id,1,9,10,2);
569
                                   long y2 = cycle(y1,0,4,11,7);
570
                                   return    cycle(y2,3,6,8,5);
571

    
572
                     case LAYER_M: if( getBit(id,12)==1 || getBit(id,13)==1 || getBit(id,14)==1 ||
573
                                       getBit(id,15)==1 || getBit(id,16)==1 || getBit(id,17)==1 ||
574
                                       getBit(id,18)==1 || getBit(id,19)==1 || getBit(id,20)==1 ||
575
                                       getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
576
                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
577
                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
578

    
579
                                   long y4 = cycle(id,21,25,32,28);
580
                                   long y5 = cycle(y4,22,30,31,23);
581
                                   return    cycle(y5,24,27,29,26);
582

    
583
                     case LAYER_R: if( getBit(id,33)==1 || getBit(id,34)==1 || getBit(id,35)==1 ||
584
                                       getBit(id,36)==1 || getBit(id,37)==1 || getBit(id,38)==1 ||
585
                                       getBit(id,39)==1 || getBit(id,40)==1 || getBit(id,41)==1  ) return INVALID_MOVE;
586

    
587
                                   long y7 = cycle(id,42,46,53,49);
588
                                   long y8 = cycle(y7,43,51,52,44);
589
                                   return    cycle(y8,45,48,50,47);
590
                     }
591

    
592
      case AXIS_Z: switch(layer)
593
                     {
594
                     case LAYER_L: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
595
                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
596
                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1  ) return INVALID_MOVE;
597

    
598
                                   long z1 = cycle(id,10,20,53,39);
599
                                   long z2 = cycle(z1,11,41,52,18);
600
                                   return    cycle(z2,19,32,40,31);
601

    
602
                     case LAYER_M: if( getBit(id, 7)==1 || getBit(id, 8)==1 || getBit(id, 9)==1 ||
603
                                       getBit(id,28)==1 || getBit(id,29)==1 || getBit(id,30)==1 ||
604
                                       getBit(id,49)==1 || getBit(id,50)==1 || getBit(id,51)==1 ||
605
                                       getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
606
                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
607
                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
608

    
609
                                   long z4 = cycle(id,5,17,48,36);
610
                                   long z5 = cycle(z4,6,38,47,15);
611
                                   return    cycle(z5,16,27,37,26);
612

    
613
                     case LAYER_R: if( getBit(id, 2)==1 || getBit(id, 3)==1 || getBit(id, 4)==1 ||
614
                                       getBit(id,23)==1 || getBit(id,24)==1 || getBit(id,25)==1 ||
615
                                       getBit(id,44)==1 || getBit(id,45)==1 || getBit(id,46)==1  ) return INVALID_MOVE;
616

    
617
                                   long z7 = cycle(id,0,14,43,33);
618
                                   long z8 = cycle(z7,1,35,42,12);
619
                                   return    cycle(z8,13,22,34,21);
620
                     }
621
      }
622

    
623
    return 0;
624
    }
625

    
626
///////////////////////////////////////////////////////////////////////////////////////////////////
627
// bit b1 in place of b2 etc.
628

    
629
  private static long cycle(long id, int b1, int b2, int b3, int b4)
630
    {
631
    long bit1 = getBit(id,b1);
632
    long bit2 = getBit(id,b2);
633
    long bit3 = getBit(id,b3);
634
    long bit4 = getBit(id,b4);
635

    
636
    long i1 = setBit(id,b2,bit1);
637
    long i2 = setBit(i1,b3,bit2);
638
    long i3 = setBit(i2,b4,bit3);
639
    return    setBit(i3,b1,bit4);
640
    }
641

    
642
///////////////////////////////////////////////////////////////////////////////////////////////////
643

    
644
  private static long getBit(long id, int bit)
645
    {
646
    return (id>>bit)&0x1;
647
    }
648

    
649
///////////////////////////////////////////////////////////////////////////////////////////////////
650

    
651
  private static long setBit(long id, int bit, long value)
652
    {
653
    long old = getBit(id,bit);
654

    
655
    if( old!=value )
656
      {
657
      long diff = (1L<<bit);
658
      id += (value==0 ? -diff : diff);
659
      }
660

    
661
    return id;
662
    }
663

    
664
///////////////////////////////////////////////////////////////////////////////////////////////////
665

    
666
  private void printMoves()
667
    {
668
    String moves = "";
669

    
670
    for(int i=0; i<NUM_MOVES; i++)
671
      {
672
      moves += (" " + mMoves[i]);
673
      }
674

    
675
    android.util.Log.e("D", moves);
676
    }
677

    
678
///////////////////////////////////////////////////////////////////////////////////////////////////
679

    
680
  private static String printBits(long id)
681
    {
682
    String ret = "[";
683
    boolean first = true;
684

    
685
    for(int i=0; i<64; i++)
686
      {
687
      if( ( (id>>i)&0x1)==1 )
688
        {
689
        String num = (i<10 ? " "+i : ""+i);
690

    
691
        if( first ) { ret += num; first=false; }
692
        else          ret += (","+num);
693
        }
694
      }
695

    
696
    return ret + "]";
697
    }
698

    
699
///////////////////////////////////////////////////////////////////////////////////////////////////
700

    
701
  private static void printGraph(Map<Long, ScrambleStateBandaged3x3> map)
702
    {
703
    int num = map.size();
704
    android.util.Log.e("D", "\n"+num+" states\n");
705

    
706
    for (Map.Entry<Long, ScrambleStateBandaged3x3> entry : map.entrySet())
707
      {
708
      ScrambleStateBandaged3x3 value = entry.getValue();
709
      android.util.Log.e("D", value.formatMoves());
710
      }
711
    }
712
}
(4-4/6)