Project

General

Profile

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

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

1 a110ebe1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 352b3356 Leszek Koltunski
import android.content.res.Resources;
13
14 a110ebe1 Leszek Koltunski
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 352b3356 Leszek Koltunski
import java.io.ByteArrayOutputStream;
20
import java.io.IOException;
21
import java.io.InputStream;
22 bf5c802b Leszek Koltunski
import java.util.ArrayList;
23 3addecce Leszek Koltunski
import java.util.Random;
24 bf5c802b Leszek Koltunski
25 a110ebe1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
26
27 1725b607 Leszek Koltunski
public abstract class TablebasesAbstract
28 a110ebe1 Leszek Koltunski
{
29
  private final Static3D[] mAxis;
30 c0266cb1 Leszek Koltunski
  private final int mSize, mMinScramble;
31 a110ebe1 Leszek Koltunski
  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 b4111717 Leszek Koltunski
  private final boolean[][] mRotatable;
42 3addecce Leszek Koltunski
  private final int mScalingFactor;
43 b4111717 Leszek Koltunski
44 d4b628bf Leszek Koltunski
  private Tablebase mTablebase;
45 a110ebe1 Leszek Koltunski
  private int[][] mQuatMult;
46 352b3356 Leszek Koltunski
  private boolean mInitialized;
47 a110ebe1 Leszek Koltunski
48 b4111717 Leszek Koltunski
  private static final float[] mTmp = new float[4];
49
50 a110ebe1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
51
52
  abstract int[][] getBasicAngles();
53
  abstract Static3D[] getRotationAxis();
54
  abstract float[][] getPosition();
55
  abstract float[][] getCuts();
56
57 b4111717 Leszek Koltunski
  abstract boolean[][] getRotatable();
58
59 c0266cb1 Leszek Koltunski
  abstract int getMinScramble();
60 b4111717 Leszek Koltunski
  abstract int getSize();
61
  abstract int[] getQuats(int index);
62
  abstract int getIndex(int[] quats);
63
64 a110ebe1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
65
66 1725b607 Leszek Koltunski
  public TablebasesAbstract()
67 a110ebe1 Leszek Koltunski
    {
68
    mSize = getSize();
69 c0266cb1 Leszek Koltunski
    mMinScramble = getMinScramble();
70 a110ebe1 Leszek Koltunski
    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 b4111717 Leszek Koltunski
    mRotatable = getRotatable();
80 a110ebe1 Leszek Koltunski
    mCuts = getCuts();
81
    mNumCuts = new int[mNumAxis];
82
83 3addecce Leszek Koltunski
    int scaling = 0;
84
85 a110ebe1 Leszek Koltunski
    for(int i=0; i<mNumAxis; i++)
86
      {
87
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
88 3addecce Leszek Koltunski
      int numLayers = mNumLayers[i];
89
      for(int j=0; j<numLayers; j++) scaling+=(mAngles[i][j]-1);
90 a110ebe1 Leszek Koltunski
      }
91
92 3addecce Leszek Koltunski
    mScalingFactor = scaling;
93 a110ebe1 Leszek Koltunski
    mRotRow = new int[mNumCubits][mNumAxis];
94 5e501d98 Leszek Koltunski
    mInitialized = false;
95 352b3356 Leszek Koltunski
    }
96
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98
99 1725b607 Leszek Koltunski
  public TablebasesAbstract(Resources res, int resource)
100 352b3356 Leszek Koltunski
    {
101
    this();
102
103 f9980f6a Leszek Koltunski
    mInitialized = true;
104 352b3356 Leszek Koltunski
    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 1725b607 Leszek Koltunski
      stream.close();
117 352b3356 Leszek Koltunski
      byte[] data = buffer.toByteArray();
118 1725b607 Leszek Koltunski
      buffer.close();
119 352b3356 Leszek Koltunski
      mTablebase = new Tablebase(data);
120
      }
121
    catch(IOException ex)
122
      {
123
      mInitialized = false;
124
      }
125 a110ebe1 Leszek Koltunski
    }
126
127 c0266cb1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
128
129
  private int computeRowFromBitmap(int rowBitmap)
