Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / tablebases / TablebasesAbstract.java @ 693cc52e

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[][] mCubitRows;
36
  private final int[][] mCubitQuatMap;
37
  private final float[][] mQuats;
38
  private final float[][] mPosition;
39

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

    
43
  Tablebase mTablebase;
44
  boolean mInitialized;
45

    
46
  final int mScalingFactor;
47
  final int mNumAxis;
48
  final int[][] mRotRow;
49
  final int mNumCubits;
50
  final boolean[][] mRotatable;
51

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

    
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55

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

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

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

    
68
///////////////////////////////////////////////////////////////////////////////////////////////////
69

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

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

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

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

    
96
    int scaling = 0;
97

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

    
105
    mScalingFactor = scaling;
106
    mRotRow = new int[mNumCubits][mNumAxis];
107

    
108
    mCubitRows = new int[mNumCubits][mNumAxis];
109
    mCubitQuatMap = new int[mNumCubits][mNumQuats];
110

    
111
    for(int i=0; i<mNumCubits; i++)
112
      {
113
      computeInitRow(i,mCubitRows[i]);
114
      for(int j=0; j<mNumQuats; j++) mCubitQuatMap[i][j] = computeMap(i,j);
115
      }
116

    
117
    mInitialized = false;
118
    }
119

    
120
///////////////////////////////////////////////////////////////////////////////////////////////////
121

    
122
  public TablebasesAbstract(OperatingSystemInterface os, int resource)
123
    {
124
    this();
125

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

    
130
    int nRead;
131
    byte[] tmp = new byte[16384];
132

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

    
150
///////////////////////////////////////////////////////////////////////////////////////////////////
151

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

    
157
    QuatHelper.rotateVectorByQuat(mTmp,pos[0],pos[1],pos[2],1.0f,q);
158
    float dist = Float.MAX_VALUE;
159
    int min = 0;
160

    
161
    for(int i=0; i<mNumCubits; i++)
162
      {
163
      float[] p = mPosition[i];
164
      float dx = p[0]-mTmp[0];
165
      float dy = p[1]-mTmp[1];
166
      float dz = p[2]-mTmp[2];
167

    
168
      float d = dx*dx + dy*dy + dz*dz;
169

    
170
      if( d<dist )
171
        {
172
        dist = d;
173
        min = i;
174
        }
175
      }
176

    
177
    return min;
178
    }
179

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

    
182
  int computeBitLayer(int ax, int layer)
183
    {
184
    return (1<<layer);
185
    }
186

    
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

    
189
  void computeInitRow(int cubit, int[] output)
190
    {
191
    float[] pos = mPosition[cubit];
192
    int num = output.length;
193

    
194
    for(int i=0; i<num; i++)
195
      {
196
      Static3D axis = mAxis[i];
197
      float axisX = axis.get0();
198
      float axisY = axis.get1();
199
      float axisZ = axis.get2();
200
      float casted = pos[0]*axisX + pos[1]*axisY + pos[2]*axisZ;
201
      output[i] = computeSingleRow(i,casted);
202
      }
203
    }
204

    
205
///////////////////////////////////////////////////////////////////////////////////////////////////
206

    
207
  private int computeSingleRow(int axisIndex,float casted)
