Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / tablebases / TablebasesAbstract.java @ 6777e712

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 org.distorted.library.helpers.QuatHelper;
13
import org.distorted.library.type.Static3D;
14
import org.distorted.library.type.Static4D;
15
import org.distorted.objectlib.helpers.OperatingSystemInterface;
16
import org.distorted.objectlib.helpers.QuatGroupGenerator;
17

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

    
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25

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

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

    
42
  private ArrayList<int[]> mMoves;
43

    
44
  Tablebase mTablebase;
45
  boolean mInitialized;
46
  int mLastSolvingIndex;
47

    
48
  final int mScalingFactor;
49
  final int mNumAxis;
50
  final int[][] mRotRow;
51
  final int mNumCubits;
52
  final boolean[][] mRotatable;
53

    
54
  private static final float[] mTmp = new float[4];
55

    
56
///////////////////////////////////////////////////////////////////////////////////////////////////
57

    
58
  abstract int[][] getBasicAngles();
59
  abstract Static3D[] getRotationAxis();
60
  abstract float[][] getPosition();
61
  abstract float[][] getCuts();
62

    
63
  abstract boolean[][] getRotatable();
64

    
65
  abstract int getMinScramble();
66
  abstract int getSize();
67
  abstract int[] getQuats(int index);
68
  abstract int getIndex(int[] quats);
69

    
70
///////////////////////////////////////////////////////////////////////////////////////////////////
71

    
72
  public TablebasesAbstract()
73
    {
74
    mSize = getSize();
75
    mMinScramble = getMinScramble();
76
    mAngles = getBasicAngles();
77
    mAxis = getRotationAxis();
78
    mNumAxis = mAxis.length;
79
    mNumLayers = new int[mNumAxis];
80
    for(int i=0; i<mNumAxis; i++) mNumLayers[i] = mAngles[i].length;
81

    
82
    Static4D[] quats = QuatGroupGenerator.computeGroup(mAxis,mAngles);
83
    mNumQuats = quats.length;
84
    mQuats = new float[mNumQuats][];
85

    
86
    for(int i=0; i<mNumQuats; i++)
87
      {
88
      Static4D q = quats[i];
89
      mQuats[i] = new float[] {q.get0(),q.get1(),q.get2(),q.get3()};
90
      }
91

    
92
    mPosition = getPosition();
93
    mNumCubits = mPosition.length;
94
    mRotatable = getRotatable();
95
    mCuts = getCuts();
96
    mNumCuts = new int[mNumAxis];
97

    
98
    int scaling = 0;
99

    
100
    for(int i=0; i<mNumAxis; i++)
101
      {
102
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
103
      int numLayers = mNumLayers[i];
104
      for(int j=0; j<numLayers; j++) scaling+=(mAngles[i][j]-1);
105
      }
106

    
107
    mScalingFactor = scaling;
108
    mRotRow = new int[mNumCubits][mNumAxis];
109

    
110
    mCubitQuatRows = new int[mNumCubits][mNumQuats][];
111

    
112
    for(int i=0; i<mNumCubits; i++)
113
      for(int j=0; j<mNumQuats; j++) mCubitQuatRows[i][j] = computeMap(i,j);
114

    
115
    mInitialized = false;
116
    }
117

    
118
///////////////////////////////////////////////////////////////////////////////////////////////////
119

    
120
  public TablebasesAbstract(OperatingSystemInterface os, int resource)
121
    {
122
    this();
123

    
124
    mInitialized = true;
125
    InputStream stream = os.openLocalFile(resource);
126
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
127

    
128
    int nRead;
129
    byte[] tmp = new byte[16384];
130

    
131
    try
132
      {
133
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
134
        {
135
        buffer.write(tmp, 0, nRead);
136
        }
137
      stream.close();
138
      byte[] data = buffer.toByteArray();
139
      buffer.close();
140
      mTablebase = new Tablebase(data);
141
      }
142
    catch(IOException ex)
143
      {
144
      mInitialized = false;
145
      }
146
    }
