Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / tablebases / TablebasesAbstract.java @ 5b9f1cec

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

    
10
package org.distorted.objectlib.tablebases;
11

    
12
import android.content.res.Resources;
13

    
14
import org.distorted.library.main.QuatHelper;
15
import org.distorted.library.type.Static3D;
16
import org.distorted.library.type.Static4D;
17
import org.distorted.objectlib.helpers.QuatGroupGenerator;
18

    
19
import java.io.ByteArrayOutputStream;
20
import java.io.IOException;
21
import java.io.InputStream;
22
import java.util.ArrayList;
23
import java.util.Random;
24

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

    
27
public abstract class TablebasesAbstract
28
{
29
  private final Static3D[] mAxis;
30
  private final int mSize, mMinScramble;
31
  private final int[][] mAngles;
32
  private final int[] mNumLayers;
33
  private final int mNumQuats;
34
  private final Static4D[] mQuats;
35
  private final float[][] mCuts;
36
  private final int[] mNumCuts;
37

    
38
  private int[][] mQuatMult;
39
  private int[] mInvertedQuat;
40

    
41
  Tablebase mTablebase;
42
  boolean mInitialized;
43

    
44
  final int mScalingFactor;
45
  final int mNumAxis;
46
  final float[][] mPosition;
47
  final int[][] mRotRow;
48
  final int mNumCubits;
49
  final boolean[][] mRotatable;
50

    
51
  private static final float[] mTmp = new float[4];
52

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

    
55
  abstract int[][] getBasicAngles();
56
  abstract Static3D[] getRotationAxis();
57
  abstract float[][] getPosition();
58
  abstract float[][] getCuts();
59

    
60
  abstract boolean[][] getRotatable();
61

    
62
  abstract int getMinScramble();
63
  abstract int getSize();
64
  abstract int[] getQuats(int index);
65
  abstract int getIndex(int[] quats);
66

    
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

    
69
  public TablebasesAbstract()
70
    {
71
    mSize = getSize();
72
    mMinScramble = getMinScramble();
73
    mAngles = getBasicAngles();
74
    mAxis = getRotationAxis();
75
    mNumAxis = mAxis.length;
76
    mNumLayers = new int[mNumAxis];
77
    for(int i=0; i<mNumAxis; i++) mNumLayers[i] = mAngles[i].length;
78
    mQuats = QuatGroupGenerator.computeGroup(mAxis,mAngles);
79
    mNumQuats = mQuats.length;
80
    mPosition = getPosition();
81
    mNumCubits = mPosition.length;
82
    mRotatable = getRotatable();
83
    mCuts = getCuts();
84
    mNumCuts = new int[mNumAxis];
85

    
86
    int scaling = 0;
87

    
88
    for(int i=0; i<mNumAxis; i++)
89
      {
90
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
91
      int numLayers = mNumLayers[i];
92
      for(int j=0; j<numLayers; j++) scaling+=(mAngles[i][j]-1);
93
      }
94

    
95
    mScalingFactor = scaling;
96
    mRotRow = new int[mNumCubits][mNumAxis];
97
    mInitialized = false;
98
    }
99

    
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101

    
102
  public TablebasesAbstract(Resources res, int resource)
103
    {
104
    this();
105

    
106
    mInitialized = true;
107
    InputStream stream = res.openRawResource(resource);
108
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
109

    
110
    int nRead;
111
    byte[] tmp = new byte[16384];
112

    
113
    try
114
      {
115
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
116
        {
117
        buffer.write(tmp, 0, nRead);
118
        }
119
      stream.close();
120
      byte[] data = buffer.toByteArray();
121
      buffer.close();
122
      mTablebase = new Tablebase(data);
123
      }
124
    catch(IOException ex)
125
      {
126
      mInitialized = false;
127
      }
128
    }
129

    
130
///////////////////////////////////////////////////////////////////////////////////////////////////
131

    
132
  int computeRow(float[] pos, int quat, int axisIndex)
133
    {
134
    int ret=0;
135
    int len = pos.length/3;
136
    Static3D axis = mAxis[axisIndex];
137
    float axisX = axis.get0();
138
    float axisY = axis.get1();
139
    float axisZ = axis.get2();
140
    float casted, xoff=0, yoff=0, zoff=0;
141
    Static4D q = mQuats[quat];
142

    
143
    for(int i=0; i<len; i++)
144
      {
145
      QuatHelper.rotateVectorByQuat(mTmp,pos[3*i],pos[3*i+1],pos[3*i+2],1.0f,q);
146
      casted = (mTmp[0]+xoff)*axisX + (mTmp[1]+yoff)*axisY + (mTmp[2]+zoff)*axisZ;
147
      ret |= computeSingleRow(axisIndex,casted);
148
      }
149

    
150
    return ret;
151
    }
152

    
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154

    
155
  private int computeSingleRow(int axisIndex,float casted)
156
    {
157
    int num = mNumCuts[axisIndex];
158

    
159
    for(int i=0; i<num; i++)
160
      {
161
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
162
      }
163

    
164
    return (1<<num);
165
    }
166

    
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168
// remember about the double cover or unit quaternions!
169

    
170
  private int mulQuat(int q1, int q2)
171
    {
172
    Static4D result = QuatHelper.quatMultiply(mQuats[q1],mQuats[q2]);
173

    
174
    float rX = result.get0();
175
    float rY = result.get1();
176
    float rZ = result.get2();
177
    float rW = result.get3();
178

    
179
    final float MAX_ERROR = 0.1f;
180
    float dX,dY,dZ,dW;
181

    
182
    for(int i=0; i<mNumQuats; i++)
183
      {
184
      dX = mQuats[i].get0() - rX;
185
      dY = mQuats[i].get1() - rY;
186
      dZ = mQuats[i].get2() - rZ;
187
      dW = mQuats[i].get3() - rW;
188

    
189
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
190
          dY<MAX_ERROR && dY>-MAX_ERROR &&
191
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
192
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
193

    
194
      dX = mQuats[i].get0() + rX;
195
      dY = mQuats[i].get1() + rY;
196
      dZ = mQuats[i].get2() + rZ;
197
      dW = mQuats[i].get3() + rW;
198

    
199
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
200
          dY<MAX_ERROR && dY>-MAX_ERROR &&
201
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
202
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
203
      }
204

    
205
    return -1;
206
    }
207

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

    
210
  int getMultQuat(int index1, int index2)
211
    {
212
    if( mQuatMult==null )
213
      {
214
      mQuatMult = new int[mNumQuats][mNumQuats];
215

    
216
      for(int i=0; i<mNumQuats; i++)
217
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
218
      }
219

    
220
    if( index1<mNumQuats && index2<mNumQuats )
221
      {
222
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2);
223
      return mQuatMult[index1][index2];
224
      }
225

    
226
    return -2;
227
    }