130
    {
131
    int index = 0;
132
133
    while(index<32)
134
      {
135
      if( (rowBitmap&0x1) != 0 ) return index;
136
      rowBitmap>>=1;
137
      index++;
138
      }
139
    return 0;
140
    }
141
142 a110ebe1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
143
144 b4111717 Leszek Koltunski
  private int computeRow(float[] pos, int quat, int axisIndex)
145 a110ebe1 Leszek Koltunski
    {
146
    int ret=0;
147
    int len = pos.length/3;
148
    Static3D axis = mAxis[axisIndex];
149
    float axisX = axis.get0();
150
    float axisY = axis.get1();
151
    float axisZ = axis.get2();
152
    float casted, xoff=0, yoff=0, zoff=0;
153 b4111717 Leszek Koltunski
    Static4D q = mQuats[quat];
154 a110ebe1 Leszek Koltunski
155
    for(int i=0; i<len; i++)
156
      {
157 b4111717 Leszek Koltunski
      QuatHelper.rotateVectorByQuat(mTmp,pos[3*i],pos[3*i+1],pos[3*i+2],1.0f,q);
158
      casted = (mTmp[0]+xoff)*axisX + (mTmp[1]+yoff)*axisY + (mTmp[2]+zoff)*axisZ;
159 a110ebe1 Leszek Koltunski
      ret |= computeSingleRow(axisIndex,casted);
160
      }
161
162
    return ret;
163
    }
164
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166
167
  private int computeSingleRow(int axisIndex,float casted)
168
    {
169
    int num = mNumCuts[axisIndex];
170
171
    for(int i=0; i<num; i++)
172
      {
173
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
174
      }
175
176
    return (1<<num);
177
    }
178
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180
// remember about the double cover or unit quaternions!
181
182
  private int mulQuat(int q1, int q2)
183
    {
184
    Static4D result = QuatHelper.quatMultiply(mQuats[q1],mQuats[q2]);
185
186
    float rX = result.get0();
187
    float rY = result.get1();
188
    float rZ = result.get2();
189
    float rW = result.get3();
190
191
    final float MAX_ERROR = 0.1f;
192
    float dX,dY,dZ,dW;
193
194
    for(int i=0; i<mNumQuats; i++)
195
      {
196
      dX = mQuats[i].get0() - rX;
197
      dY = mQuats[i].get1() - rY;
198
      dZ = mQuats[i].get2() - rZ;
199
      dW = mQuats[i].get3() - rW;
200
201
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
202
          dY<MAX_ERROR && dY>-MAX_ERROR &&
203
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
204
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
205
206
      dX = mQuats[i].get0() + rX;
207
      dY = mQuats[i].get1() + rY;
208
      dZ = mQuats[i].get2() + rZ;
209
      dW = mQuats[i].get3() + rW;
210
211
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
212
          dY<MAX_ERROR && dY>-MAX_ERROR &&
213
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
214
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
215
      }
216
217
    return -1;
218
    }
219
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221
222
  private int getMultQuat(int index1, int index2)
223
    {
224
    if( mQuatMult==null )
225
      {
226
      mQuatMult = new int[mNumQuats][mNumQuats];
227
228
      for(int i=0; i<mNumQuats; i++)
229
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
230
      }
231
232
    if( index1<mNumQuats && index2<mNumQuats )
233
      {
234
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2);
235
      return mQuatMult[index1][index2];
236
      }
237
238
    return -2;
239
    }
240
241
///////////////////////////////////////////////////////////////////////////////////////////////////
242
// assumption: all layers have the same basicAngles!
243
244
  private int insertAllChildren(int index, byte level)