147

    
148
///////////////////////////////////////////////////////////////////////////////////////////////////
149

    
150
  private int[] computeMap(int cubit, int quat)
151
    {
152
    float[] pos = mPosition[cubit];
153
    float[] q = mQuats[quat];
154

    
155
    QuatHelper.rotateVectorByQuat(mTmp,pos[0],pos[1],pos[2],1.0f,q);
156

    
157
    int[] output = new int[mNumAxis];
158

    
159
    for(int i=0; i<mNumAxis; i++)
160
      {
161
      Static3D ax = mAxis[i];
162
      float axisX = ax.get0();
163
      float axisY = ax.get1();
164
      float axisZ = ax.get2();
165
      float casted = mTmp[0]*axisX + mTmp[1]*axisY + mTmp[2]*axisZ;
166
      output[i] = computeSingleRow(i,casted);
167
      }
168

    
169
    return output;
170
    }
171

    
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173

    
174
  private int computeSingleRow(int axisIndex,float casted)
175
    {
176
    int num = mNumCuts[axisIndex];
177

    
178
    for(int i=0; i<num; i++)
179
      {
180
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
181
      }
182

    
183
    return (1<<num);
184
    }
185

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

    
188
  int computeBitLayer(int ax, int layer)
189
    {
190
    return (1<<layer);
191
    }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194

    
195
  int[] computeRow(int cubit, int quat)
196
    {
197
    return mCubitQuatRows[cubit][quat];
198
    }
199

    
200
///////////////////////////////////////////////////////////////////////////////////////////////////
201
// remember about the double cover or unit quaternions!
202

    
203
  public static int mulQuat(int q1, int q2, float[][] quats)
204
    {
205
    int numQuats = quats.length;
206
    float[] result = QuatHelper.quatMultiply(quats[q1],quats[q2]);
207

    
208
    float rX = result[0];
209
    float rY = result[1];
210
    float rZ = result[2];
211
    float rW = result[3];
212

    
213
    final float MAX_ERROR = 0.1f;
214
    float dX,dY,dZ,dW;
215

    
216
    for(int i=0; i<numQuats; i++)
217
      {
218
      float[] q = quats[i];
219
      dX = q[0] - rX;
220
      dY = q[1] - rY;
221
      dZ = q[2] - rZ;
222
      dW = q[3] - rW;
223

    
224
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
225
          dY<MAX_ERROR && dY>-MAX_ERROR &&
226
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
227
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
228

    
229
      dX = q[0] + rX;
230
      dY = q[1] + rY;
231
      dZ = q[2] + rZ;
232
      dW = q[3] + rW;
233

    
234
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
235
          dY<MAX_ERROR && dY>-MAX_ERROR &&
236
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
237
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
238
      }
239

    
240
    return -1;
241
    }
242

    
243
///////////////////////////////////////////////////////////////////////////////////////////////////
244

    
245
  int getMultQuat(int index1, int index2)
246
    {
247
    if( mQuatMult==null )
248
      {
249
      mQuatMult = new int[mNumQuats][mNumQuats];
250

    
251
      for(int i=0; i<mNumQuats; i++)
252
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
253
      }
254

    
255
    if( index1<mNumQuats && index2<mNumQuats )
256
      {
257
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2, mQuats);
258
      return mQuatMult[index1][index2];
259
      }
260

    
261
    return -2;
262
    }
263

    
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265

    
266
  int getInvertedQuat(int index)
267
    {
268
    if( mInvertedQuat==null )
269
      {
270
      mInvertedQuat = new int[mNumQuats];
271

    
272
      for(int i=0; i<mNumQuats; i++)
273
        for(int j=0; j<mNumQuats; j++)
274
          {
275
          int result = getMultQuat(i,j);
276

    
277
          if( result==0 )
278
            {
279
            mInvertedQuat[i] = j;
280
            break;
281
            }
282
          }
283
      }
284

    
285
    return mInvertedQuat[index];
286
    }