228

    
229
///////////////////////////////////////////////////////////////////////////////////////////////////
230

    
231
  int getInvertedQuat(int index)
232
    {
233
    if( mInvertedQuat==null )
234
      {
235
      mInvertedQuat = new int[mNumQuats];
236

    
237
      for(int i=0; i<mNumQuats; i++)
238
        for(int j=0; j<mNumQuats; j++)
239
          {
240
          int result = getMultQuat(i,j);
241

    
242
          if( result==0 )
243
            {
244
            mInvertedQuat[i] = j;
245
            break;
246
            }
247
          }
248
      }
249

    
250
    return mInvertedQuat[index];
251
    }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254
// assumption: all layers have the same basicAngles!
255

    
256
  private int insertAllChildren(int index, byte level)
257
    {
258
    int ret = 0;
259
    int[] quats = getQuats(index);
260
    int numQuats = quats.length;
261
    int[] tmpQuats = new int[numQuats];
262
    byte newLevel = (byte)(level+1);
263
    int quatBasis = 0;
264

    
265
    for(int ax=0; ax<mNumAxis; ax++)
266
      for(int cubit=0; cubit<mNumCubits; cubit++)
267
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
268

    
269
    for(int ax=0; ax<mNumAxis; ax++)
270
      {
271
      for(int layer=0; layer<mNumLayers[ax]; layer++)
272
        {
273
        if( !mRotatable[ax][layer] ) continue;
274
        int bitLayer = (1<<layer);
275
        int maxAngle = mAngles[ax][layer];
276

    
277
        for(int angle=1; angle<maxAngle; angle++ )
278
          {
279
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
280
          int quat = quatBasis + angle;
281

    
282
          for(int cubit=0; cubit<mNumCubits; cubit++)
283
            if( mRotRow[cubit][ax]==bitLayer )
284
              {
285
              int currQuat = tmpQuats[cubit];
286
              int newQuat = getMultQuat(quat,currQuat);
287
              tmpQuats[cubit] = newQuat;
288
              }
289

    
290
          int childIndex = getIndex(tmpQuats);
291
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
292
          }
293
        }
294

    
295
      quatBasis += (mAngles[ax][0]-1);
296
      }
297

    
298
    return ret;
299
    }