245
    {
246
    int ret = 0;
247
    int[] quats = getQuats(index);
248
    int numQuats = quats.length;
249
    int[] tmpQuats = new int[numQuats];
250
    byte newLevel = (byte)(level+1);
251
    int quatBasis = 0;
252
253 ce7202ef Leszek Koltunski
    for(int ax=0; ax<mNumAxis; ax++)
254
      for(int cubit=0; cubit<mNumCubits; cubit++)
255
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
256
257 a110ebe1 Leszek Koltunski
    for(int ax=0; ax<mNumAxis; ax++)
258
      {
259
      for(int layer=0; layer<mNumLayers[ax]; layer++)
260
        {
261 b4111717 Leszek Koltunski
        if( !mRotatable[ax][layer] ) continue;
262
        int bitLayer = (1<<layer);
263 a110ebe1 Leszek Koltunski
        int maxAngle = mAngles[ax][layer];
264
265
        for(int angle=1; angle<maxAngle; angle++ )
266
          {
267
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
268
          int quat = quatBasis + angle;
269
270
          for(int cubit=0; cubit<mNumCubits; cubit++)
271 ce7202ef Leszek Koltunski
            if( mRotRow[cubit][ax]==bitLayer )
272 a110ebe1 Leszek Koltunski
              {
273
              int currQuat = tmpQuats[cubit];
274 b4111717 Leszek Koltunski
              int newQuat = getMultQuat(quat,currQuat);
275 a110ebe1 Leszek Koltunski
              tmpQuats[cubit] = newQuat;
276
              }
277
278
          int childIndex = getIndex(tmpQuats);
279
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
280
          }
281
        }
282
283
      quatBasis += (mAngles[ax][0]-1);
284
      }
285
286
    return ret;
287
    }
288
289
///////////////////////////////////////////////////////////////////////////////////////////////////
290
291
  public void createTablebase()
292
    {
293
    mTablebase = new Tablebase(mSize);
294
    mTablebase.insertUnpacked(0,(byte)0);
295
296 f9980f6a Leszek Koltunski
    int numInserted, totalInserted=1;
297 a110ebe1 Leszek Koltunski
    byte insertingLevel = 0;
298
299 462911fd Leszek Koltunski
    android.util.Log.e("D", "creating tablebase of size "+mSize);
300
301 a110ebe1 Leszek Koltunski
    do
302
      {
303
      numInserted = 0;
304
305
      for(int i=0; i<mSize; i++)
306
        {
307
        byte level = mTablebase.retrieveUnpacked(i);
308
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
309
        }
310
311
      insertingLevel++;
312 f9980f6a Leszek Koltunski
      totalInserted += numInserted;
313 bf5c802b Leszek Koltunski
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
314 a110ebe1 Leszek Koltunski
      }
315
    while( numInserted>0 );
316 bf5c802b Leszek Koltunski
317 f9980f6a Leszek Koltunski
    android.util.Log.e("D", "total Inserted: "+totalInserted);
318 462911fd Leszek Koltunski
    android.util.Log.e("D", "packing...");
319 bf5c802b Leszek Koltunski
    mTablebase.pack();
320 462911fd Leszek Koltunski
    android.util.Log.e("D", "all done");
321 5e501d98 Leszek Koltunski
    mInitialized = true;
322 462911fd Leszek Koltunski
    }
323
324
///////////////////////////////////////////////////////////////////////////////////////////////////
325
326
  public byte[] getPacked()
327
    {
328
    return mTablebase.getPacked();
329 a110ebe1 Leszek Koltunski
    }
330 3ee79c9c Leszek Koltunski
331
///////////////////////////////////////////////////////////////////////////////////////////////////
332
333 bf5c802b Leszek Koltunski
  private void addMove(ArrayList<int[]> moves, int axis, int layer, int angle)
334 3ee79c9c Leszek Koltunski
    {
335
    int maxAngle = mAngles[axis][layer];
336
    angle = maxAngle-angle;
337
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
338
339 bf5c802b Leszek Koltunski
    int[] move = new int[] { axis, (1<<layer), angle };
340
    moves.add(move);
341
    }
342
343
///////////////////////////////////////////////////////////////////////////////////////////////////
344
345
  private int[][] convertMoves(ArrayList<int[]> moves)
346
    {
347
    int len = moves.size();
348
    int[][] ret = new int[len][];
349
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
350
    return ret;
351 3ee79c9c Leszek Koltunski
    }