287

    
288
///////////////////////////////////////////////////////////////////////////////////////////////////
289
// assumption: all layers have the same basicAngles!
290

    
291
  private int insertAllChildren(int index, byte level)
292
    {
293
    int ret = 0;
294
    int[] quats = getQuats(index);
295
    int numQuats = quats.length;
296
    int[] tmpQuats = new int[numQuats];
297
    byte newLevel = (byte)(level+1);
298
    int quatBasis = 0;
299

    
300
    for(int cubit=0; cubit<mNumCubits; cubit++) mRotRow[cubit] = computeRow(cubit,quats[cubit]);
301

    
302
    for(int ax=0; ax<mNumAxis; ax++)
303
      {
304
      for(int layer=0; layer<mNumLayers[ax]; layer++)
305
        {
306
        if( !mRotatable[ax][layer] ) continue;
307
        int bitLayer = computeBitLayer(ax,layer);
308
        int maxAngle = mAngles[ax][layer];
309

    
310
        for(int angle=1; angle<maxAngle; angle++ )
311
          {
312
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
313
          int quat = quatBasis + angle;
314

    
315
          for(int cubit=0; cubit<mNumCubits; cubit++)
316
            if( (mRotRow[cubit][ax] & bitLayer) != 0 )
317
              tmpQuats[cubit] = getMultQuat(quat,tmpQuats[cubit]);
318

    
319
          int childIndex = getIndex(tmpQuats);
320
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
321
          }
322
        }
323

    
324
      quatBasis += (mAngles[ax][0]-1);
325
      }
326

    
327
    return ret;
328
    }
329

    
330
///////////////////////////////////////////////////////////////////////////////////////////////////
331

    
332
  public boolean useForScrambling()
333
    {
334
    return true;
335
    }
336

    
337
///////////////////////////////////////////////////////////////////////////////////////////////////
338

    
339
  public void createTablebase(int maxLevel)
340
    {
341
    mTablebase = new Tablebase(mSize);
342
    int[] solved = getSolvedIndices();
343
    int numSolved = solved.length;
344
    for(int s : solved ) mTablebase.insertUnpacked(s,(byte)0);
345

    
346
    int numInserted, totalInserted=numSolved;
347
    byte insertingLevel = 0;
348

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

    
351
    do
352
      {
353
      numInserted = 0;
354

    
355
      for(int i=0; i<mSize; i++)
356
        {
357
        byte level = mTablebase.retrieveUnpacked(i);
358
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
359
        }
360

    
361
      insertingLevel++;
362
      totalInserted += numInserted;
363
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
364
      }
365
    while( numInserted>0 && insertingLevel!=maxLevel );
366

    
367
    android.util.Log.e("D", "total Inserted: "+totalInserted);
368
    }
369

    
370
///////////////////////////////////////////////////////////////////////////////////////////////////
371

    
372
  int[] getSolvedIndices()
373
    {
374
    return new int[] {0};
375
    }
376

    
377
///////////////////////////////////////////////////////////////////////////////////////////////////
378

    
379
  boolean isSolved(int index)
380
    {
381
    return index==0;
382
    }
383

    
384
///////////////////////////////////////////////////////////////////////////////////////////////////
385

    
386
  public void pack()
387
    {
388
    android.util.Log.e("D", "packing...");
389
    mTablebase.pack();
390
    android.util.Log.e("D", "all done");
391
    mInitialized = true;
392
    }
393

    
394
///////////////////////////////////////////////////////////////////////////////////////////////////
395

    
396
  public byte[][] getPacked()
397
    {
398
    if( !mInitialized )
399
      {
400
      createTablebase(-1);
401
      pack();
402
      }
403

    
404
    byte[] data = mTablebase.getPacked();
405
    return new byte[][] { data };
406
    }
407

    
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409

    
410
  void convertMoves(int[][] moves)
