Project

General

Profile

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

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

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 android.content.res.Resources;
13

    
14
import org.distorted.library.main.QuatHelper;
15
import org.distorted.library.type.Static3D;
16
import org.distorted.library.type.Static4D;
17
import org.distorted.objectlib.helpers.QuatGroupGenerator;
18

    
19
import java.io.ByteArrayOutputStream;
20
import java.io.IOException;
21
import java.io.InputStream;
22
import java.util.ArrayList;
23
import java.util.Random;
24

    
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26

    
27
public abstract class TablebasesAbstract
28
{
29
  private final Static3D[] mAxis;
30
  private final int mSize, mMinScramble;
31
  private final int[][] mAngles;
32
  private final int mNumAxis;
33
  private final int[] mNumLayers;
34
  private final int mNumQuats;
35
  private final Static4D[] mQuats;
36
  private final int[][] mRotRow;
37
  private final int mNumCubits;
38
  private final float[][] mPosition;
39
  private final float[][] mCuts;
40
  private final int[] mNumCuts;
41
  private final boolean[][] mRotatable;
42
  private final int mScalingFactor;
43

    
44
  private Tablebase mTablebase;
45
  private int[][] mQuatMult;
46
  private boolean mInitialized;
47

    
48
  private static final float[] mTmp = new float[4];
49

    
50
///////////////////////////////////////////////////////////////////////////////////////////////////
51

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

    
57
  abstract boolean[][] getRotatable();
58

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

    
64
///////////////////////////////////////////////////////////////////////////////////////////////////
65

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

    
83
    int scaling = 0;
84

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

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

    
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98

    
99
  public TablebasesAbstract(Resources res, int resource)
100
    {
101
    this();
102

    
103
    mInitialized = true;
104
    InputStream stream = res.openRawResource(resource);
105
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
106

    
107
    int nRead;
108
    byte[] tmp = new byte[16384];
109

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

    
127
///////////////////////////////////////////////////////////////////////////////////////////////////
128

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

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

    
147
    return ret;
148
    }
149

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

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

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

    
161
    return (1<<num);
162
    }
163

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165
// remember about the double cover or unit quaternions!
166

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

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

    
176
    final float MAX_ERROR = 0.1f;
177
    float dX,dY,dZ,dW;
178

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

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

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

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

    
202
    return -1;
203
    }
204

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

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

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

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

    
223
    return -2;
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227
// assumption: all layers have the same basicAngles!
228

    
229
  private int insertAllChildren(int index, byte level)
230
    {
231
    int ret = 0;
232
    int[] quats = getQuats(index);
233
    int numQuats = quats.length;
234
    int[] tmpQuats = new int[numQuats];
235
    byte newLevel = (byte)(level+1);
236
    int quatBasis = 0;
237

    
238
    for(int ax=0; ax<mNumAxis; ax++)
239
      for(int cubit=0; cubit<mNumCubits; cubit++)
240
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
241

    
242
    for(int ax=0; ax<mNumAxis; ax++)
243
      {
244
      for(int layer=0; layer<mNumLayers[ax]; layer++)
245
        {
246
        if( !mRotatable[ax][layer] ) continue;
247
        int bitLayer = (1<<layer);
248
        int maxAngle = mAngles[ax][layer];
249

    
250
        for(int angle=1; angle<maxAngle; angle++ )
251
          {
252
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
253
          int quat = quatBasis + angle;
254

    
255
          for(int cubit=0; cubit<mNumCubits; cubit++)
256
            if( mRotRow[cubit][ax]==bitLayer )
257
              {
258
              int currQuat = tmpQuats[cubit];
259
              int newQuat = getMultQuat(quat,currQuat);
260
              tmpQuats[cubit] = newQuat;
261
              }
262

    
263
          int childIndex = getIndex(tmpQuats);
264
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
265
          }
266
        }
267

    
268
      quatBasis += (mAngles[ax][0]-1);
269
      }
270

    
271
    return ret;
272
    }
273

    
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275

    
276
  public void createTablebase(int maxLevel)
