Project

General

Profile

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

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

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
  Tablebase mTablebase;
43
  boolean mInitialized;
44

    
45
  final int mScalingFactor;
46
  final int mNumAxis;
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

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

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

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

    
95
    int scaling = 0;
96

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

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

    
107
    mCubitQuatRows = new int[mNumCubits][mNumQuats][];
108

    
109
    for(int i=0; i<mNumCubits; i++)
110
      for(int j=0; j<mNumQuats; j++) mCubitQuatRows[i][j] = computeMap(i,j);
111

    
112
    mInitialized = false;
113
    }
114

    
115
///////////////////////////////////////////////////////////////////////////////////////////////////
116

    
117
  public TablebasesAbstract(OperatingSystemInterface os, int resource)
118
    {
119
    this();
120

    
121
    mInitialized = true;
122
    InputStream stream = os.openLocalFile(resource);
123
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
124

    
125
    int nRead;
126
    byte[] tmp = new byte[16384];
127

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

    
145
///////////////////////////////////////////////////////////////////////////////////////////////////
146

    
147
  private int[] computeMap(int cubit, int quat)
148
    {
149
    float[] pos = mPosition[cubit];
150
    float[] q = mQuats[quat];
151

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

    
154
    int[] output = new int[mNumAxis];
155

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

    
166
    return output;
167
    }
168

    
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170

    
171
  private int computeSingleRow(int axisIndex,float casted)
172
    {
173
    int num = mNumCuts[axisIndex];
174

    
175
    for(int i=0; i<num; i++)
176
      {
177
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
178
      }
179

    
180
    return (1<<num);
181
    }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184

    
185
  int computeBitLayer(int ax, int layer)
186
    {
187
    return (1<<layer);
188
    }
189

    
190
///////////////////////////////////////////////////////////////////////////////////////////////////
191

    
192
  int[] computeRow(int cubit, int quat)
193
    {
194
    return mCubitQuatRows[cubit][quat];
195
    }
196

    
197
///////////////////////////////////////////////////////////////////////////////////////////////////
198
// remember about the double cover or unit quaternions!
199

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

    
205
    float rX = result[0];
206
    float rY = result[1];
207
    float rZ = result[2];
208
    float rW = result[3];
209

    
210
    final float MAX_ERROR = 0.1f;
211
    float dX,dY,dZ,dW;
212

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

    
221
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
222
          dY<MAX_ERROR && dY>-MAX_ERROR &&
223
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
224
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
225

    
226
      dX = q[0] + rX;
227
      dY = q[1] + rY;
228
      dZ = q[2] + rZ;
229
      dW = q[3] + rW;
230

    
231
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
232
          dY<MAX_ERROR && dY>-MAX_ERROR &&
233
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
234
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
235
      }
236

    
237
    return -1;
238
    }
239

    
240
///////////////////////////////////////////////////////////////////////////////////////////////////
241

    
242
  int getMultQuat(int index1, int index2)
243
    {
244
    if( mQuatMult==null )
245
      {
246
      mQuatMult = new int[mNumQuats][mNumQuats];
247

    
248
      for(int i=0; i<mNumQuats; i++)
249
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
250
      }
251

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

    
258
    return -2;
259
    }
260

    
261
///////////////////////////////////////////////////////////////////////////////////////////////////
262

    
263
  int getInvertedQuat(int index)
264
    {
265
    if( mInvertedQuat==null )
266
      {
267
      mInvertedQuat = new int[mNumQuats];
268

    
269
      for(int i=0; i<mNumQuats; i++)
270
        for(int j=0; j<mNumQuats; j++)
271
          {
272
          int result = getMultQuat(i,j);
273

    
274
          if( result==0 )
275
            {
276
            mInvertedQuat[i] = j;
277
            break;
278
            }
279
          }
280
      }
281

    
282
    return mInvertedQuat[index];
283
    }
284

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286
// assumption: all layers have the same basicAngles!
287

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

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

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

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

    
312
          for(int cubit=0; cubit<mNumCubits; cubit++)
313
            if( (mRotRow[cubit][ax] & bitLayer) != 0 )
314
              {
315
              int currQuat = tmpQuats[cubit];
316
              int newQuat = getMultQuat(quat,currQuat);
317
              tmpQuats[cubit] = newQuat;
318
              }
319

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

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

    
328
    return ret;
329
    }
330

    
331
///////////////////////////////////////////////////////////////////////////////////////////////////
332

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

    
338
///////////////////////////////////////////////////////////////////////////////////////////////////
339

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

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

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

    
352
    do