411
    {
412
    for(int[] move : moves )
413
      {
414
      int[] m = newMove(move[0],move[1],move[2]);
415

    
416
      move[0] = m[0];
417
      move[1] = m[1];
418
      move[2] = m[2];
419
      }
420
    }
421

    
422
///////////////////////////////////////////////////////////////////////////////////////////////////
423

    
424
  int[] newMove(int ax, int layer, int angle)
425
    {
426
    int maxAngle = mAngles[ax][layer];
427
    angle = maxAngle-angle;
428
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
429

    
430
    return new int[] { ax, computeBitLayer(ax,layer), angle };
431
    }
432

    
433
///////////////////////////////////////////////////////////////////////////////////////////////////
434

    
435
  int[][] convertMovesFromArray(ArrayList<int[]> moves)
436
    {
437
    int len = moves.size();
438
    int[][] ret = new int[len][];
439
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
440
    return ret;
441
    }
442

    
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444

    
445
  void getNextAxisLayerAngleQuat(int[] data)
446
    {
447
    int axis = data[0];
448
    int layer= data[1];
449
    int angle= data[2];
450

    
451
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
452
    else
453
      {
454
      data[2] = 1;
455

    
456
      if( layer< mNumLayers[axis]-1 ) data[1]++;
457
      else
458
        {
459
        data[1] = 0;
460
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
461
        }
462
      }
463

    
464
    data[3] = data[2];
465
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
466
    }
467

    
468
///////////////////////////////////////////////////////////////////////////////////////////////////
469

    
470
  int[][] extraInfo(int[][] moves, int[] extra)
471
    {
472
    return moves;
473
    }
474

    
475
///////////////////////////////////////////////////////////////////////////////////////////////////
476

    
477
  public int[][] solution(int index, int[] extra, OperatingSystemInterface osi)
478
    {
479
    if( !mInitialized ) return null;
480

    
481
    if( mMoves==null ) mMoves = new ArrayList<>();
482
    else               mMoves.clear();
483

    
484
    int[] data = new int[4];
485
    byte level = mTablebase.retrievePacked(index);
486
    int[] quats = getQuats(index);
487
    int numQuats = quats.length;
488
    int[] tmpQuats = new int[numQuats];
489
    mLastSolvingIndex = index;
490

    
491
    while( !isSolved(index) )
492
      {
493
      boolean found = false;
494

    
495
      data[0]=0;
496
      data[1]=0;
497
      data[2]=1;
498
      data[3]=1;
499

    
500
      for(int cubit=0; cubit<mNumCubits; cubit++) mRotRow[cubit] = computeRow(cubit,quats[cubit]);
501

    
502
      for(int s=0; s<mScalingFactor && !found; s++)
503
        {
504
        int ax    = data[0];
505
        int layer = data[1];
506
        int angle = data[2];
507
        int quat  = data[3];
508

    
509
        if( mRotatable[ax][layer] )
510
          {
511
          int bitLayer = computeBitLayer(ax,layer);
512
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
513

    
514
          for(int cubit=0; cubit<mNumCubits; cubit++)
515
            if( (mRotRow[cubit][ax] & bitLayer) != 0 )
516
              {
517
              int currQuat = tmpQuats[cubit];
518
              int newQuat = getMultQuat(quat,currQuat);
519
              tmpQuats[cubit] = newQuat;
520
              }
521

    
522
          int childIndex = getIndex(tmpQuats);
523
          byte newLevel = mTablebase.retrievePacked(childIndex);
524

    
525
          if( ((newLevel-level+1)%3) == 0 )
526
            {
527
            int[] tmpMove = newMove(ax,layer,angle);
528
            mMoves.add(tmpMove);
529
            index = childIndex;
530
            level = (level==0 ? 2 : (byte)(level-1));
531
            found = true;
532
            }
533
          }
534

    
535
        getNextAxisLayerAngleQuat(data);
536
        }
537

    
538
      quats = getQuats(index);
539

    
540
      if( !found )
541
        {
542
        if( osi!=null ) osi.reportError("solution error: no move found!"+index);
543
        return null;
544
        }
545
      }
546

    
547
    int[][] ret = convertMovesFromArray(mMoves);
548
    return extraInfo(ret,extra);
549
    }
