Project

General

Profile

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

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

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
  public static int mulQuat(int q1, int q2, Static4D[] quats)
170
    {
171
    int numQuats = quats.length;
172
    Static4D result = QuatHelper.quatMultiply(quats[q1],quats[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<numQuats; i++)
183
      {
184
      dX = quats[i].get0() - rX;
185
      dY = quats[i].get1() - rY;
186
      dZ = quats[i].get2() - rZ;
187
      dW = quats[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 = quats[i].get0() + rX;
195
      dY = quats[i].get1() + rY;
196
      dZ = quats[i].get2() + rZ;
197
      dW = quats[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, mQuats);
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) )
292
            {
293
            //android.util.Log.e("D", newLevel+" parent:"+index+" index: "+childIndex+" ax="+ax+" layer="+layer+" angle="+angle);
294
            ret++;
295
            }
296
          else
297
            {
298
            //android.util.Log.d("D", newLevel+" parent:"+index+" index: "+childIndex+" ax="+ax+" layer="+layer+" angle="+angle);
299
            }
300
          }
301
        }
302

    
303
      quatBasis += (mAngles[ax][0]-1);
304
      }
305

    
306
    return ret;
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

    
311
  public void createTablebase(int maxLevel)
312
    {
313
    mTablebase = new Tablebase(mSize);
314
    int[] solved = getSolvedIndices();
315
    int numSolved = solved.length;
316
    for(int s : solved ) mTablebase.insertUnpacked(s,(byte)0);
317

    
318
    int numInserted, totalInserted=numSolved;
319
    byte insertingLevel = 0;
320

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

    
323
    do
324
      {
325
      numInserted = 0;
326

    
327
      for(int i=0; i<mSize; i++)
328
        {
329
        byte level = mTablebase.retrieveUnpacked(i);
330
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
331
        }
332

    
333
      insertingLevel++;
334
      totalInserted += numInserted;
335
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
336
      }
337
    while( numInserted>0 && insertingLevel!=maxLevel );
338

    
339
    android.util.Log.e("D", "total Inserted: "+totalInserted);
340
    }
341

    
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343

    
344
  int[] getSolvedIndices()
345
    {
346
    return new int[] {0};
347
    }
348

    
349
///////////////////////////////////////////////////////////////////////////////////////////////////
350

    
351
  boolean isSolved(int index)
352
    {
353
    return index==0;
354
    }
355

    
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357

    
358
  public void pack()
359
    {
360
    android.util.Log.e("D", "packing...");
361
    mTablebase.pack();
362
    android.util.Log.e("D", "all done");
363
    mInitialized = true;
364
    }
365

    
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367

    
368
  public byte[][] getPacked()
369
    {
370
    if( !mInitialized )
371
      {
372
      createTablebase(-1);
373
      pack();
374
      }
375

    
376
    byte[] data = mTablebase.getPacked();
377
    return new byte[][] { data };
378
    }
379

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381

    
382
  void convertMoves(int[][] moves)
383
    {
384
    for(int[] move : moves )
385
      {
386
      int[] m = newMove(move[0],move[1],move[2]);
387

    
388
      move[0] = m[0];
389
      move[1] = m[1];
390
      move[2] = m[2];
391
      }
392
    }
393

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

    
396
  int[] newMove(int axis, int layer, int angle)
397
    {
398
    int maxAngle = mAngles[axis][layer];
399
    angle = maxAngle-angle;
400
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
401

    
402
    return new int[] { axis, (1<<layer), angle };
403
    }
404

    
405
///////////////////////////////////////////////////////////////////////////////////////////////////
406

    
407
  private int[][] convertMovesFromArray(ArrayList<int[]> moves)
408
    {
409
    int len = moves.size();
410
    int[][] ret = new int[len][];
411
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
412
    return ret;
413
    }
414

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

    
417
  void getNextAxisLayerAngleQuat(int[] data)
418
    {
419
    int axis = data[0];
420
    int layer= data[1];
421
    int angle= data[2];
422

    
423
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
424
    else
425
      {
426
      data[2] = 1;
427

    
428
      if( layer< mNumLayers[axis]-1 ) data[1]++;
429
      else
430
        {
431
        data[1] = 0;
432
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
433
        }
434
      }
435

    
436
    data[3] = data[2];
437
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
438
    }
439

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

    
442
  int[][] extraInfo(int[][] moves, int[] extra)
443
    {
444
    return moves;
445
    }
446

    
447
///////////////////////////////////////////////////////////////////////////////////////////////////
448

    
449
  public int[][] solution(int index, int[] extra, OperatingSystemInterface osi)