353
      {
354
      numInserted = 0;
355

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

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

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

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

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

    
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379

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

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

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

    
395
///////////////////////////////////////////////////////////////////////////////////////////////////
396

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

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

    
409
///////////////////////////////////////////////////////////////////////////////////////////////////
410

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

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

    
423
///////////////////////////////////////////////////////////////////////////////////////////////////
424

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

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

    
434
///////////////////////////////////////////////////////////////////////////////////////////////////
435

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

    
444
///////////////////////////////////////////////////////////////////////////////////////////////////
445

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

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

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

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

    
469
///////////////////////////////////////////////////////////////////////////////////////////////////
470

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

    
476
///////////////////////////////////////////////////////////////////////////////////////////////////
477

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

    
482
    int[] data = new int[4];
483
    byte level = mTablebase.retrievePacked(index);
484
    ArrayList<int[]> moves = new ArrayList<>();
485
    int[] quats = getQuats(index);
486
    int numQuats = quats.length;
487
    int[] tmpQuats = new int[numQuats];
488

    
489
    while( !isSolved(index) )
490
      {
491
      boolean found = false;
492

    
493
      data[0]=0;
494
      data[1]=0;
495
      data[2]=1;
496
      data[3]=1;
497

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

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

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

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

    
520
          int childIndex = getIndex(tmpQuats);
521
          byte newLevel = mTablebase.retrievePacked(childIndex);
522

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

    
533
        getNextAxisLayerAngleQuat(data);
534
        }
535

    
536
      quats = getQuats(index);
537

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

    
545
    int[][] ret = convertMovesFromArray(moves);
546
    return extraInfo(ret,extra);
547
    }
548

    
549
///////////////////////////////////////////////////////////////////////////////////////////////////
550

    
551
  public void scramble(Random rnd, int depth, int[][] scramble)
552
    {
553
    if( !mInitialized ) return;
554

    
555
    int solDepth = 0;
556
    int scraLen = scramble.length;
557
    if( depth>mMinScramble ) depth = mMinScramble;
558

    
559
    int[] cornerTwist = new int[4];
560
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
561

    
562
    while( solDepth<depth )
563
      {
564
      int randomPosition = rnd.nextInt(mSize-1);
565
      int[][] sol = solution(randomPosition,cornerTwist,null);
566
      solDepth = (sol!=null ? sol.length : 0);
567

    
568
      if( solDepth>=depth )
569
        {
570
        int num = Math.min(scraLen,solDepth);
571

    
572
        for(int i=0; i<num; i++)
573
          {
574
          int[] source = sol[solDepth-1-i];
575
          scramble[i][0] = source[0];
576
          scramble[i][1] = source[1];
577
          scramble[i][2] =-source[2];
578
          }
579
        }
580
      }
581
    }
582

    
583
///////////////////////////////////////////////////////////////////////////////////////////////////
584
// TESTING
585
///////////////////////////////////////////////////////////////////////////////////////////////////
586

    
587
  public boolean test(int index)
588
    {
589
    if( !mInitialized ) return false;
590

    
591
    int[] data = new int[4];
592
    byte level = mTablebase.retrievePacked(index);
593
    int[] quats = getQuats(index);
594
    int numQuats = quats.length;
595
    int[] tmpQuats = new int[numQuats];
596

    
597
    data[0]=0;
598
    data[1]=0;
599
    data[2]=1;
600
    data[3]=1;
601

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

    
604
    for(int s=0; s<mScalingFactor; s++)
605
      {
606
      int ax    = data[0];
607
      int layer = data[1];
608
      int quat  = data[3];
609

    
610
      if( mRotatable[ax][layer] )
611
        {
612
        int bitLayer = computeBitLayer(ax,layer);
613
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
614

    
615
        for(int cubit=0; cubit<mNumCubits; cubit++)
616
          if( (mRotRow[cubit][ax] & bitLayer) != 0 )
617
            {
618
            int currQuat = tmpQuats[cubit];
619
            int newQuat = getMultQuat(quat,currQuat);
620
            tmpQuats[cubit] = newQuat;
621
            }
622

    
623
        int childIndex = getIndex(tmpQuats);
624
        byte newLevel = mTablebase.retrievePacked(childIndex);
625

    
626
        if( ((newLevel-level+1)%3) == 0 ) return true;
627
        }
628

    
629
      getNextAxisLayerAngleQuat(data);
630
      }
631

    
632
    return false;
633
    }
634

    
635
///////////////////////////////////////////////////////////////////////////////////////////////////
636

    
637
  public void test(OperatingSystemInterface os)
638
    {
639
    int index = 252373232;
640
    int[] q = getQuats(index);
641
    TablebaseHelpers.displayTable(q , "QUATS ");
642
    }
643
}
(18-18/19)