Project

General

Profile

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

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

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

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

    
39
  Tablebase mTablebase;
40
  boolean mInitialized;
41

    
42
  final float[][] mQuats;
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
  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

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

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

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

    
94
    int scaling = 0;
95

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

    
103
    mScalingFactor = scaling;
104
    mRotRow = new int[mNumCubits][mNumAxis];
105
    mInitialized = false;
106
    }
107

    
108
///////////////////////////////////////////////////////////////////////////////////////////////////
109

    
110
  public TablebasesAbstract(OperatingSystemInterface os, int resource)
111
    {
112
    this();
113

    
114
    mInitialized = true;
115
    InputStream stream = os.openLocalFile(resource);
116
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
117

    
118
    int nRead;
119
    byte[] tmp = new byte[16384];
120

    
121
    try
122
      {
123
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
124
        {
125
        buffer.write(tmp, 0, nRead);
126
        }
127
      stream.close();
128
      byte[] data = buffer.toByteArray();
129
      buffer.close();
130
      mTablebase = new Tablebase(data);
131
      }
132
    catch(IOException ex)
133
      {
134
      mInitialized = false;
135
      }
136
    }
137

    
138
///////////////////////////////////////////////////////////////////////////////////////////////////
139

    
140
  int computeBitLayer(int ax, int layer)
141
    {
142
    return (1<<layer);
143
    }
144

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

    
147
  void computeRow(float[] pos, int quat, int[] output)