277
    {
278
    mTablebase = new Tablebase(mSize);
279
    mTablebase.insertUnpacked(0,(byte)0);
280

    
281
    int numInserted, totalInserted=1;
282
    byte insertingLevel = 0;
283

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

    
286
    do
287
      {
288
      numInserted = 0;
289

    
290
      for(int i=0; i<mSize; i++)
291
        {
292
        byte level = mTablebase.retrieveUnpacked(i);
293
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
294
        }
295

    
296
      insertingLevel++;
297
      totalInserted += numInserted;
298
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
299
      }
300
    while( numInserted>0 && insertingLevel!=maxLevel );
301

    
302
    android.util.Log.e("D", "total Inserted: "+totalInserted);
303
    }
304

    
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306

    
307
  public void pack()
308
    {
309
    android.util.Log.e("D", "packing...");
310
    mTablebase.pack();
311
    android.util.Log.e("D", "all done");
312
    mInitialized = true;
313
    }
314

    
315
///////////////////////////////////////////////////////////////////////////////////////////////////
316

    
317
  public byte[] getPacked()
318
    {
319
    return mTablebase.getPacked();
320
    }
321

    
322
///////////////////////////////////////////////////////////////////////////////////////////////////
323

    
324
  private void addMove(ArrayList<int[]> moves, int axis, int layer, int angle)
325
    {
326
    int maxAngle = mAngles[axis][layer];
327
    angle = maxAngle-angle;
328
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
329

    
330
    int[] move = new int[] { axis, (1<<layer), angle };
331
    moves.add(move);
332
    }
333

    
334
///////////////////////////////////////////////////////////////////////////////////////////////////
335

    
336
  private int[][] convertMoves(ArrayList<int[]> moves)
337
    {
338
    int len = moves.size();
339
    int[][] ret = new int[len][];
340
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
341
    return ret;
342
    }
343

    
344
///////////////////////////////////////////////////////////////////////////////////////////////////
345

    
346
  private void getNextAxisLayerAngleQuat(int[] data)
347
    {
348
    int axis = data[0];
349
    int layer= data[1];
350
    int angle= data[2];
351

    
352
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
353
    else
354
      {
355
      data[2] = 1;
356

    
357
      if( layer< mNumLayers[axis]-1 ) data[1]++;
358
      else
359
        {
360
        data[1] = 0;
361
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
362
        }
363
      }
364

    
365
    data[3] = data[2];
366
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
367
    }
368

    
369
///////////////////////////////////////////////////////////////////////////////////////////////////
370

    
371
  int[][] extraInfo(int[][] moves, int[] extra)
372
    {
373
    return moves;
374
    }
375

    
376
///////////////////////////////////////////////////////////////////////////////////////////////////
377

    
378
  Static4D[] getQuats()
379
    {
380
    return mQuats;
381
    }
382

    
383
///////////////////////////////////////////////////////////////////////////////////////////////////
384

    
385
  public int[][] solution(int index, int[] extra)
386
    {
387
    if( !mInitialized ) return null;
388

    
389
    int[] data = new int[4];
390
    byte level = mTablebase.retrievePacked(index);
391
    ArrayList<int[]> moves = new ArrayList<>();
392
    int[] quats = getQuats(index);
393
    int numQuats = quats.length;
394
    int[] tmpQuats = new int[numQuats];
395

    
396
    while(index!=0)
397
      {
398
      boolean found = false;
399

    
400
      data[0]=0;
401
      data[1]=0;
402
      data[2]=1;
403
      data[3]=1;
404

    
405
      for(int ax=0; ax<mNumAxis; ax++)
406
        for(int cubit=0; cubit<mNumCubits; cubit++)
407
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
408

    
409
      for(int s=0; s<mScalingFactor && !found; s++)
410
        {
411
        int ax    = data[0];
412
        int layer = data[1];
413
        int angle = data[2];
414
        int quat  = data[3];
415

    
416
        if( mRotatable[ax][layer] )
417
          {
418
          int bitLayer = (1<<layer);
419
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
420

    
421
          for(int cubit=0; cubit<mNumCubits; cubit++)
422
            if( mRotRow[cubit][ax]==bitLayer )
423
              {
424
              int currQuat = tmpQuats[cubit];
425
              int newQuat = getMultQuat(quat,currQuat);
426
              tmpQuats[cubit] = newQuat;
427
              }
428

    
429
          int childIndex = getIndex(tmpQuats);
430
          byte newLevel = mTablebase.retrievePacked(childIndex);
431

    
432
          if( ((newLevel-level+1)%3) == 0 )
433
            {
434
            addMove(moves,ax,layer,angle);
435
            index = childIndex;
436
            level = (level==0 ? 2 : (byte)(level-1));
437
            found = true;
438
            }
439
          }
440

    
441
        getNextAxisLayerAngleQuat(data);
442
        }
443

    
444
      quats = getQuats(index);
445

    
446
      if( !found )
447
        {
448
        android.util.Log.e("D", "----> solution error: no move found!");
449
        return null;
450
        }
451
      }
452

    
453
    int[][] ret = convertMoves(moves);
454
    return extraInfo(ret,extra);
455
    }