352
353 3addecce Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
354
355 971a184e Leszek Koltunski
  private void getNextAxisLayerAngleQuat(int[] data)
356 3addecce Leszek Koltunski
    {
357
    int axis = data[0];
358
    int layer= data[1];
359
    int angle= data[2];
360
361
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
362
    else
363
      {
364
      data[2] = 1;
365
366
      if( layer< mNumLayers[axis]-1 ) data[1]++;
367
      else
368
        {
369
        data[1] = 0;
370
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
371
        }
372
      }
373 971a184e Leszek Koltunski
374
    data[3] = data[2];
375
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
376 3addecce Leszek Koltunski
    }
377
378 3ee79c9c Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
379
380 6d637f71 Leszek Koltunski
  int[][] extraInfo(int[][] moves, int[] extra)
381
    {
382
    return moves;
383
    }
384
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386
387
  public int[][] solution(int index, int[] extra)
388 3ee79c9c Leszek Koltunski
    {
389 352b3356 Leszek Koltunski
    if( !mInitialized ) return null;
390
391 971a184e Leszek Koltunski
    int[] data = new int[4];
392 431ee33b Leszek Koltunski
    byte level = mTablebase.retrievePacked(index);
393 bf5c802b Leszek Koltunski
    ArrayList<int[]> moves = new ArrayList<>();
394 3ee79c9c Leszek Koltunski
    int[] quats = getQuats(index);
395
    int numQuats = quats.length;
396
    int[] tmpQuats = new int[numQuats];
397
398 bf5c802b Leszek Koltunski
    while(index!=0)
399 3ee79c9c Leszek Koltunski
      {
400 7c1a110c Leszek Koltunski
      boolean found = false;
401
402 3addecce Leszek Koltunski
      data[0]=0;
403
      data[1]=0;
404
      data[2]=1;
405 971a184e Leszek Koltunski
      data[3]=1;
406 3addecce Leszek Koltunski
407 ce7202ef Leszek Koltunski
      for(int ax=0; ax<mNumAxis; ax++)
408
        for(int cubit=0; cubit<mNumCubits; cubit++)
409
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
410
411 3addecce Leszek Koltunski
      for(int s=0; s<mScalingFactor && !found; s++)
412 3ee79c9c Leszek Koltunski
        {
413 3addecce Leszek Koltunski
        int ax    = data[0];
414
        int layer = data[1];
415
        int angle = data[2];
416 971a184e Leszek Koltunski
        int quat  = data[3];
417 3ee79c9c Leszek Koltunski
418 3addecce Leszek Koltunski
        if( mRotatable[ax][layer] )
419 3ee79c9c Leszek Koltunski
          {
420
          int bitLayer = (1<<layer);
421 3addecce Leszek Koltunski
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
422 3ee79c9c Leszek Koltunski
423 3addecce Leszek Koltunski
          for(int cubit=0; cubit<mNumCubits; cubit++)
424
            if( mRotRow[cubit][ax]==bitLayer )
425 3ee79c9c Leszek Koltunski
              {
426 3addecce Leszek Koltunski
              int currQuat = tmpQuats[cubit];
427
              int newQuat = getMultQuat(quat,currQuat);
428
              tmpQuats[cubit] = newQuat;
429 3ee79c9c Leszek Koltunski
              }
430 3addecce Leszek Koltunski
431
          int childIndex = getIndex(tmpQuats);
432
          byte newLevel = mTablebase.retrievePacked(childIndex);
433
434
          if( ((newLevel-level+1)%3) == 0 )
435
            {
436
            addMove(moves,ax,layer,angle);
437
            index = childIndex;
438
            level = (level==0 ? 2 : (byte)(level-1));
439
            found = true;
440 3ee79c9c Leszek Koltunski
            }
441
          }
442
443 971a184e Leszek Koltunski
        getNextAxisLayerAngleQuat(data);
444 3ee79c9c Leszek Koltunski
        }
445
446
      quats = getQuats(index);
447 7c1a110c Leszek Koltunski
448
      if( !found )
449
        {
450 74cc695a Leszek Koltunski
        android.util.Log.e("D", "----> solution error: no move found!");
451 7c1a110c Leszek Koltunski
        return null;
452
        }
453 3ee79c9c Leszek Koltunski
      }
454
455 6d637f71 Leszek Koltunski
    int[][] ret = convertMoves(moves);
456
    return extraInfo(ret,extra);
457 3ee79c9c Leszek Koltunski
    }