208
    {
209
    int num = mNumCuts[axisIndex];
210

    
211
    for(int i=0; i<num; i++)
212
      {
213
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
214
      }
215

    
216
    return (1<<num);
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220

    
221
  int[] computeRow(int cubit, int quat)
222
    {
223
    int newPos = mCubitQuatMap[cubit][quat];
224
    return mCubitRows[newPos];
225
    }
226

    
227
///////////////////////////////////////////////////////////////////////////////////////////////////
228
// remember about the double cover or unit quaternions!
229

    
230
  public static int mulQuat(int q1, int q2, float[][] quats)
231
    {
232
    int numQuats = quats.length;
233
    float[] result = QuatHelper.quatMultiply(quats[q1],quats[q2]);
234

    
235
    float rX = result[0];
236
    float rY = result[1];
237
    float rZ = result[2];
238
    float rW = result[3];
239

    
240
    final float MAX_ERROR = 0.1f;
241
    float dX,dY,dZ,dW;
242

    
243
    for(int i=0; i<numQuats; i++)
244
      {
245
      float[] q = quats[i];
246
      dX = q[0] - rX;
247
      dY = q[1] - rY;
248
      dZ = q[2] - rZ;
249
      dW = q[3] - rW;
250

    
251
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
252
          dY<MAX_ERROR && dY>-MAX_ERROR &&
253
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
254
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
255

    
256
      dX = q[0] + rX;
257
      dY = q[1] + rY;
258
      dZ = q[2] + rZ;
259
      dW = q[3] + rW;
260

    
261
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
262
          dY<MAX_ERROR && dY>-MAX_ERROR &&
263
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
264
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
265
      }
266

    
267
    return -1;
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

    
272
  int getMultQuat(int index1, int index2)
273
    {
274
    if( mQuatMult==null )
275
      {
276
      mQuatMult = new int[mNumQuats][mNumQuats];
277

    
278
      for(int i=0; i<mNumQuats; i++)
279
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
280
      }
281

    
282
    if( index1<mNumQuats && index2<mNumQuats )
283
      {
284
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2, mQuats);
285
      return mQuatMult[index1][index2];
286
      }
287

    
288
    return -2;
289
    }
290

    
291
///////////////////////////////////////////////////////////////////////////////////////////////////
292

    
293
  int getInvertedQuat(int index)
294
    {
295
    if( mInvertedQuat==null )
296
      {
297
      mInvertedQuat = new int[mNumQuats];
298

    
299
      for(int i=0; i<mNumQuats; i++)
300
        for(int j=0; j<mNumQuats; j++)
301
          {
302
          int result = getMultQuat(i,j);
303

    
304
          if( result==0 )
305
            {
306
            mInvertedQuat[i] = j;
307
            break;
308
            }
309
          }
310
      }
311

    
312
    return mInvertedQuat[index];
313
    }
314

    
315
///////////////////////////////////////////////////////////////////////////////////////////////////
316
// assumption: all layers have the same basicAngles!
317

    
318
  private int insertAllChildren(int index, byte level)
319
    {
320
    int ret = 0;
321
    int[] quats = getQuats(index);
322
    int numQuats = quats.length;
323
    int[] tmpQuats = new int[numQuats];
324
    byte newLevel = (byte)(level+1);
325
    int quatBasis = 0;
326

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

    
329
    for(int ax=0; ax<mNumAxis; ax++)
330
      {
331
      for(int layer=0; layer<mNumLayers[ax]; layer++)
332
        {
333
        if( !mRotatable[ax][layer] ) continue;
334
        int bitLayer = computeBitLayer(ax,layer);
335
        int maxAngle = mAngles[ax][layer];
336

    
337
        for(int angle=1; angle<maxAngle; angle++ )
338
          {
339
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
340
          int quat = quatBasis + angle;
341

    
342
          for(int cubit=0; cubit<mNumCubits; cubit++)
343
            if( (mRotRow[cubit][ax] & bitLayer) != 0 )
344
              {
345
              int currQuat = tmpQuats[cubit];
346
              int newQuat = getMultQuat(quat,currQuat);
347
              tmpQuats[cubit] = newQuat;
348
              }
349

    
350
          int childIndex = getIndex(tmpQuats);
351
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
352
          }
353
        }
354

    
355
      quatBasis += (mAngles[ax][0]-1);
356
      }
357

    
358
    return ret;
359
    }
360

    
361
///////////////////////////////////////////////////////////////////////////////////////////////////
362

    
363
  public boolean useForScrambling()
364
    {
365
    return true;
366
    }
367

    
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369

    
370
  public void createTablebase(int maxLevel)
371
    {
372
    mTablebase = new Tablebase(mSize);
373
    int[] solved = getSolvedIndices();
374
    int numSolved = solved.length;
375
    for(int s : solved ) mTablebase.insertUnpacked(s,(byte)0);
376

    
377
    int numInserted, totalInserted=numSolved;
378
    byte insertingLevel = 0;
379

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

    
382
    do
383
      {
384
      numInserted = 0;
385

    
386
      for(int i=0; i<mSize; i++)
387
        {
388
        byte level = mTablebase.retrieveUnpacked(i);
389
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
390
        }
391

    
392
      insertingLevel++;
393
      totalInserted += numInserted;
394
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
395
      }
396
    while( numInserted>0 && insertingLevel!=maxLevel );
397

    
398
    android.util.Log.e("D", "total Inserted: "+totalInserted);
399
    }
400

    
401
///////////////////////////////////////////////////////////////////////////////////////////////////
402

    
403
  int[] getSolvedIndices()
404
    {
405
    return new int[] {0};
406
    }
407

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

    
410
  boolean isSolved(int index)
411
    {
412
    return index==0;
413
    }
414

    
415
///////////////////////////////////////////////////////////////////////////////////////////////////
416

    
417
  public void pack()
418
    {
419
    android.util.Log.e("D", "packing...");
420
    mTablebase.pack();
421
    android.util.Log.e("D", "all done");
422
    mInitialized = true;
423
    }
424

    
425
///////////////////////////////////////////////////////////////////////////////////////////////////
426

    
427
  public byte[][] getPacked()
428
    {
429
    if( !mInitialized )
430
      {
431
      createTablebase(-1);
432
      pack();
433
      }
434

    
435
    byte[] data = mTablebase.getPacked();
436
    return new byte[][] { data };
437
    }
438

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

    
441
  void convertMoves(int[][] moves)
442
    {
443
    for(int[] move : moves )
444
      {
445
      int[] m = newMove(move[0],move[1],move[2]);
446

    
447
      move[0] = m[0];
448
      move[1] = m[1];
449
      move[2] = m[2];
450
      }
451
    }
452

    
453
///////////////////////////////////////////////////////////////////////////////////////////////////
454

    
455
  int[] newMove(int ax, int layer, int angle)
456
    {
457
    int maxAngle = mAngles[ax][layer];
458
    angle = maxAngle-angle;
459
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
460

    
461
    return new int[] { ax, computeBitLayer(ax,layer), angle };
462
    }
463

    
464
///////////////////////////////////////////////////////////////////////////////////////////////////
465

    
466
  int[][] convertMovesFromArray(ArrayList<int[]> moves)
467
    {
468
    int len = moves.size();
469
    int[][] ret = new int[len][];
470
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
471
    return ret;
472
    }
473

    
474
///////////////////////////////////////////////////////////////////////////////////////////////////
475

    
476
  void getNextAxisLayerAngleQuat(int[] data)
477
    {
478
    int axis = data[0];
479
    int layer= data[1];
480
    int angle= data[2];
481

    
482
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
483
    else
484
      {
485
      data[2] = 1;
486

    
487
      if( layer< mNumLayers[axis]-1 ) data[1]++;
488
      else
489
        {
490
        data[1] = 0;
491
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
492
        }
493
      }
494

    
495
    data[3] = data[2];
496
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
497
    }
498

    
499
///////////////////////////////////////////////////////////////////////////////////////////////////
500

    
501
  int[][] extraInfo(int[][] moves, int[] extra)
502
    {
503
    return moves;
504
    }
505

    
506
///////////////////////////////////////////////////////////////////////////////////////////////////
507

    
508
  public int[][] solution(int index, int[] extra, OperatingSystemInterface osi)
509
    {
510
    if( !mInitialized ) return null;
511

    
512
    int[] data = new int[4];
513
    byte level = mTablebase.retrievePacked(index);
514
    ArrayList<int[]> moves = new ArrayList<>();
515
    int[] quats = getQuats(index);
516
    int numQuats = quats.length;
517
    int[] tmpQuats = new int[numQuats];
518

    
519
    while( !isSolved(index) )
520
      {
521
      boolean found = false;
522

    
523
      data[0]=0;
524
      data[1]=0;
525
      data[2]=1;
526
      data[3]=1;
527

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

    
530
      for(int s=0; s<mScalingFactor && !found; s++)
531
        {
532
        int ax    = data[0];
533
        int layer = data[1];
534
        int angle = data[2];
535
        int quat  = data[3];
536

    
537
        if( mRotatable[ax][layer] )
538
          {
539
          int bitLayer = computeBitLayer(ax,layer);
540
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
541

    
542
          for(int cubit=0; cubit<mNumCubits; cubit++)
543
            if( (mRotRow[cubit][ax] & bitLayer) != 0 )
544
              {
545
              int currQuat = tmpQuats[cubit];
546
              int newQuat = getMultQuat(quat,currQuat);
547
              tmpQuats[cubit] = newQuat;
548
              }
549

    
550
          int childIndex = getIndex(tmpQuats);
551
          byte newLevel = mTablebase.retrievePacked(childIndex);
552

    
553
          if( ((newLevel-level+1)%3) == 0 )
554
            {
555
            int[] tmpMove = newMove(ax,layer,angle);
556
            moves.add(tmpMove);
557
            index = childIndex;
558
            level = (level==0 ? 2 : (byte)(level-1));
559
            found = true;
560
            }
561
          }
562

    
563
        getNextAxisLayerAngleQuat(data);
564
        }
565

    
566
      quats = getQuats(index);
567

    
568
      if( !found )
569
        {
570
        if( osi!=null ) osi.reportError("solution error: no move found!"+index);
571
        return null;
572
        }
573
      }
574

    
575
    int[][] ret = convertMovesFromArray(moves);
576
    return extraInfo(ret,extra);
577
    }
578

    
579
///////////////////////////////////////////////////////////////////////////////////////////////////
580

    
581
  public void scramble(Random rnd, int depth, int[][] scramble)
582
    {
583
    if( !mInitialized ) return;
584

    
585
    int solDepth = 0;
586
    int scraLen = scramble.length;
587
    if( depth>mMinScramble ) depth = mMinScramble;
588

    
589
    int[] cornerTwist = new int[4];
590
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
591

    
592
    while( solDepth<depth )
593
      {
594
      int randomPosition = rnd.nextInt(mSize-1);
595
      int[][] sol = solution(randomPosition,cornerTwist,null);
596
      solDepth = (sol!=null ? sol.length : 0);
597

    
598
      if( solDepth>=depth )
599
        {
600
        int num = Math.min(scraLen,solDepth);
601

    
602
        for(int i=0; i<num; i++)
603
          {
604
          int[] source = sol[solDepth-1-i];
605
          scramble[i][0] = source[0];
606
          scramble[i][1] = source[1];
607
          scramble[i][2] =-source[2];
608
          }
609
        }
610
      }
611
    }
612

    
613
///////////////////////////////////////////////////////////////////////////////////////////////////
614
// TESTING
615
///////////////////////////////////////////////////////////////////////////////////////////////////
616

    
617
  public boolean test(int index)
618
    {
619
    if( !mInitialized ) return false;
620

    
621
    int[] data = new int[4];
622
    byte level = mTablebase.retrievePacked(index);
623
    int[] quats = getQuats(index);
624
    int numQuats = quats.length;
625
    int[] tmpQuats = new int[numQuats];
626

    
627
    data[0]=0;
628
    data[1]=0;
629
    data[2]=1;
630
    data[3]=1;
631

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

    
634
    for(int s=0; s<mScalingFactor; s++)
635
      {
636
      int ax    = data[0];
637
      int layer = data[1];
638
      int quat  = data[3];
639

    
640
      if( mRotatable[ax][layer] )
641
        {
642
        int bitLayer = computeBitLayer(ax,layer);
643
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
644

    
645
        for(int cubit=0; cubit<mNumCubits; cubit++)
646
          if( (mRotRow[cubit][ax] & bitLayer) != 0 )
647
            {
648
            int currQuat = tmpQuats[cubit];
649
            int newQuat = getMultQuat(quat,currQuat);
650
            tmpQuats[cubit] = newQuat;
651
            }
652

    
653
        int childIndex = getIndex(tmpQuats);
654
        byte newLevel = mTablebase.retrievePacked(childIndex);
655

    
656
        if( ((newLevel-level+1)%3) == 0 ) return true;
657
        }
658

    
659
      getNextAxisLayerAngleQuat(data);
660
      }
661

    
662
    return false;
663
    }
664

    
665
///////////////////////////////////////////////////////////////////////////////////////////////////
666

    
667
  public void test(OperatingSystemInterface os)
668
    {
669
    int index = 252373232;
670
    int[] q = getQuats(index);
671
    TablebaseHelpers.displayTable(q , "QUATS ");
672
    }
673
}
(18-18/19)