550

    
551
///////////////////////////////////////////////////////////////////////////////////////////////////
552

    
553
  void postScramble(int[][] scramble, int num)
554
    {
555

    
556
    }
557

    
558
///////////////////////////////////////////////////////////////////////////////////////////////////
559

    
560
  public void scramble(Random rnd, int depth, int[][] scramble)
561
    {
562
    if( !mInitialized ) return;
563

    
564
    int solDepth = 0;
565
    int scraLen = scramble.length;
566
    if( depth>mMinScramble ) depth = mMinScramble;
567

    
568
    int num=0;
569
    int[] cornerTwist = new int[4];
570
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
571

    
572
    while( solDepth<depth )
573
      {
574
      int randomPosition = rnd.nextInt(mSize-1);
575
      int[][] sol = solution(randomPosition,cornerTwist,null);
576
      solDepth = (sol!=null ? sol.length : 0);
577

    
578
      if( solDepth>=depth )
579
        {
580
        num = Math.min(scraLen,solDepth);
581

    
582
        for(int i=0; i<num; i++)
583
          {
584
          int[] source = sol[solDepth-1-i];
585
          scramble[i][0] = source[0];
586
          scramble[i][1] = source[1];
587
          scramble[i][2] =-source[2];
588
          }
589
        }
590
      }
591

    
592
    postScramble(scramble,num);
593
    }
594

    
595
///////////////////////////////////////////////////////////////////////////////////////////////////
596
// TESTING
597
///////////////////////////////////////////////////////////////////////////////////////////////////
598

    
599
  public boolean test(int index)
600
    {
601
    if( !mInitialized ) return false;
602

    
603
    int[] data = new int[4];
604
    byte level = mTablebase.retrievePacked(index);
605
    int[] quats = getQuats(index);
606
    int numQuats = quats.length;
607
    int[] tmpQuats = new int[numQuats];
608

    
609
    data[0]=0;
610
    data[1]=0;
611
    data[2]=1;
612
    data[3]=1;
613

    
614
    for(int cubit=0; cubit<mNumCubits; cubit++) mRotRow[cubit] = computeRow(cubit,quats[cubit]);
615

    
616
    for(int s=0; s<mScalingFactor; s++)
617
      {
618
      int ax    = data[0];
619
      int layer = data[1];
620
      int quat  = data[3];
621

    
622
      if( mRotatable[ax][layer] )
623
        {
624
        int bitLayer = computeBitLayer(ax,layer);
625
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
626

    
627
        for(int cubit=0; cubit<mNumCubits; cubit++)
628
          if( (mRotRow[cubit][ax] & bitLayer) != 0 )
629
            {
630
            int currQuat = tmpQuats[cubit];
631
            int newQuat = getMultQuat(quat,currQuat);
632
            tmpQuats[cubit] = newQuat;
633
            }
634

    
635
        int childIndex = getIndex(tmpQuats);
636
        byte newLevel = mTablebase.retrievePacked(childIndex);
637

    
638
        if( ((newLevel-level+1)%3) == 0 ) return true;
639
        }
640

    
641
      getNextAxisLayerAngleQuat(data);
642
      }
643

    
644
    return false;
645
    }
646

    
647
///////////////////////////////////////////////////////////////////////////////////////////////////
648

    
649
  public void test(OperatingSystemInterface os)
650
    {
651
    /*
652
    int index = 252373232;
653
    int[] q = getQuats(index);
654
    TablebaseHelpers.displayTable(q , "QUATS ");
655
    */
656

    
657
    int[] extra = new int[4];
658

    
659
    for(int i=10113000; i<19958400; i++)
660
      {
661
      int[][] sol = solution(i,extra,os);
662
      if( (i%1000)==0 ) android.util.Log.e("D ", "solving: "+i);
663
      }
664
    }
665
}
(18-18/19)