148
    {
149
    int len = pos.length/3;
150
    float[] q = mQuats[quat];
151
    int num = output.length;
152

    
153
    for(int i=0; i<len; i++)
154
      {
155
      QuatHelper.rotateVectorByQuat(mTmp,pos[3*i],pos[3*i+1],pos[3*i+2],1.0f,q);
156

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

    
170
///////////////////////////////////////////////////////////////////////////////////////////////////
171

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

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

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

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185
// remember about the double cover or unit quaternions!
186

    
187
  public static int mulQuat(int q1, int q2, float[][] quats)
188
    {
189
    int numQuats = quats.length;
190
    float[] result = QuatHelper.quatMultiply(quats[q1],quats[q2]);
191

    
192
    float rX = result[0];
193
    float rY = result[1];
194
    float rZ = result[2];
195
    float rW = result[3];
196

    
197
    final float MAX_ERROR = 0.1f;
198
    float dX,dY,dZ,dW;
199

    
200
    for(int i=0; i<numQuats; i++)
201
      {
202
      float[] q = quats[i];
203
      dX = q[0] - rX;
204
      dY = q[1] - rY;
205
      dZ = q[2] - rZ;
206
      dW = q[3] - rW;
207

    
208
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
209
          dY<MAX_ERROR && dY>-MAX_ERROR &&
210
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
211
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
212

    
213
      dX = q[0] + rX;
214
      dY = q[1] + rY;
215
      dZ = q[2] + rZ;
216
      dW = q[3] + rW;
217

    
218
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
219
          dY<MAX_ERROR && dY>-MAX_ERROR &&
220
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
221
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
222
      }
223

    
224
    return -1;
225
    }
226

    
227
///////////////////////////////////////////////////////////////////////////////////////////////////
228

    
229
  int getMultQuat(int index1, int index2)
230
    {
231
    if( mQuatMult==null )
232
      {
233
      mQuatMult = new int[mNumQuats][mNumQuats];
234

    
235
      for(int i=0; i<mNumQuats; i++)
236
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
237
      }
238

    
239
    if( index1<mNumQuats && index2<mNumQuats )
240
      {
241
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2, mQuats);
242
      return mQuatMult[index1][index2];
243
      }
244

    
245
    return -2;
246
    }
247

    
248
///////////////////////////////////////////////////////////////////////////////////////////////////
249

    
250
  int getInvertedQuat(int index)
251
    {
252
    if( mInvertedQuat==null )
253
      {
254
      mInvertedQuat = new int[mNumQuats];
255

    
256
      for(int i=0; i<mNumQuats; i++)
257
        for(int j=0; j<mNumQuats; j++)
258
          {
259
          int result = getMultQuat(i,j);
260

    
261
          if( result==0 )
262
            {
263
            mInvertedQuat[i] = j;
264
            break;
265
            }
266
          }
267
      }
268

    
269
    return mInvertedQuat[index];
270
    }
271

    
272
///////////////////////////////////////////////////////////////////////////////////////////////////
273
// assumption: all layers have the same basicAngles!
274

    
275
  private int insertAllChildren(int index, byte level)
276
    {
277
    int ret = 0;
278
    int[] quats = getQuats(index);
279
    int numQuats = quats.length;
280
    int[] tmpQuats = new int[numQuats];
281
    byte newLevel = (byte)(level+1);
282
    int quatBasis = 0;
283

    
284
    for(int cubit=0; cubit<mNumCubits; cubit++) computeRow(mPosition[cubit],quats[cubit],mRotRow[cubit]);
285

    
286
    for(int ax=0; ax<mNumAxis; ax++)
287
      {
288
      for(int layer=0; layer<mNumLayers[ax]; layer++)
289
        {
290
        if( !mRotatable[ax][layer] ) continue;
291
        int bitLayer = computeBitLayer(ax,layer);
292
        int maxAngle = mAngles[ax][layer];
293

    
294
        for(int angle=1; angle<maxAngle; angle++ )
295
          {
296
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
297
          int quat = quatBasis + angle;
298

    
299
          for(int cubit=0; cubit<mNumCubits; cubit++)
300
            if( (mRotRow[cubit][ax] & bitLayer) != 0 )
301
              {
302
              int currQuat = tmpQuats[cubit];
303
              int newQuat = getMultQuat(quat,currQuat);
304
              tmpQuats[cubit] = newQuat;
305
              }
306

    
307
          int childIndex = getIndex(tmpQuats);
308
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
309
          }
310
        }
311

    
312
      quatBasis += (mAngles[ax][0]-1);
313
      }
314

    
315
    return ret;
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319

    
320
  public boolean useForScrambling()
321
    {
322
    return true;
323
    }
324

    
325
///////////////////////////////////////////////////////////////////////////////////////////////////
326

    
327
  public void createTablebase(int maxLevel)
328
    {
329
    mTablebase = new Tablebase(mSize);
330
    int[] solved = getSolvedIndices();
331
    int numSolved = solved.length;
332
    for(int s : solved ) mTablebase.insertUnpacked(s,(byte)0);
333

    
334
    int numInserted, totalInserted=numSolved;
335
    byte insertingLevel = 0;
336

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

    
339
    do
340
      {
341
      numInserted = 0;
342

    
343
      for(int i=0; i<mSize; i++)
344
        {
345
        byte level = mTablebase.retrieveUnpacked(i);
346
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
347
        }
348

    
349
      insertingLevel++;
350
      totalInserted += numInserted;
351
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
352
      }
353
    while( numInserted>0 && insertingLevel!=maxLevel );
354

    
355
    android.util.Log.e("D", "total Inserted: "+totalInserted);
356
    }
357

    
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359

    
360
  int[] getSolvedIndices()
361
    {
362
    return new int[] {0};
363
    }
364

    
365
///////////////////////////////////////////////////////////////////////////////////////////////////
366

    
367
  boolean isSolved(int index)
368
    {
369
    return index==0;
370
    }
371

    
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373

    
374
  public void pack()
375
    {
376
    android.util.Log.e("D", "packing...");
377
    mTablebase.pack();
378
    android.util.Log.e("D", "all done");
379
    mInitialized = true;
380
    }
381

    
382
///////////////////////////////////////////////////////////////////////////////////////////////////
383

    
384
  public byte[][] getPacked()
385
    {
386
    if( !mInitialized )
387
      {
388
      createTablebase(-1);
389
      pack();
390
      }
391

    
392
    byte[] data = mTablebase.getPacked();
393
    return new byte[][] { data };
394
    }
395

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

    
398
  void convertMoves(int[][] moves)
399
    {
400
    for(int[] move : moves )
401
      {
402
      int[] m = newMove(move[0],move[1],move[2]);
403

    
404
      move[0] = m[0];
405
      move[1] = m[1];
406
      move[2] = m[2];
407
      }
408
    }
409

    
410
///////////////////////////////////////////////////////////////////////////////////////////////////
411

    
412
  int[] newMove(int ax, int layer, int angle)
413
    {
414
    int maxAngle = mAngles[ax][layer];
415
    angle = maxAngle-angle;
416
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
417

    
418
    return new int[] { ax, computeBitLayer(ax,layer), angle };
419
    }
420

    
421
///////////////////////////////////////////////////////////////////////////////////////////////////
422

    
423
  int[][] convertMovesFromArray(ArrayList<int[]> moves)
424
    {
425
    int len = moves.size();
426
    int[][] ret = new int[len][];
427
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
428
    return ret;
429
    }
430

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

    
433
  void getNextAxisLayerAngleQuat(int[] data)
434
    {
435
    int axis = data[0];
436
    int layer= data[1];
437
    int angle= data[2];
438

    
439
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
440
    else
441
      {
442
      data[2] = 1;
443

    
444
      if( layer< mNumLayers[axis]-1 ) data[1]++;
445
      else
446
        {
447
        data[1] = 0;
448
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
449
        }
450
      }
451

    
452
    data[3] = data[2];
453
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
454
    }
455

    
456
///////////////////////////////////////////////////////////////////////////////////////////////////
457

    
458
  int[][] extraInfo(int[][] moves, int[] extra)
459
    {
460
    return moves;
461
    }
462

    
463
///////////////////////////////////////////////////////////////////////////////////////////////////
464

    
465
  public int[][] solution(int index, int[] extra, OperatingSystemInterface osi)
466
    {
467
    if( !mInitialized ) return null;
468

    
469
    int[] data = new int[4];
470
    byte level = mTablebase.retrievePacked(index);
471
    ArrayList<int[]> moves = new ArrayList<>();
472
    int[] quats = getQuats(index);
473
    int numQuats = quats.length;
474
    int[] tmpQuats = new int[numQuats];
475

    
476
    while( !isSolved(index) )
477
      {
478
      boolean found = false;
479

    
480
      data[0]=0;
481
      data[1]=0;
482
      data[2]=1;
483
      data[3]=1;
484

    
485
      for(int cubit=0; cubit<mNumCubits; cubit++) computeRow(mPosition[cubit],quats[cubit],mRotRow[cubit]);
486

    
487
      for(int s=0; s<mScalingFactor && !found; s++)
488
        {
489
        int ax    = data[0];
490
        int layer = data[1];
491
        int angle = data[2];
492
        int quat  = data[3];
493

    
494
        if( mRotatable[ax][layer] )
495
          {
496
          int bitLayer = computeBitLayer(ax,layer);
497
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
498

    
499
          for(int cubit=0; cubit<mNumCubits; cubit++)
500
            if( (mRotRow[cubit][ax] & bitLayer) != 0 )
501
              {
502
              int currQuat = tmpQuats[cubit];
503
              int newQuat = getMultQuat(quat,currQuat);
504
              tmpQuats[cubit] = newQuat;
505
              }
506

    
507
          int childIndex = getIndex(tmpQuats);
508
          byte newLevel = mTablebase.retrievePacked(childIndex);
509

    
510
          if( ((newLevel-level+1)%3) == 0 )
511
            {
512
            int[] tmpMove = newMove(ax,layer,angle);
513
            moves.add(tmpMove);
514
            index = childIndex;
515
            level = (level==0 ? 2 : (byte)(level-1));
516
            found = true;
517
            }
518
          }
519

    
520
        getNextAxisLayerAngleQuat(data);
521
        }
522

    
523
      quats = getQuats(index);
524

    
525
      if( !found )
526
        {
527
        if( osi!=null ) osi.reportError("solution error: no move found!"+index);
528
        return null;
529
        }
530
      }
531

    
532
    int[][] ret = convertMovesFromArray(moves);
533
    return extraInfo(ret,extra);
534
    }
535

    
536
///////////////////////////////////////////////////////////////////////////////////////////////////
537

    
538
  public void scramble(Random rnd, int depth, int[][] scramble)
539
    {
540
    if( !mInitialized ) return;
541

    
542
    int solDepth = 0;
543
    int scraLen = scramble.length;
544
    if( depth>mMinScramble ) depth = mMinScramble;
545

    
546
    int[] cornerTwist = new int[4];
547
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
548

    
549
    while( solDepth<depth )
550
      {
551
      int randomPosition = rnd.nextInt(mSize-1);
552
      int[][] sol = solution(randomPosition,cornerTwist,null);
553
      solDepth = (sol!=null ? sol.length : 0);
554

    
555
      if( solDepth>=depth )
556
        {
557
        int num = Math.min(scraLen,solDepth);
558

    
559
        for(int i=0; i<num; i++)
560
          {
561
          int[] source = sol[solDepth-1-i];
562
          scramble[i][0] = source[0];
563
          scramble[i][1] = source[1];
564
          scramble[i][2] =-source[2];
565
          }
566
        }
567
      }
568
    }
569

    
570
///////////////////////////////////////////////////////////////////////////////////////////////////
571
// TESTING
572
///////////////////////////////////////////////////////////////////////////////////////////////////
573

    
574
  public boolean test(int index)
575
    {
576
    if( !mInitialized ) return false;
577

    
578
    int[] data = new int[4];
579
    byte level = mTablebase.retrievePacked(index);
580
    int[] quats = getQuats(index);
581
    int numQuats = quats.length;
582
    int[] tmpQuats = new int[numQuats];
583

    
584
    data[0]=0;
585
    data[1]=0;
586
    data[2]=1;
587
    data[3]=1;
588

    
589
    for(int cubit=0; cubit<mNumCubits; cubit++) computeRow(mPosition[cubit],quats[cubit],mRotRow[cubit]);
590

    
591
    for(int s=0; s<mScalingFactor; s++)
592
      {
593
      int ax    = data[0];
594
      int layer = data[1];
595
      int quat  = data[3];
596

    
597
      if( mRotatable[ax][layer] )
598
        {
599
        int bitLayer = computeBitLayer(ax,layer);
600
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
601

    
602
        for(int cubit=0; cubit<mNumCubits; cubit++)
603
          if( (mRotRow[cubit][ax] & bitLayer) != 0 )
604
            {
605
            int currQuat = tmpQuats[cubit];
606
            int newQuat = getMultQuat(quat,currQuat);
607
            tmpQuats[cubit] = newQuat;
608
            }
609

    
610
        int childIndex = getIndex(tmpQuats);
611
        byte newLevel = mTablebase.retrievePacked(childIndex);
612

    
613
        if( ((newLevel-level+1)%3) == 0 ) return true;
614
        }
615

    
616
      getNextAxisLayerAngleQuat(data);
617
      }
618

    
619
    return false;
620
    }
621

    
622
///////////////////////////////////////////////////////////////////////////////////////////////////
623

    
624
  public void test(OperatingSystemInterface os)
625
    {
626
    int index = 252373232;
627
    int[] q = getQuats(index);
628
    TablebaseHelpers.displayTable(q , "QUATS ");
629
    }
630
}
(18-18/19)