300

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

    
303
  public void createTablebase(int maxLevel)
304
    {
305
    mTablebase = new Tablebase(mSize);
306
    int[] solved = getSolvedIndices();
307
    int numSolved = solved.length;
308
    for(int s : solved ) mTablebase.insertUnpacked(s,(byte)0);
309

    
310
    int numInserted, totalInserted=numSolved;
311
    byte insertingLevel = 0;
312

    
313
    android.util.Log.e("D", "creating tablebase of size "+mSize);
314

    
315
    do
316
      {
317
      numInserted = 0;
318

    
319
      for(int i=0; i<mSize; i++)
320
        {
321
        byte level = mTablebase.retrieveUnpacked(i);
322
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
323
        }
324

    
325
      insertingLevel++;
326
      totalInserted += numInserted;
327
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
328
      }
329
    while( numInserted>0 && insertingLevel!=maxLevel );
330

    
331
    android.util.Log.e("D", "total Inserted: "+totalInserted);
332
    }
333

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

    
336
  int[] getSolvedIndices()
337
    {
338
    return new int[] {0};
339
    }
340

    
341
///////////////////////////////////////////////////////////////////////////////////////////////////
342

    
343
  boolean isSolved(int index)
344
    {
345
    return index==0;
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349

    
350
  public void pack()
351
    {
352
    android.util.Log.e("D", "packing...");
353
    mTablebase.pack();
354
    android.util.Log.e("D", "all done");
355
    mInitialized = true;
356
    }
357

    
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359

    
360
  public byte[][] getPacked()
361
    {
362
    if( !mInitialized )
363
      {
364
      createTablebase(-1);
365
      pack();
366
      }
367

    
368
    byte[] data = mTablebase.getPacked();
369
    return new byte[][] { data };
370
    }
371

    
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373

    
374
  void convertMoves(int[][] moves)
375
    {
376
    for(int[] move : moves )
377
      {
378
      int[] newMove = newMove(move[0],move[1],move[2]);
379

    
380
      move[0] = newMove[0];
381
      move[1] = newMove[1];
382
      move[2] = newMove[2];
383
      }
384
    }
385

    
386
///////////////////////////////////////////////////////////////////////////////////////////////////
387

    
388
  int[] newMove(int axis, int layer, int angle)
389
    {
390
    int maxAngle = mAngles[axis][layer];
391
    angle = maxAngle-angle;
392
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
393

    
394
    return new int[] { axis, (1<<layer), angle };
395
    }
396

    
397
///////////////////////////////////////////////////////////////////////////////////////////////////
398

    
399
  private int[][] convertMoves(ArrayList<int[]> moves)
400
    {
401
    int len = moves.size();
402
    int[][] ret = new int[len][];
403
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
404
    return ret;
405
    }
406

    
407
///////////////////////////////////////////////////////////////////////////////////////////////////
408

    
409
  void getNextAxisLayerAngleQuat(int[] data)
410
    {
411
    int axis = data[0];
412
    int layer= data[1];
413
    int angle= data[2];
414

    
415
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
416
    else
417
      {
418
      data[2] = 1;
419

    
420
      if( layer< mNumLayers[axis]-1 ) data[1]++;
421
      else
422
        {
423
        data[1] = 0;
424
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
425
        }
426
      }
427

    
428
    data[3] = data[2];
429
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
430
    }
431

    
432
///////////////////////////////////////////////////////////////////////////////////////////////////
433

    
434
  int[][] extraInfo(int[][] moves, int[] extra)
435
    {
436
    return moves;
437
    }
438

    
439
///////////////////////////////////////////////////////////////////////////////////////////////////
440

    
441
  public int[][] solution(int index, int[] extra)
442
    {
443
    if( !mInitialized ) return null;
444

    
445
    int[] data = new int[4];
446
    byte level = mTablebase.retrievePacked(index);
447
    ArrayList<int[]> moves = new ArrayList<>();
448
    int[] quats = getQuats(index);
449
    int numQuats = quats.length;
450
    int[] tmpQuats = new int[numQuats];
451

    
452
    while( !isSolved(index) )
453
      {
454
      boolean found = false;
455

    
456
      data[0]=0;
457
      data[1]=0;
458
      data[2]=1;
459
      data[3]=1;
460

    
461
      for(int ax=0; ax<mNumAxis; ax++)
462
        for(int cubit=0; cubit<mNumCubits; cubit++)
463
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
464

    
465
      for(int s=0; s<mScalingFactor && !found; s++)
466
        {
467
        int ax    = data[0];
468
        int layer = data[1];
469
        int angle = data[2];
470
        int quat  = data[3];
471

    
472
        if( mRotatable[ax][layer] )
473
          {
474
          int bitLayer = (1<<layer);
475
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
476

    
477
          for(int cubit=0; cubit<mNumCubits; cubit++)
478
            if( mRotRow[cubit][ax]==bitLayer )
479
              {
480
              int currQuat = tmpQuats[cubit];
481
              int newQuat = getMultQuat(quat,currQuat);
482
              tmpQuats[cubit] = newQuat;
483
              }
484

    
485
          int childIndex = getIndex(tmpQuats);
486
          byte newLevel = mTablebase.retrievePacked(childIndex);
487

    
488
          if( ((newLevel-level+1)%3) == 0 )
489
            {
490
            int[] tmpMove = newMove(ax,layer,angle);
491
            moves.add(tmpMove);
492
            index = childIndex;
493
            level = (level==0 ? 2 : (byte)(level-1));
494
            found = true;
495
            }
496
          }
497

    
498
        getNextAxisLayerAngleQuat(data);
499
        }
500

    
501
      quats = getQuats(index);
502

    
503
      if( !found )
504
        {
505
        android.util.Log.e("D", "----> solution error: no move found!");
506
        return null;
507
        }
508
      }
509

    
510
    int[][] ret = convertMoves(moves);
511
    return extraInfo(ret,extra);
512
    }
513

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

    
516
  public void scramble(Random rnd, int depth, int[][] scramble)
517
    {
518
    if( !mInitialized ) return;
519

    
520
    int solDepth = 0;
521
    int scraLen = scramble.length;
522
    if( depth>mMinScramble ) depth = mMinScramble;
523

    
524
    int[] cornerTwist = new int[4];
525
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
526

    
527
    while( solDepth<depth )
528
      {
529
      int randomPosition = rnd.nextInt(mSize-1);
530
      int[][] sol = solution(randomPosition,cornerTwist);
531
      solDepth = sol.length;
532

    
533
      if( solDepth>=depth )
534
        {
535
        int num = Math.min(scraLen,solDepth);
536

    
537
        for(int i=0; i<num; i++)
538
          {
539
          int[] source = sol[solDepth-1-i];
540
          scramble[i][0] = source[0];
541
          scramble[i][1] = source[1];
542
          scramble[i][2] =-source[2];
543
          }
544
        }
545
      }
546
    }
547

    
548
///////////////////////////////////////////////////////////////////////////////////////////////////
549
// TESTING
550
///////////////////////////////////////////////////////////////////////////////////////////////////
551

    
552
  public boolean test(int index)
553
    {
554
    if( !mInitialized ) return false;
555

    
556
    int[] data = new int[4];
557
    byte level = mTablebase.retrievePacked(index);
558
    int[] quats = getQuats(index);
559
    int numQuats = quats.length;
560
    int[] tmpQuats = new int[numQuats];
561

    
562
    data[0]=0;
563
    data[1]=0;
564
    data[2]=1;
565
    data[3]=1;
566

    
567
    for(int ax=0; ax<mNumAxis; ax++)
568
      for(int cubit=0; cubit<mNumCubits; cubit++)
569
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
570

    
571
    for(int s=0; s<mScalingFactor; s++)
572
      {
573
      int ax    = data[0];
574
      int layer = data[1];
575
      int quat  = data[3];
576

    
577
      if( mRotatable[ax][layer] )
578
        {
579
        int bitLayer = (1<<layer);
580
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
581

    
582
        for(int cubit=0; cubit<mNumCubits; cubit++)
583
          if( mRotRow[cubit][ax]==bitLayer )
584
            {
585
            int currQuat = tmpQuats[cubit];
586
            int newQuat = getMultQuat(quat,currQuat);
587
            tmpQuats[cubit] = newQuat;
588
            }
589

    
590
        int childIndex = getIndex(tmpQuats);
591
        byte newLevel = mTablebase.retrievePacked(childIndex);
592

    
593
        if( ((newLevel-level+1)%3) == 0 ) return true;
594
        }
595

    
596
      getNextAxisLayerAngleQuat(data);
597
      }
598

    
599
    return false;
600
    }
601

    
602
///////////////////////////////////////////////////////////////////////////////////////////////////
603

    
604
  public void testPruning(int level)
605
    {
606
    for( int supp : PruningTable.SUPPORTED )
607
      {
608
      PruningTable table = new PruningTable(mTablebase, level, supp);
609
      int size = mTablebase.getSize();
610

    
611
      StringBuilder sb = new StringBuilder();
612
      int num = 0;
613

    
614
      for(int i=0; i<size; i++)
615
        {
616
        if (table.contains(i))
617
          {
618
          if ((num % 10) == 0) sb.append("\n");
619
          num++;
620
          sb.append(i);
621
          sb.append(' ');
622
          }
623
        }
624

    
625
      android.util.Log.e("D", "numbers: " + sb);
626
      }
627
    }
628

    
629
///////////////////////////////////////////////////////////////////////////////////////////////////
630

    
631
  private void printQuats(int[] quats)
632
    {
633
    StringBuilder sb = new StringBuilder();
634
    for(int q : quats )
635
      {
636
      sb.append(' ');
637
      sb.append(q);
638
      }
639
    android.util.Log.e("D", "quats: "+sb);
640
    }
641

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

    
644
  public void test()
645
    {
646
    int[] p0 = {0,5,6,10, 2,3,11,9, 1,4,7,8};
647
    int[] q0 = TBDino6.getQuatsFromPerm(p0);
648
    int index0 = getIndex(q0);
649
    android.util.Log.e("D", "index0: "+index0);
650

    
651
    int[] p1 = {0,3,11,6,  7,8,10,2,  1,4,9,5};
652
    int[] q1 = TBDino6.getQuatsFromPerm(p1);
653
    int index1 = getIndex(q1);
654
    android.util.Log.e("D", "index1: "+index1);
655

    
656
    int[] p2 = {0,3,2,1,  7,6,5,4,  8,11,10,9};
657
    int[] q2 = TBDino6.getQuatsFromPerm(p2);
658
    int index2 = getIndex(q2);
659
    android.util.Log.e("D", "index2: "+index2);
660
    }
661

    
662
///////////////////////////////////////////////////////////////////////////////////////////////////
663

    
664
  private void printPerm(int index)
665
    {
666
    int[] perm11 = new int[11];
667
    TablebaseHelpers.getEvenPermutationFromNum(perm11,index);
668
    int[] perm = new int[12];
669
    for(int i=1; i<12; i++) perm[i] = perm11[i-1]+1;
670

    
671
    StringBuilder sb = new StringBuilder();
672
    sb.append(index);
673
    sb.append(" : ");
674

    
675
    for(int i=0; i<12; i++)
676
      {
677
      sb.append(' ');
678
      sb.append(perm[i]);
679
      }
680
    android.util.Log.e("D", "PERM: "+sb);
681
    }
682

    
683
///////////////////////////////////////////////////////////////////////////////////////////////////
684

    
685
  private void printQuat(int index)
686
    {
687
    int[] perm11 = new int[11];
688
    TablebaseHelpers.getEvenPermutationFromNum(perm11,index);
689
    int[] perm = new int[12];
690
    for(int i=1; i<12; i++) perm[i] = perm11[i-1]+1;
691
    int[] quats = TBDino6.getQuatsFromPerm(perm);
692

    
693
    StringBuilder sb = new StringBuilder();
694
    sb.append(index);
695
    sb.append(" : ");
696

    
697
    for(int i=0; i<12; i++)
698
      {
699
      sb.append(' ');
700
      sb.append(quats[i]);
701
      }
702
    android.util.Log.e("D", "QUATS: "+sb);
703
    }
704
}
(14-14/15)