Project

General

Profile

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

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

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.main.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 Static4D[] mQuats;
34
  private final float[][] mCuts;
35
  private final int[] mNumCuts;
36

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

    
40
  Tablebase mTablebase;
41
  boolean mInitialized;
42

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

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

    
52
///////////////////////////////////////////////////////////////////////////////////////////////////
53

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

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

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

    
66
///////////////////////////////////////////////////////////////////////////////////////////////////
67

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

    
85
    int scaling = 0;
86

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

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

    
99
///////////////////////////////////////////////////////////////////////////////////////////////////
100

    
101
  public TablebasesAbstract(OperatingSystemInterface os, int resource)
102
    {
103
    this();
104

    
105
    mInitialized = true;
106
    InputStream stream = os.openLocalFile(resource);
107
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
108

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

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

    
129
///////////////////////////////////////////////////////////////////////////////////////////////////
130

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

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

    
149
    return ret;
150
    }
151

    
152
///////////////////////////////////////////////////////////////////////////////////////////////////
153

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

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

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

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

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

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

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

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

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

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

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

    
204
    return -1;
205
    }
206

    
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208

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

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

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

    
225
    return -2;
226
    }
227

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

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

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

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

    
249
    return mInvertedQuat[index];
250
    }
251

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

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

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

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

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

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

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

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

    
297
    return ret;
298
    }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301

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

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

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

    
314
    do
315
      {
316
      numInserted = 0;
317

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

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

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

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

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

    
340
///////////////////////////////////////////////////////////////////////////////////////////////////
341

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

    
347
///////////////////////////////////////////////////////////////////////////////////////////////////
348

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

    
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358

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

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

    
371
///////////////////////////////////////////////////////////////////////////////////////////////////
372

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

    
379
      move[0] = m[0];
380
      move[1] = m[1];
381
      move[2] = m[2];
382
      }
383
    }
384

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386

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

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

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397

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

    
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407

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

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

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

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

    
431
///////////////////////////////////////////////////////////////////////////////////////////////////
432

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

    
438
///////////////////////////////////////////////////////////////////////////////////////////////////
439

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

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

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

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

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

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

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

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

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

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

    
497
        getNextAxisLayerAngleQuat(data);
498
        }
499

    
500
      quats = getQuats(index);
501

    
502
      if( !found )
503
        {
504
        if( osi!=null ) osi.reportError("solution error: no move found!"+index);
505
        return null;
506
        }
507
      }
508

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

    
513
///////////////////////////////////////////////////////////////////////////////////////////////////
514

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

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

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

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

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

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

    
547
///////////////////////////////////////////////////////////////////////////////////////////////////
548
// TESTING
549
///////////////////////////////////////////////////////////////////////////////////////////////////
550

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

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

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

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

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

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

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

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

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

    
595
      getNextAxisLayerAngleQuat(data);
596
      }
597

    
598
    return false;
599
    }
600

    
601
///////////////////////////////////////////////////////////////////////////////////////////////////
602

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

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

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

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

    
628
///////////////////////////////////////////////////////////////////////////////////////////////////
629

    
630
  public void test()
631
    {
632
    int[] q1= {0,0,0,0,1,1,1,1, 0,0,0,0,1,1,1,1, 0,0};
633
    int index = getIndex(q1);
634
    int[] q2= getQuats(index);
635

    
636
    TablebaseHelpers.displayTable(q1, "QUATS1");
637
    TablebaseHelpers.displayTable(q2, "QUATS2");
638
    android.util.Log.e("D", "index="+index);
639
    }
640
}
(18-18/19)