456

    
457
///////////////////////////////////////////////////////////////////////////////////////////////////
458

    
459
  public void scramble(Random rnd, int depth, int[][] scramble)
460
    {
461
    if( !mInitialized ) return;
462

    
463
    int solDepth = 0;
464
    int scraLen = scramble.length;
465
    if( depth>mMinScramble ) depth = mMinScramble;
466

    
467
    int[] cornerTwist = new int[4];
468
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
469

    
470
    while( solDepth<depth )
471
      {
472
      int randomPosition = rnd.nextInt(mSize-1);
473
      int[][] sol = solution(randomPosition,cornerTwist);
474
      solDepth = sol.length;
475

    
476
      if( solDepth>=depth )
477
        {
478
        int num = Math.min(scraLen,solDepth);
479

    
480
        for(int i=0; i<num; i++)
481
          {
482
          int[] source = sol[solDepth-1-i];
483
          scramble[i][0] = source[0];
484
          scramble[i][1] = source[1];
485
          scramble[i][2] =-source[2];
486
          }
487
        }
488
      }
489
    }
490

    
491
///////////////////////////////////////////////////////////////////////////////////////////////////
492

    
493
  public boolean test(int index)
494
    {
495
    if( !mInitialized ) return false;
496

    
497
    int[] data = new int[4];
498
    byte level = mTablebase.retrievePacked(index);
499
    int[] quats = getQuats(index);
500
    int numQuats = quats.length;
501
    int[] tmpQuats = new int[numQuats];
502

    
503
    data[0]=0;
504
    data[1]=0;
505
    data[2]=1;
506
    data[3]=1;
507

    
508
    for(int ax=0; ax<mNumAxis; ax++)
509
      for(int cubit=0; cubit<mNumCubits; cubit++)
510
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
511

    
512
    for(int s=0; s<mScalingFactor; s++)
513
      {
514
      int ax    = data[0];
515
      int layer = data[1];
516
      int quat  = data[3];
517

    
518
      if( mRotatable[ax][layer] )
519
        {
520
        int bitLayer = (1<<layer);
521
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
522

    
523
        for(int cubit=0; cubit<mNumCubits; cubit++)
524
          if( mRotRow[cubit][ax]==bitLayer )
525
            {
526
            int currQuat = tmpQuats[cubit];
527
            int newQuat = getMultQuat(quat,currQuat);
528
            tmpQuats[cubit] = newQuat;
529
            }
530

    
531
        int childIndex = getIndex(tmpQuats);
532
        byte newLevel = mTablebase.retrievePacked(childIndex);
533

    
534
        if( ((newLevel-level+1)%3) == 0 ) return true;
535
        }
536

    
537
      getNextAxisLayerAngleQuat(data);
538
      }
539

    
540
    return false;
541
    }
542

    
543
///////////////////////////////////////////////////////////////////////////////////////////////////
544

    
545
    public void testPruning(int level)
546
      {
547
      PruningTable table = new PruningTable(mTablebase,level);
548
      int size = mTablebase.getSize();
549

    
550
      StringBuilder sb = new StringBuilder();
551
      int num=0;
552

    
553
      for(int i=0; i<size; i++)
554
        {
555
        if( table.belongs(i) )
556
          {
557
          if( (num%10)==0 ) sb.append("\n");
558
          num++;
559
          sb.append(i);
560
          sb.append(' ');
561
          }
562
        }
563

    
564
      android.util.Log.e("D", "numbers: "+sb);
565
      }
566
}
(5-5/12)