450
    {
451
    if( !mInitialized ) return null;
452

    
453
    int[] data = new int[4];
454
    byte level = mTablebase.retrievePacked(index);
455
    ArrayList<int[]> moves = new ArrayList<>();
456
    int[] quats = getQuats(index);
457
    int numQuats = quats.length;
458
    int[] tmpQuats = new int[numQuats];
459

    
460
    while( !isSolved(index) )
461
      {
462
      boolean found = false;
463

    
464
      data[0]=0;
465
      data[1]=0;
466
      data[2]=1;
467
      data[3]=1;
468

    
469
      for(int ax=0; ax<mNumAxis; ax++)
470
        for(int cubit=0; cubit<mNumCubits; cubit++)
471
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
472

    
473
      for(int s=0; s<mScalingFactor && !found; s++)
474
        {
475
        int ax    = data[0];
476
        int layer = data[1];
477
        int angle = data[2];
478
        int quat  = data[3];
479

    
480
        if( mRotatable[ax][layer] )
481
          {
482
          int bitLayer = (1<<layer);
483
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
484

    
485
          for(int cubit=0; cubit<mNumCubits; cubit++)
486
            if( mRotRow[cubit][ax]==bitLayer )
487
              {
488
              int currQuat = tmpQuats[cubit];
489
              int newQuat = getMultQuat(quat,currQuat);
490
              tmpQuats[cubit] = newQuat;
491
              }
492

    
493
          int childIndex = getIndex(tmpQuats);
494
          byte newLevel = mTablebase.retrievePacked(childIndex);
495

    
496
          if( ((newLevel-level+1)%3) == 0 )
497
            {
498
            int[] tmpMove = newMove(ax,layer,angle);
499
            moves.add(tmpMove);
500
            index = childIndex;
501
            level = (level==0 ? 2 : (byte)(level-1));
502
            found = true;
503
            }
504
          }
505

    
506
        getNextAxisLayerAngleQuat(data);
507
        }
508

    
509
      quats = getQuats(index);
510

    
511
      if( !found )
512
        {
513
        if( osi!=null ) osi.reportError("solution error: no move found!"+index);
514
        return null;
515
        }
516
      }
517

    
518
    int[][] ret = convertMovesFromArray(moves);
519
    return extraInfo(ret,extra);
520
    }
521

    
522
///////////////////////////////////////////////////////////////////////////////////////////////////
523

    
524
  public void scramble(Random rnd, int depth, int[][] scramble)
525
    {
526
    if( !mInitialized ) return;
527

    
528
    int solDepth = 0;
529
    int scraLen = scramble.length;
530
    if( depth>mMinScramble ) depth = mMinScramble;
531

    
532
    int[] cornerTwist = new int[4];
533
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
534

    
535
    while( solDepth<depth )
536
      {
537
      int randomPosition = rnd.nextInt(mSize-1);
538
      int[][] sol = solution(randomPosition,cornerTwist,null);
539
      solDepth = (sol!=null ? sol.length : 0);
540

    
541
      if( solDepth>=depth )
542
        {
543
        int num = Math.min(scraLen,solDepth);
544

    
545
        for(int i=0; i<num; i++)
546
          {
547
          int[] source = sol[solDepth-1-i];
548
          scramble[i][0] = source[0];
549
          scramble[i][1] = source[1];
550
          scramble[i][2] =-source[2];
551
          }
552
        }
553
      }
554
    }
555

    
556
///////////////////////////////////////////////////////////////////////////////////////////////////
557
// TESTING
558
///////////////////////////////////////////////////////////////////////////////////////////////////
559

    
560
  public boolean test(int index)
561
    {
562
    if( !mInitialized ) return false;
563

    
564
    int[] data = new int[4];
565
    byte level = mTablebase.retrievePacked(index);
566
    int[] quats = getQuats(index);
567
    int numQuats = quats.length;
568
    int[] tmpQuats = new int[numQuats];
569

    
570
    data[0]=0;
571
    data[1]=0;
572
    data[2]=1;
573
    data[3]=1;
574

    
575
    for(int ax=0; ax<mNumAxis; ax++)
576
      for(int cubit=0; cubit<mNumCubits; cubit++)
577
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
578

    
579
    for(int s=0; s<mScalingFactor; s++)
580
      {
581
      int ax    = data[0];
582
      int layer = data[1];
583
      int quat  = data[3];
584

    
585
      if( mRotatable[ax][layer] )
586
        {
587
        int bitLayer = (1<<layer);
588
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
589

    
590
        for(int cubit=0; cubit<mNumCubits; cubit++)
591
          if( mRotRow[cubit][ax]==bitLayer )
592
            {
593
            int currQuat = tmpQuats[cubit];
594
            int newQuat = getMultQuat(quat,currQuat);
595
            tmpQuats[cubit] = newQuat;
596
            }
597

    
598
        int childIndex = getIndex(tmpQuats);
599
        byte newLevel = mTablebase.retrievePacked(childIndex);
600

    
601
        if( ((newLevel-level+1)%3) == 0 ) return true;
602
        }
603

    
604
      getNextAxisLayerAngleQuat(data);
605
      }
606

    
607
    return false;
608
    }
609

    
610
///////////////////////////////////////////////////////////////////////////////////////////////////
611

    
612
  public void test()
613
    {
614
    int index = 252373232;
615
    int[] q = getQuats(index);
616
    TablebaseHelpers.displayTable(q , "QUATS ");
617
    }
618
}
(18-18/19)