458 462911fd Leszek Koltunski
459
///////////////////////////////////////////////////////////////////////////////////////////////////
460
461 c0266cb1 Leszek Koltunski
  public void scramble(Random rnd, int depth, int[][] scramble)
462 462911fd Leszek Koltunski
    {
463 c0266cb1 Leszek Koltunski
    if( !mInitialized ) return;
464 3addecce Leszek Koltunski
465 c0266cb1 Leszek Koltunski
    int solDepth = 0;
466
    int scraLen = scramble.length;
467
    if( depth>mMinScramble ) depth = mMinScramble;
468 462911fd Leszek Koltunski
469 6d637f71 Leszek Koltunski
    int[] cornerTwist = new int[4];
470
    for(int i=0; i<4; i++) cornerTwist[i] = (rnd.nextInt(3)-1);
471
472 c0266cb1 Leszek Koltunski
    while( solDepth<depth )
473 462911fd Leszek Koltunski
      {
474 c0266cb1 Leszek Koltunski
      int randomPosition = rnd.nextInt(mSize-1);
475 6d637f71 Leszek Koltunski
      int[][] sol = solution(randomPosition,cornerTwist);
476 c0266cb1 Leszek Koltunski
      solDepth = sol.length;
477 ce7202ef Leszek Koltunski
478 c0266cb1 Leszek Koltunski
      if( solDepth>=depth )
479 462911fd Leszek Koltunski
        {
480 c0266cb1 Leszek Koltunski
        int num = Math.min(scraLen,solDepth);
481 462911fd Leszek Koltunski
482 c0266cb1 Leszek Koltunski
        for(int i=0; i<num; i++)
483 462911fd Leszek Koltunski
          {
484 c0266cb1 Leszek Koltunski
          int[] source = sol[solDepth-1-i];
485
          scramble[i][0] = source[0];
486
          scramble[i][1] = computeRowFromBitmap(source[1]);
487
          scramble[i][2] =-source[2];
488 462911fd Leszek Koltunski
          }
489
        }
490
      }
491
    }
492 74cc695a Leszek Koltunski
493
///////////////////////////////////////////////////////////////////////////////////////////////////
494
495
  public boolean test(int index)
496
    {
497
    if( !mInitialized ) return false;
498
499
    int[] data = new int[4];
500
    byte level = mTablebase.retrievePacked(index);
501
    int[] quats = getQuats(index);
502
    int numQuats = quats.length;
503
    int[] tmpQuats = new int[numQuats];
504
505
    data[0]=0;
506
    data[1]=0;
507
    data[2]=1;
508
    data[3]=1;
509
510
    for(int ax=0; ax<mNumAxis; ax++)
511
      for(int cubit=0; cubit<mNumCubits; cubit++)
512
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
513
514
    for(int s=0; s<mScalingFactor; s++)
515
      {
516
      int ax    = data[0];
517
      int layer = data[1];
518
      int quat  = data[3];
519
520
      if( mRotatable[ax][layer] )
521
        {
522
        int bitLayer = (1<<layer);
523
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
524
525
        for(int cubit=0; cubit<mNumCubits; cubit++)
526
          if( mRotRow[cubit][ax]==bitLayer )
527
            {
528
            int currQuat = tmpQuats[cubit];
529
            int newQuat = getMultQuat(quat,currQuat);
530
            tmpQuats[cubit] = newQuat;
531
            }
532
533
        int childIndex = getIndex(tmpQuats);
534
        byte newLevel = mTablebase.retrievePacked(childIndex);
535
536
        if( ((newLevel-level+1)%3) == 0 ) return true;
537
        }
538
539
      getNextAxisLayerAngleQuat(data);
540
      }
541
542
    return false;
543
    }
544 a110ebe1 Leszek Koltunski
}